Contract 0x302f746ee2fdc10ddff63188f71639094717a766 16

 
Txn Hash Method
Block
From
To
Value
0x2a29734bd8f15a8b8b43352c9037f83a2741a623231c33d5c786591e3a29ae3aCreate With Sign...1179716212024-03-27 12:20:191 day 4 hrs ago0xaa186b60c30ce3c5c0439fcfc91eb4290d2f88b5 IN Mirror: Writing Editions Factory0 ETH0.0000021725370.014643156
0xa175cf7d2b9d0aad363637f827bf4734c1a12cba73597ebc5b4a44fa103781e7Create With Sign...1178877612024-03-25 13:44:593 days 2 hrs ago0x8d85d3edea57b09fee39c94e5e0ca2757ebb6be7 IN Mirror: Writing Editions Factory0 ETH0.0000170078180.030475916
0x0a1117d8c84a97cb5ef24b2e28937e6e55755ccdea26b45da4602c5022e4077fCreate With Sign...1178735862024-03-25 5:52:293 days 10 hrs ago0x98919fc9e7ba248eb32ef971a193ebec89c0e8bf IN Mirror: Writing Editions Factory0 ETH0.0000047976520.007474688
0xeabf99fd88fbe5837e833b4c880208d63a860247ae212b89ead7c07de741bb0aCreate With Sign...1178346802024-03-24 8:15:374 days 8 hrs ago0xda8bc70d2af41c4160e5e853b9be435bb493a843 IN Mirror: Writing Editions Factory0 ETH0.000003713320.006400507
0xd250b0f6a7e9bf38d2ade973922eafb6a3865c561c8cf037a7a2bf2b14286184Create With Sign...1178186882024-03-23 23:22:334 days 16 hrs ago0xe977fa8d8ae7d3d6e28c17a868ef04bd301c583f IN Mirror: Writing Editions Factory0 ETH0.0000044699520.007106928
0x9811eae9a8237218ffd41501fad1ced2d22fb69cd9e8311df88a63233cd7e4a0Create With Sign...1177182962024-03-21 15:36:097 days 44 mins ago0xf8744fdda5534391d5df5df2d7411b94714b386c IN Mirror: Writing Editions Factory0 ETH0.0000040501650.006435917
0xeaecf91b0648c8a2757b52ada4eea93bc69cfe367d844be98d489e59479a1d8eCreate With Sign...1177170562024-03-21 14:54:497 days 1 hr ago0x7aed7eb9ace976d64c95da4baf85d1c5634d607a IN Mirror: Writing Editions Factory0 ETH0.0000046277580.0070481
0xf6e1cd5dd8a831a42baca22738d9731ff278df21a96045195980c490e093f5d3Create With Sign...1177021682024-03-21 6:38:337 days 9 hrs ago0xf7a5bdbbc2579a2f4523c17b2ce18c9e6896d435 IN Mirror: Writing Editions Factory0 ETH0.0000054238150.0074103
0x9bf5e3a44b18c608acc94a26b4490bfcc81a0db36f4a12005d208d0d6dbf7d51Create With Sign...1177018472024-03-21 6:27:517 days 9 hrs ago0x3b5198795509c866bd49fd2a06f368e9c18768f6 IN Mirror: Writing Editions Factory0 ETH0.0000052114340.0071891
0xab6602c8b184d543a5372a359af217c559214779d15cf0928df3db324d43d1cdCreate With Sign...1176915112024-03-21 0:43:197 days 15 hrs ago0x3b5198795509c866bd49fd2a06f368e9c18768f6 IN Mirror: Writing Editions Factory0 ETH0.0000051951460.0074697
0x29463c211a660b2554ab266d51777c95e11fd7c5baa5ecfde45f7c944a2e16b1Create With Sign...1176543712024-03-20 4:05:198 days 12 hrs ago0xf7255842f651d42e5ad287c404a26fa544a4b97a IN Mirror: Writing Editions Factory0 ETH0.000001576380.009069535
0xe0b554c4b7220e4fa9263b2633043ed5730b457aaf80e3120747e099e3f1316dCreate With Sign...1176416552024-03-19 21:01:278 days 19 hrs ago0x4bba7715fd58180ed3d43b5f192671e7a9c68502 IN Mirror: Writing Editions Factory0 ETH0.0000013757850.006963182
0x6ca00c97ecfef3f11be5d93172e5dda811a61db39f46f4bf787e3ec1005acbbeCreate With Sign...1176416092024-03-19 20:59:558 days 19 hrs ago0x4bba7715fd58180ed3d43b5f192671e7a9c68502 IN Mirror: Writing Editions Factory0 ETH0.0000014864470.007013419
0x50561c33ea470ea033685eb5b2acc4481fc09affd3fcaa4e03288e5b959f8476Create With Sign...1176257612024-03-19 12:11:399 days 4 hrs ago0x929fb9d2d309610ec5f153be6270bb6015a6026c IN Mirror: Writing Editions Factory0 ETH0.0000048571320.0063008
0x9bdba5f4987cccc3377c411b757c661145b671980f70b7ae5b4b3e76077ddcf2Create With Sign...1176257452024-03-19 12:11:079 days 4 hrs ago0x7aed7eb9ace976d64c95da4baf85d1c5634d607a IN Mirror: Writing Editions Factory0 ETH0.0000050784790.0061798
0x33bdea72509578914f38ddd8bda0c67d2159d6c41f55d5d7a7f16aaa066cc3d1Create With Sign...1176243992024-03-19 11:26:159 days 4 hrs ago0xb653786d49c354e74f21ea100fb826a701171f0a IN Mirror: Writing Editions Factory0 ETH0.0000072366990.006591099
0x211105596956d12972bce7c20d75614313639c3c0d37cdc2127bea3bfb8e4b22Create With Sign...1176141532024-03-19 5:44:439 days 10 hrs ago0x3028159a3fcc48053cade0bae7def0ba7440187e IN Mirror: Writing Editions Factory0 ETH0.0000044384070.006893
0x273520249bc52cb2d0ee9cfee3a2ccda20bb26e0509fe0f8cda0ecf491ed3de0Create With Sign...1176141392024-03-19 5:44:159 days 10 hrs ago0xfd3d3309c4531818fcb1a4ac520867f23af6488b IN Mirror: Writing Editions Factory0 ETH0.0000038675340.0063765
0x49b937112ea30aad0225842e8564b933b872279adbfac5cde0f60bd85c27bf17Create With Sign...1176116582024-03-19 4:21:339 days 11 hrs ago0x033f68fd9df6bac726e6651e95ac399dbca3dc58 IN Mirror: Writing Editions Factory0 ETH0.0000044516230.0063568
0xed28846242743af5d3cacee69a87a7be9f3dc3265718a02569d73d9f0fff790fCreate With Sign...1176116422024-03-19 4:21:019 days 11 hrs ago0xbdc7c0b075774d1e625dff86f52342da53c0cba1 IN Mirror: Writing Editions Factory0 ETH0.0000044908940.0064436
0x2b9fbe16013f6e286e4214eebfd829bfd445e5702b6c99e14ec5c60ed07ea06dCreate With Sign...1175862252024-03-18 14:13:4710 days 2 hrs ago0x0326b542e6ddedc20faf51d6521309ea042ff6e0 IN Mirror: Writing Editions Factory0 ETH0.0000063380990.010156797
0x7f4093b543bf5cc208691db07ebfa3295cc9ea7fb94fb196dafcae9f5892bb13Create With Sign...1175850022024-03-18 13:33:0110 days 2 hrs ago0x7aed7eb9ace976d64c95da4baf85d1c5634d607a IN Mirror: Writing Editions Factory0.0001 ETH0.0000068209440.0075
0xc9c0cc3c1602c2ff2b93f33acd0fc051b3e2b1aadb5a6338524df6c9f8ea0929Create With Sign...1175796372024-03-18 10:34:1110 days 5 hrs ago0x91b3d8fe1380f4d70b7672b7eeeef98df42a42c2 IN Mirror: Writing Editions Factory0 ETH0.0000038136770.006050672
0x43f5bff8178e4472bffec339cc918803ca9528d0b1fabb3d7039c35f4f7f01bbCreate With Sign...1175661762024-03-18 3:05:2910 days 13 hrs ago0xe5d49f932f2e8dced58ba89ba8639f066b8ece3e IN Mirror: Writing Editions Factory0 ETH0.000001183790.006058748
0x5d9a9dad5ad3dfcc2a9af844e1674c3f09e8038a2ba5d520357cedf4c9964598Create With Sign...1175651992024-03-18 2:32:5510 days 13 hrs ago0x0a415464695ed9613303d70ac92074559ac47a75 IN Mirror: Writing Editions Factory0 ETH0.0000011653520.006066502
[ Download CSV Export 
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0xa175cf7d2b9d0aad363637f827bf4734c1a12cba73597ebc5b4a44fa103781e71178877612024-03-25 13:44:593 days 2 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x0a1117d8c84a97cb5ef24b2e28937e6e55755ccdea26b45da4602c5022e4077f1178735862024-03-25 5:52:293 days 10 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xeabf99fd88fbe5837e833b4c880208d63a860247ae212b89ead7c07de741bb0a1178346802024-03-24 8:15:374 days 8 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xd250b0f6a7e9bf38d2ade973922eafb6a3865c561c8cf037a7a2bf2b142861841178186882024-03-23 23:22:334 days 16 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x9811eae9a8237218ffd41501fad1ced2d22fb69cd9e8311df88a63233cd7e4a01177182962024-03-21 15:36:097 days 44 mins ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xeaecf91b0648c8a2757b52ada4eea93bc69cfe367d844be98d489e59479a1d8e1177170562024-03-21 14:54:497 days 1 hr ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xf6e1cd5dd8a831a42baca22738d9731ff278df21a96045195980c490e093f5d31177021682024-03-21 6:38:337 days 9 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x9bf5e3a44b18c608acc94a26b4490bfcc81a0db36f4a12005d208d0d6dbf7d511177018472024-03-21 6:27:517 days 9 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xab6602c8b184d543a5372a359af217c559214779d15cf0928df3db324d43d1cd1176915112024-03-21 0:43:197 days 15 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x50561c33ea470ea033685eb5b2acc4481fc09affd3fcaa4e03288e5b959f84761176257612024-03-19 12:11:399 days 4 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x9bdba5f4987cccc3377c411b757c661145b671980f70b7ae5b4b3e76077ddcf21176257452024-03-19 12:11:079 days 4 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x33bdea72509578914f38ddd8bda0c67d2159d6c41f55d5d7a7f16aaa066cc3d11176243992024-03-19 11:26:159 days 4 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x211105596956d12972bce7c20d75614313639c3c0d37cdc2127bea3bfb8e4b221176141532024-03-19 5:44:439 days 10 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x273520249bc52cb2d0ee9cfee3a2ccda20bb26e0509fe0f8cda0ecf491ed3de01176141392024-03-19 5:44:159 days 10 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x49b937112ea30aad0225842e8564b933b872279adbfac5cde0f60bd85c27bf171176116582024-03-19 4:21:339 days 11 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xed28846242743af5d3cacee69a87a7be9f3dc3265718a02569d73d9f0fff790f1176116422024-03-19 4:21:019 days 11 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x2b9fbe16013f6e286e4214eebfd829bfd445e5702b6c99e14ec5c60ed07ea06d1175862252024-03-18 14:13:4710 days 2 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x7f4093b543bf5cc208691db07ebfa3295cc9ea7fb94fb196dafcae9f5892bb131175850022024-03-18 13:33:0110 days 2 hrs ago Mirror: Writing Editions Factory 0xcb4ffa24c27e8a8d82331f7f050a710efc9915aa0.0001 ETH
0x7f4093b543bf5cc208691db07ebfa3295cc9ea7fb94fb196dafcae9f5892bb131175850022024-03-18 13:33:0110 days 2 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0xc9c0cc3c1602c2ff2b93f33acd0fc051b3e2b1aadb5a6338524df6c9f8ea09291175796372024-03-18 10:34:1110 days 5 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x9b1ca784945805604c5db4c7e38a675eb2ee43003a8e08070244198cc31b88891174938472024-03-16 10:54:3112 days 5 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x4d8808167f0cc5cb6f0e01bc4b1875f2fb2a270f74c3cc939b40cec4a2e06e731174678992024-03-15 20:29:3512 days 19 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x6acde23deb19b78ce3216a6689c7628dd731607f1dde84e16bdab8366016a4f81174330742024-03-15 1:08:4513 days 15 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x51ce04fb2fa59d1dbdbfa2aac80243518a9443b483ac3ce43174c9d555b80b3a1174330252024-03-15 1:07:0713 days 15 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
0x449db36c58c4b0e2889b99cde97a7816d17827d6dc7263be50be899f5ff867971174329902024-03-15 1:05:5713 days 15 hrs ago Mirror: Writing Editions Factory  Contract Creation0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WritingEditionsFactory

Compiler Version
v0.8.12+commit.f00d7308

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 23 : WritingEditionsFactory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/// > [[[[[[[[[[[ Imports ]]]]]]]]]]]

import "./WritingEditions.sol";
import "./interface/IWritingEditionsFactory.sol";
import "../observability/Observability.sol";
import "../lib/ERC1271/interface/IERC1271.sol";
import "../lib/Ownable.sol";

/// > [[[[[[[[[[[ External Library Imports ]]]]]]]]]]]

import "openzeppelin-contracts/contracts/proxy/Clones.sol";
import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol";
import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";

/**
 * @title WritingEditionsFactory
 * @notice The WritingEditionsFactory contract deploys WritingEdition clones.
 * A WritingEdition is a token that supports ERC721: Non-Fungible Token Standard.
 * The factory supports deploying clones with account signatures for counterfactual
 * contract deployments when minting a token, otherwise colloquially known as "lazy minting".
 * @author MirrorXYZ
 * @custom:security-contact [email protected]
 */
contract WritingEditionsFactory is
    Ownable,
    ReentrancyGuard,
    IWritingEditionsFactoryEvents,
    IWritingEditionsFactory,
    IObservabilityEvents
{
    /// @notice Version.
    uint8 public immutable override VERSION = 1;

    /// @notice Observability contract for data processing.
    address public immutable override o11y;

    /// @notice Treasury configuration.
    address public immutable override treasuryConfiguration;

    /// @notice Transaction Reentrancy Guard.
    bool public override guardOn;

    /// > [[[[[[[[[[[ Deployments ]]]]]]]]]]]

    /// @notice Writing edition implementation.
    address public override implementation;

    /// @dev Store when a salt is used.
    mapping(bytes32 => bool) public override salts;

    /// @dev Contract/domain separator for generating a salt.
    bytes32 public immutable override DOMAIN_SEPARATOR;

    /// @dev Create function separator for generating a salt.
    bytes32 public constant override CREATE_TYPEHASH =
        keccak256(
            "Create(address owner,bytes32 salt,uint256 limit,uint256 price,address fundingRecipient,address renderer,uint256 nonce,uint16 fee)"
        );

    /// @dev Max limit.
    uint256 public override maxLimit;

    /// > [[[[[[[[[[[ Signature Verification ]]]]]]]]]]]

    /// @dev Used to verify smart contract signatures (ERC1271).
    bytes4 internal constant MAGIC_VALUE =
        bytes4(keccak256("isValidSignature(bytes32,bytes)"));

    /// > [[[[[[[[[[[ Description ]]]]]]]]]]]

    /// @notice Base URI for clone description.
    string public constant override baseDescriptionURI = "https://mirror.xyz/";

    /// > [[[[[[[[[[[ Constructor ]]]]]]]]]]]

    /// @notice Stores treasury, generates domain-separator, and deploys
    /// observability and implementation contracts.
    /// @param _treasuryConfiguration Mirror treasury configuration.
    /// @param _maxLimit Max edition limit.
    /// @param _guardOn guard status.
    constructor(
        address _owner,
        address _treasuryConfiguration,
        uint256 _maxLimit,
        bool _guardOn
    ) Ownable(_owner) {
        require(
            _treasuryConfiguration != address(0),
            "must set treasury config"
        );

        // Store treasury configuration.
        treasuryConfiguration = _treasuryConfiguration;

        // Generate domain separator.
        DOMAIN_SEPARATOR = keccak256(
            abi.encode(
                keccak256(
                    "EIP712Domain(string version,uint256 chainId,address verifyingContract)"
                ),
                keccak256(bytes("1")),
                block.chainid,
                address(this)
            )
        );

        // Deploy and store Observability contract.
        o11y = address(new Observability());

        // Deploy and store implementation contract.
        implementation = address(
            new WritingEditions(address(this), _treasuryConfiguration, o11y)
        );

        // Store guard status.
        guardOn = _guardOn;

        // Store max limit.
        maxLimit = _maxLimit;
    }

    /// > [[[[[[[[[[[ View functions ]]]]]]]]]]]

    /// @notice Generates the address that a clone will be deployed to.
    /// @param _implementation the WritingEditions address.
    /// @param salt the entropy used by create2 for generatating a deterministic address.
    function predictDeterministicAddress(address _implementation, bytes32 salt)
        external
        view
        override
        returns (address)
    {
        return
            Clones.predictDeterministicAddress(
                _implementation,
                salt,
                address(this)
            );
    }

    /// @notice Generates the salt parameter for `owner` to sign. The signature
    /// and parameters can be used to deploy a clone through `createWithSignature`.
    /// @param owner owner of the clone.
    /// @param edition edition parameters used to deploy the clone.
    function getSalt(
        address owner,
        WritingEditions.WritingEdition memory edition
    ) external view returns (bytes32) {
        return _getSalt(owner, edition);
    }

    /// @notice validate that a `salt` was signed by `owner`.
    /// @param owner owner of the clone.
    /// @param v signature value.
    /// @param r signature value.
    /// @param s signature value.
    function isValid(
        address owner,
        bytes32 salt,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external view override returns (bool) {
        return _isValid(owner, salt, v, r, s);
    }

    /// > [[[[[[[[[[[ Limit ]]]]]]]]]]]

    function setLimit(uint256 _maxLimit) external override onlyOwner {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitFactoryLimitSet(
            // oldLimit
            maxLimit,
            // newLimit
            _maxLimit
        );

        // Store max limit.
        maxLimit = _maxLimit;
    }

    /// > [[[[[[[[[[[ Guard ]]]]]]]]]]]

    function setGuard(bool _guardOn) external override onlyOwner {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitFactoryGuardSet(_guardOn);

        // Store guard status.
        guardOn = _guardOn;
    }

    /// > [[[[[[[[[[[ Implementation ]]]]]]]]]]]

    function setImplementation(address _implementation)
        external
        override
        onlyOwner
    {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitFactoryImplementationSet(
            // oldImplementation
            implementation,
            // newImplementation
            _implementation
        );

        // Store implementation.
        implementation = _implementation;
    }

    /// > [[[[[[[[[[[ Deployment functions ]]]]]]]]]]]

    /// @notice Deploy a new writing edition clone with the sender as the owner.
    /// @param edition edition parameters used to deploy the clone.
    function create(WritingEditions.WritingEdition memory edition)
        external
        override
        returns (address clone)
    {
        clone = _deployCloneAndInitialize(msg.sender, edition, address(0), "");
    }

    /// @dev Deploy a new writing edition clone with a signature provided by `owner`.
    /// @param owner owner of the clone.
    /// @param edition edition parameters used to deploy the clone.
    /// @param v signature value.
    /// @param r signature value.
    /// @param s signature value.
    /// @param tokenRecipient account that will receive the first minted token.
    /// @param message message sent with the token purchase, not stored.
    function createWithSignature(
        address owner,
        WritingEditions.WritingEdition memory edition,
        uint8 v,
        bytes32 r,
        bytes32 s,
        address tokenRecipient,
        string memory message
    ) external payable override nonReentrant returns (address clone) {
        // Assert enough ETH was sent to purchase the token.
        require(msg.value == edition.price, "incorrect value");

        // Generate salt from parameters.
        bytes32 salt = _getSalt(owner, edition);

        // If the clone has been deployed, purchase instead of deploying.
        if (salts[salt]) {
            clone = Clones.predictDeterministicAddress(
                implementation,
                keccak256(
                    abi.encode(
                        owner,
                        edition.name,
                        edition.symbol,
                        edition.nonce
                    )
                ),
                address(this)
            );

            require(clone.code.length > 0, "invalid clone address");

            // slither-disable-next-line unused-return
            WritingEditions(clone).purchaseThroughFactory{value: msg.value}(
                tokenRecipient,
                message
            );
        } else {
            salts[salt] = true;

            // Assert the signature is valid.
            require(
                _isValid(owner, salt, v, r, s),
                "invalid or unable to verify signature"
            );

            clone = _deployCloneAndInitialize(
                owner,
                edition,
                tokenRecipient,
                message
            );
        }
    }

    /// > [[[[[[[[[[[ Tributary ]]]]]]]]]]]

    /// @notice Set a new tributary.
    /// @param _tributary new tributary.
    function setTributary(address clone, address _tributary) external override {
        require(msg.sender == Ownable(clone).owner(), "unauthorized");

        address tributaryRegistry = _getTributaryRegistry();

        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitTributarySet(
            // clone
            clone,
            // oldTributary
            ITributaryRegistry(tributaryRegistry).producerToTributary(clone),
            // newTributary
            _tributary
        );

        ITributaryRegistry(tributaryRegistry).setTributary(clone, _tributary);
    }

    /// > [[[[[[[[[[[ Purchase functions ]]]]]]]]]]]

    /// @notice Purchase a token through the factory.
    /// @param clone the WritingEdition address for the token to purchase.
    /// @param tokenRecipient account that will receive the first minted token.
    /// @param message message sent with the token purchase, not stored.
    function purchaseThroughFactory(
        address clone,
        address tokenRecipient,
        string memory message
    ) external payable override returns (uint256 tokenId) {
        return
            WritingEditions(clone).purchaseThroughFactory{value: msg.value}(
                tokenRecipient,
                message
            );
    }

    /// > [[[[[[[[[[[ Internal functions ]]]]]]]]]]]

    function _isValid(
        address owner,
        bytes32 salt,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal view returns (bool) {
        require(owner != address(0), "cannot validate");

        // If the owner is a contract, attempt to validate the
        // signature using EIP-1271.
        if (owner.code.length != 0) {
            bytes memory signature = abi.encodePacked(r, s, v);

            // slither-disable-next-line unused-return
            try IERC1271(owner).isValidSignature(salt, signature) returns (
                // slither-disable-next-line uninitialized-local
                bytes4 magicValue
            ) {
                return MAGIC_VALUE == magicValue;
            } catch {
                return false;
            }
        }

        address recoveredAddress = ECDSA.recover(salt, v, r, s);

        return recoveredAddress == owner;
    }

    function _getSalt(
        address owner,
        WritingEditions.WritingEdition memory edition
    ) internal view returns (bytes32) {
        return
            ECDSA.toTypedDataHash(
                DOMAIN_SEPARATOR,
                keccak256(
                    abi.encode(
                        CREATE_TYPEHASH,
                        owner,
                        keccak256(
                            abi.encodePacked(
                                edition.name,
                                edition.symbol,
                                edition.imageURI,
                                edition.contentURI
                            )
                        ),
                        edition.limit,
                        edition.price,
                        edition.fundingRecipient,
                        edition.renderer,
                        edition.nonce,
                        edition.fee
                    )
                )
            );
    }

    /// @dev Deploys a clone and calls the initialize function. Additionally,
    /// this function calls `registerTributary` on the tributary registry, if
    /// one is set.
    function _deployCloneAndInitialize(
        address owner,
        WritingEditions.WritingEdition memory edition,
        address tokenRecipient,
        string memory message
    ) internal returns (address clone) {
        require(
            edition.fundingRecipient != address(0),
            "must specify recipient"
        );

        // Assert limit is valid. If maxLimit is zero allow any limit.
        require(
            maxLimit == 0 || (edition.limit > 0 && edition.limit <= maxLimit),
            "invalid limit"
        );

        clone = Clones.cloneDeterministic(
            implementation,
            keccak256(
                abi.encode(owner, edition.name, edition.symbol, edition.nonce)
            )
        );

        // Register clones _before_ initializing.
        address tributaryRegistry = _getTributaryRegistry();

        if (tributaryRegistry != address(0)) {
            IObservability(o11y).emitTributarySet(
                // clone
                clone,
                // oldTributary
                address(0),
                // newTributary
                edition.fundingRecipient
            );

            ITributaryRegistry(tributaryRegistry).setTributary(
                clone,
                edition.fundingRecipient
            );
        }

        // Initialize clone.
        WritingEditions(clone).initialize{value: msg.value}(
            owner,
            edition,
            tokenRecipient,
            message,
            guardOn
        );

        IObservability(o11y).emitDeploymentEvent(owner, clone);
    }

    function _getTributaryRegistry() internal returns (address) {
        return
            ITreasuryConfiguration(treasuryConfiguration).tributaryRegistry();
    }
}

File 2 of 23 : WritingEditions.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/// > [[[[[[[[[[[ Imports ]]]]]]]]]]]

import "./interface/IWritingEditions.sol";
import "./interface/IWritingEditionsFactory.sol";
import "../observability/interface/IObservability.sol";
import "../fee-configuration/interface/IFeeConfiguration.sol";
import "../renderer/interface/IRenderer.sol";
import "../treasury/interface/ITreasuryConfiguration.sol";
import "../treasury/interface/ITreasury.sol";
import "../treasury/interface/ITributaryRegistry.sol";
import "../lib/Ownable.sol";
import "../lib/ERC721/ERC721.sol";
import "../lib/ERC165/ERC165.sol";
import "../lib/ERC721/interface/IERC721.sol";
import "../lib/ERC2981/interface/IERC2981.sol";
import "../lib/transaction-reentrancy-guard/TransactionReentrancyGuard.sol";

/// > [[[[[[[[[[[ External Library Imports ]]]]]]]]]]]

import "openzeppelin-contracts/contracts/security/ReentrancyGuard.sol";
import "openzeppelin-contracts/contracts/utils/Base64.sol";
import "openzeppelin-contracts/contracts/utils/Strings.sol";

/**
 * @title WritingEditions
 * @author MirrorXYZ
 * @custom:security-contact [email protected]
 */
contract WritingEditions is
    Ownable,
    TransactionReentrancyGuard,
    ReentrancyGuard,
    ERC721,
    IERC721Metadata,
    IERC2981,
    IWritingEditions,
    IWritingEditionEvents,
    IObservabilityEvents
{
    /// > [[[[[[[[[[[ Version ]]]]]]]]]]]

    /// @notice Version.
    uint8 public immutable override VERSION = 1;

    /// > [[[[[[[[[[[ Authorization ]]]]]]]]]]]

    /// @notice Address that deploys and initializes clones.
    address public immutable override factory;

    /// > [[[[[[[[[[[ Configuration ]]]]]]]]]]]

    /// @notice Address for Mirror treasury configuration.
    address public immutable override treasuryConfiguration;

    /// @notice Address for Mirror's observability contract.
    address public immutable override o11y;

    /// > [[[[[[[[[[[ ERC721 Metadata ]]]]]]]]]]]

    /// @notice Token name.
    string public override name;

    /// @notice Token symbol.
    string public override symbol;

    /// @notice Base URI for description.
    string internal _baseDescriptionURI;

    /// > [[[[[[[[[[[ Token Data ]]]]]]]]]]]

    /// @notice Total supply of editions. Used to calculate next tokenId.
    uint256 public override totalSupply;

    /// @notice Token text content, stored in Arweave.
    string public override contentURI;

    /// @notice Token image content, stored in IPFS.
    string public override imageURI;

    /// @notice Token price, set by the owner.
    uint256 public override price;

    /// @notice Token limit, set by the owner.
    uint256 public override limit;

    /// @notice Account that will receive funds from sales.
    address public override fundingRecipient;

    /// > [[[[[[[[[[[ Fees ]]]]]]]]]]]

    /// @notice Fee basis points paid if fees are on.
    uint16 public override fee;

    /// > [[[[[[[[[[[ Royalty Info (ERC2981) ]]]]]]]]]]]

    /// @notice Account that will receive royalties.
    address public override royaltyRecipient;

    /// @notice Royalty basis points.
    uint256 public override royaltyBPS;

    /// > [[[[[[[[[[[ Rendering ]]]]]]]]]]]

    /// @notice Address for a rendering contract, if set, calls to
    /// `tokenURI(uint256)` are forwarded to this address.
    address public override renderer;

    /// > [[[[[[[[[[[ Constructor ]]]]]]]]]]]

    /// @notice Implementation logic for clones.
    /// @param _factory the factory contract deploying clones with this implementation.
    /// @param _treasuryConfiguration Mirror treasury configuration.
    /// @param _o11y contract for observability.
    constructor(
        address _factory,
        address _treasuryConfiguration,
        address _o11y
    ) Ownable(address(0)) TransactionReentrancyGuard(true) {
        // Assert not the zero-address.
        require(_factory != address(0), "must set factory");

        // Store factory.
        factory = _factory;

        // Assert not the zero-address.
        require(
            _treasuryConfiguration != address(0),
            "must set treasury configuration"
        );

        // Store treasury configuration.
        treasuryConfiguration = _treasuryConfiguration;

        // Assert not the zero-address.
        require(_o11y != address(0), "must set observability");

        // Store observability.
        o11y = _o11y;
    }

    /// > [[[[[[[[[[[ Initializing ]]]]]]]]]]]

    /// @notice Initialize a clone by storing edition parameters. Called only
    /// by the factory. Mints the first edition to `tokenRecipient`.
    /// @param _owner owner of the clone.
    /// @param edition edition parameters used to deploy the clone.
    /// @param tokenRecipient account that will receive the first minted token.
    /// @param message message sent with the token purchase, not stored.
    function initialize(
        address _owner,
        WritingEdition memory edition,
        address tokenRecipient,
        string memory message,
        bool _guardOn
    ) external payable override nonReentrant {
        // Only factory can call this function.
        require(msg.sender == factory, "unauthorized caller");

        // Store ERC721 metadata.
        name = edition.name;
        symbol = edition.symbol;

        // Store edition data.
        imageURI = edition.imageURI;
        contentURI = edition.contentURI;
        price = edition.price;
        limit = edition.limit;
        fundingRecipient = edition.fundingRecipient;
        renderer = edition.renderer;

        // Store fee.
        _setFee(edition.fee);

        // Store owner.
        _setInitialOwner(_owner);

        // Mint initial token to recipient, assuming
        // the correct value was sent through the factory
        if (tokenRecipient != address(0)) {
            _purchase(tokenRecipient, message);
        }

        // Store guard status.
        _setGuard(_guardOn);
    }

    /// @notice Base description URI.
    function baseDescriptionURI()
        external
        view
        override
        returns (string memory)
    {
        return _getBaseDescriptionURI();
    }

    /// @notice Token description.
    function description() public view override returns (string memory) {
        return
            string(
                abi.encodePacked(
                    _getBaseDescriptionURI(),
                    Strings.toString(block.chainid),
                    "/",
                    _addressToString(address(this))
                )
            );
    }

    /// > [[[[[[[[[[[ View Functions ]]]]]]]]]]]

    /// @notice Helper function to get owners for a list of tokenIds.
    /// @dev Could revert if `tokenIds` is too long.
    /// @param tokenIds a list of token-ids to check ownership of.
    /// @return owners a list of token-id owners, address(0) if token is not minted
    function ownerOf(uint256[] memory tokenIds)
        external
        view
        override
        returns (address[] memory owners)
    {
        owners = new address[](tokenIds.length);

        for (uint256 i = 0; i < tokenIds.length; i++) {
            owners[i] = _owners[tokenIds[i]];
        }
    }

    /// > [[[[[[[[[[[ Funding Recipient ]]]]]]]]]]]

    /// @notice Set a new funding recipient.
    /// @param _fundingRecipient new funding recipient.
    function setFundingRecipient(address _fundingRecipient)
        external
        override
        onlyOwner
    {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitFundingRecipientSet(
            // oldFundingRecipient
            fundingRecipient,
            // newFundingRecipient
            _fundingRecipient
        );

        fundingRecipient = _fundingRecipient;
    }

    /// > [[[[[[[[[[[ Price ]]]]]]]]]]]

    /// @notice Set a new price.
    /// @param _price new price.
    function setPrice(uint256 _price) external override onlyOwner {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitPriceSet(
            // oldPrice
            price,
            // newPrice
            _price
        );

        price = _price;
    }

    /// @notice Set a new base description URI.
    /// @param newBaseDescriptionURI new base description URI
    function setBaseDescriptionURI(string memory newBaseDescriptionURI)
        external
        override
        onlyOwner
    {
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitBaseDescriptionURISet(
            // oldDescriptionURI
            _getBaseDescriptionURI(),
            // oldDescriptionURI
            newBaseDescriptionURI
        );

        _baseDescriptionURI = newBaseDescriptionURI;
    }

    /// @notice Turn Transaction Level Reentrancy Guard on/off.
    function toggleGuard() external override onlyOwner {
        _toggleGuard();
    }

    /// > [[[[[[[[[[[ Purchase ]]]]]]]]]]]

    /// @notice Purchase a token.
    /// @param tokenRecipient the account to receive the token.
    /// @param message an optional message during purchase, not stored.
    /// @return tokenId the id of the minted token.
    function purchase(address tokenRecipient, string memory message)
        external
        payable
        override
        guard
        nonReentrant
        returns (uint256 tokenId)
    {
        // Ensure enough value is sent.
        require(msg.value == price, "incorrect value");

        return _purchase(tokenRecipient, message);
    }

    /// @notice Purchase a token through the factory. Assumes balance checks
    /// were made in the factory.
    /// @param tokenRecipient the account to receive the token.
    /// @param message an optional message during purchase, not stored.
    /// @return tokenId the id of the minted token.
    function purchaseThroughFactory(
        address tokenRecipient,
        string memory message
    ) external payable override nonReentrant returns (uint256 tokenId) {
        // Ensure only factory calls.
        require(msg.sender == factory, "unauthorized");

        tokenId = _purchase(tokenRecipient, message);
    }

    /// > [[[[[[[[[[[ Mint ]]]]]]]]]]]

    /// @notice Mint an edition
    /// @dev throws if called by a non-owner
    /// @param tokenRecipient the account to receive the edition
    function mint(address tokenRecipient)
        external
        override
        onlyOwner
        returns (uint256 tokenId)
    {
        tokenId = _getTokenIdAndMint(tokenRecipient);
    }

    /// > [[[[[[[[[[[ Limit ]]]]]]]]]]]

    /// @notice Allows the owner to set a global limit on the total supply
    /// @dev throws if attempting to increase the limit
    /// @param newLimit new mint limit.
    function setLimit(uint256 newLimit) external override onlyOwner {
        // Enforce that the limit should only ever decrease once set.
        require(
            newLimit >= totalSupply && (limit == 0 || newLimit < limit),
            "limit must be < than current limit"
        );

        // Announce the change in limit.
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitWritingEditionLimitSet(
            // oldLimit
            limit,
            // newLimit
            newLimit
        );

        // Update the limit.
        limit = newLimit;
    }

    /// @notice Set the limit to the last minted tokenId.
    function setMaxLimit() external override onlyOwner {
        // Announce the change in limit.
        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitWritingEditionLimitSet(
            // oldLimit
            limit,
            // newLimit
            totalSupply
        );

        // Update the limit.
        limit = totalSupply;
    }

    /// > [[[[[[[[[[[ ERC2981 Methods ]]]]]]]]]]]

    /// @notice Called with the sale price to determine how much royalty
    //  is owed and to whom
    /// @param _tokenId - the NFT asset queried for royalty information
    /// @param _salePrice - the sale price of the NFT asset specified by _tokenId
    /// @return receiver - address of who should be sent the royalty payment
    /// @return royaltyAmount - the royalty payment amount for _salePrice
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        receiver = _royaltyRecipient();

        royaltyAmount = (_salePrice * _royaltyBPS()) / 10_000;
    }

    /// @notice Get royalties information.
    /// @param royaltyRecipient_ the address that will receive royalties
    /// @param royaltyBPS_ the royalty amount in basis points (bps)
    function setRoyaltyInfo(
        address payable royaltyRecipient_,
        uint256 royaltyBPS_
    ) external override onlyOwner {
        require(
            royaltyBPS_ <= 10_000,
            "bps must be less than or equal to 10,000"
        );

        // slither-disable-next-line reentrancy-no-eth
        IObservability(o11y).emitRoyaltyChange(
            // oldRoyaltyRecipient
            _royaltyRecipient(),
            // oldRoyaltyBPS
            _royaltyBPS(),
            // newRoyaltyRecipient
            royaltyRecipient_,
            // newRoyaltyBPS
            royaltyBPS_
        );

        royaltyRecipient = royaltyRecipient_;
        royaltyBPS = royaltyBPS_;
    }

    /// > [[[[[[[[[[[ Rendering Methods ]]]]]]]]]]]

    /// @notice Set the renderer address
    /// @dev Throws if renderer is not the zero address
    /// @param _renderer contract responsible for rendering tokens.
    function setRenderer(address _renderer) external override onlyOwner {
        require(renderer == address(0), "renderer already set");

        renderer = _renderer;

        IObservability(o11y).emitRendererSet(
            // renderer
            _renderer
        );
    }

    /// @notice Get contract metadata
    /// @dev If a renderer is set, attempt return the renderer's metadata.
    function contractURI() external view override returns (string memory) {
        if (renderer != address(0)) {
            // slither-disable-next-line unused-return
            try IRenderer(renderer).contractURI() returns (
                // slither-disable-next-line uninitialized-local
                string memory result
            ) {
                return result;
            } catch {
                // Fallback if the renderer does not implement contractURI
                return _generateContractURI();
            }
        }

        return _generateContractURI();
    }

    /// @notice Get `tokenId` URI or data
    /// @dev If a renderer is set, call renderer's tokenURI
    /// @param tokenId The tokenId used to request data
    function tokenURI(uint256 tokenId)
        external
        view
        override
        returns (string memory)
    {
        require(_exists(tokenId), "ERC721: query for nonexistent token");

        if (renderer != address(0)) {
            return IRenderer(renderer).tokenURI(tokenId);
        }

        // slither-disable-next-line uninitialized-local
        bytes memory editionNumber;
        if (limit != 0) {
            editionNumber = abi.encodePacked("/", Strings.toString(limit));
        }

        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "',
                        name,
                        " ",
                        Strings.toString(tokenId),
                        editionNumber,
                        '", "description": "',
                        string(description()),
                        '", "content": "ar://',
                        string(contentURI),
                        '", "image": "ipfs://',
                        string(imageURI),
                        '", "attributes":[{ "trait_type": "Serial", "value": ',
                        Strings.toString(tokenId),
                        "}] }"
                    )
                )
            )
        );
        return string(abi.encodePacked("data:application/json;base64,", json));
    }

    /// > [[[[[[[[[[[ IERC165 Method ]]]]]]]]]]]

    /// @param interfaceId The interface identifier, as specified in ERC-165
    function supportsInterface(bytes4 interfaceId)
        public
        pure
        override
        returns (bool)
    {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC165).interfaceId ||
            interfaceId == type(IERC2981).interfaceId;
    }

    /// > [[[[[[[[[[[ Internal Functions ]]]]]]]]]]]

    function _generateContractURI() internal view returns (string memory) {
        string memory json = Base64.encode(
            bytes(
                string(
                    abi.encodePacked(
                        '{"name": "',
                        name,
                        '", "description": "',
                        string(description()),
                        '", "content": "ar://',
                        string(contentURI),
                        '", "image": "ipfs://',
                        string(imageURI),
                        '", "seller_fee_basis_points": ',
                        Strings.toString(_royaltyBPS()),
                        ', "fee_recipient": "',
                        _addressToString(_royaltyRecipient()),
                        '", "external_link": "',
                        _getBaseDescriptionURI(),
                        '"}'
                    )
                )
            )
        );

        return string(abi.encodePacked("data:application/json;base64,", json));
    }

    /// @dev Emit a transfer event from observability contract.
    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual override {
        IObservability(o11y).emitTransferEvent(from, to, tokenId);
    }

    function _royaltyRecipient() internal view returns (address) {
        return
            royaltyRecipient == address(0)
                ? fundingRecipient
                : royaltyRecipient;
    }

    /// @dev The ternary expression below prevents from returning 0 as the
    /// royalty amount. To turn off royalties, we make the assumption that
    /// if the royaltyRecipient is set to address(0), the marketplace code
    /// will ignore the royalty amount.
    function _royaltyBPS() internal view returns (uint256) {
        return royaltyBPS == 0 ? 1000 : royaltyBPS;
    }

    function _purchase(address tokenRecipient, string memory message)
        internal
        returns (uint256 tokenId)
    {
        // Mint token, and get a tokenId.
        tokenId = _getTokenIdAndMint(tokenRecipient);

        // Emit event through observability contract.
        IObservability(o11y).emitWritingEditionPurchased(
            // tokenId
            tokenId,
            // recipient
            tokenRecipient,
            // price
            price,
            // message
            message
        );

        _withdraw(fundingRecipient, msg.value);
    }

    function _getFeeConfiguration() internal returns (address) {
        return ITreasuryConfiguration(treasuryConfiguration).feeConfiguration();
    }

    function _getTributaryRegistry() internal returns (address) {
        return
            ITreasuryConfiguration(treasuryConfiguration).tributaryRegistry();
    }

    /// @dev Withdraws `amount` to `fundsRecipient`. Sends fee to treasury
    // if fees are on.
    function _withdraw(address fundsRecipient, uint256 amount) internal {
        address feeConfiguration = _getFeeConfiguration();
        if (
            feeConfiguration != address(0) &&
            IFeeConfiguration(feeConfiguration).on()
        ) {
            // Calculate the fee on the current balance, using the fee percentage.
            uint256 feeAmount = _feeAmount(amount, fee);

            // If the fee is not zero, attempt to send it to the treasury.
            // If the treasury is not set, do not pay the fee.
            address treasury = ITreasuryConfiguration(treasuryConfiguration)
                .treasury();
            if (feeAmount != 0 && treasury != address(0)) {
                _sendEther(payable(treasury), feeAmount);

                // Transfer the remaining amount to the recipient.
                _sendEther(payable(fundsRecipient), amount - feeAmount);
            } else {
                _sendEther(payable(fundsRecipient), amount);
            }
        } else {
            _sendEther(payable(fundsRecipient), amount);
        }
    }

    function _sendEther(address payable recipient, uint256 amount) internal {
        // Ensure sufficient balance.
        require(address(this).balance >= amount, "insufficient balance");

        // Send the value.
        // slither-disable-next-line low-level-calls
        (bool success, ) = recipient.call{value: amount, gas: gasleft()}("");

        require(success, "recipient reverted");
    }

    function _feeAmount(uint256 amount, uint16 fee_)
        internal
        pure
        returns (uint256)
    {
        if (amount >= 10_000) {
            // Ignore warning since we check amount >= divisor
            // Hence no loss of precision.
            // slither-disable-next-line divide-before-multiply
            return (amount / 10_000) * fee_;
        }

        return 0;
    }

    /// @dev If fee is invalid, default to minimum fee.
    function _setFee(uint16 newFee) internal {
        address feeConfiguration = _getFeeConfiguration();

        fee = IFeeConfiguration(feeConfiguration).valid(newFee)
            ? newFee
            : IFeeConfiguration(feeConfiguration).minimumFee();
    }

    /// @dev Mints and returns tokenId
    function _getTokenIdAndMint(address tokenRecipient)
        internal
        returns (uint256 tokenId)
    {
        // Increment totalSupply to get next id and store tokenId.
        tokenId = ++totalSupply;

        // check that there are still tokens available to purchase
        // zero and max uint256 represent infinite minting
        require(
            limit == 0 || limit == type(uint256).max || tokenId < limit + 1,
            "sold out"
        );

        // mint a new token for the tokenRecipient, using the `tokenId`.
        _mint(tokenRecipient, tokenId);
    }

    function _getBaseDescriptionURI() internal view returns (string memory) {
        return
            bytes(_baseDescriptionURI).length == 0
                ? IWritingEditionsFactory(factory).baseDescriptionURI()
                : _baseDescriptionURI;
    }

    // https://ethereum.stackexchange.com/questions/8346/convert-address-to-string/8447#8447
    function _addressToString(address x) internal pure returns (string memory) {
        bytes memory s = new bytes(40);
        for (uint256 i = 0; i < 20; i++) {
            bytes1 b = bytes1(uint8(uint256(uint160(x)) / (2**(8 * (19 - i)))));
            bytes1 hi = bytes1(uint8(b) / 16);
            bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
            s[2 * i] = _char(hi);
            s[2 * i + 1] = _char(lo);
        }
        return string(abi.encodePacked("0x", s));
    }

    function _char(bytes1 b) internal pure returns (bytes1 c) {
        if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
        else return bytes1(uint8(b) + 0x57);
    }
}

File 3 of 23 : IWritingEditionsFactory.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/// > [[[[[[[[[[[ Imports ]]]]]]]]]]]

import "./IWritingEditions.sol";

interface IWritingEditionsFactoryEvents {
    event NewImplementation(
        address indexed oldImplementation,
        address indexed newImplementation
    );

    event EditionsDeployed(
        address indexed owner,
        address indexed clone,
        address indexed implementation
    );
}

interface IWritingEditionsFactory {
    function VERSION() external view returns (uint8);

    function implementation() external view returns (address);

    function o11y() external view returns (address);

    function treasuryConfiguration() external view returns (address);

    function guardOn() external view returns (bool);

    function salts(bytes32 salt) external view returns (bool);

    function maxLimit() external view returns (uint256);

    function DOMAIN_SEPARATOR() external returns (bytes32);

    function CREATE_TYPEHASH() external returns (bytes32);

    function baseDescriptionURI() external view returns (string memory);

    function predictDeterministicAddress(address implementation_, bytes32 salt)
        external
        view
        returns (address);

    function getSalt(
        address owner_,
        IWritingEditions.WritingEdition memory edition_
    ) external view returns (bytes32);

    function isValid(
        address owner_,
        bytes32 salt,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external view returns (bool);

    function setLimit(uint256 _maxLimit) external;

    function setGuard(bool _guardOn) external;

    function setImplementation(address _implementation) external;

    function create(IWritingEditions.WritingEdition memory edition_)
        external
        returns (address clone);

    function createWithSignature(
        address owner_,
        IWritingEditions.WritingEdition memory edition_,
        uint8 v,
        bytes32 r,
        bytes32 s,
        address recipient,
        string memory message
    ) external payable returns (address clone);

    function setTributary(address clone, address _tributary) external;

    function purchaseThroughFactory(
        address clone,
        address tokenRecipient,
        string memory message
    ) external payable returns (uint256 tokenId);
}

File 4 of 23 : Observability.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "./interface/IObservability.sol";

/**
 * @title Observability
 * @author MirrorXYZ
 */
contract Observability is IObservability, IObservabilityEvents {
    /// > [[[[[[[[[[[ Factory functions ]]]]]]]]]]]

    function emitDeploymentEvent(address owner, address clone)
        external
        override
    {
        emit CloneDeployed(msg.sender, owner, clone);
    }

    function emitTributarySet(
        address clone,
        address oldTributary,
        address newTributary
    ) external override {
        emit TributarySet(msg.sender, clone, oldTributary, newTributary);
    }

    function emitFactoryLimitSet(uint256 oldLimit, uint256 newLimit)
        external
        override
    {
        emit FactoryLimitSet(msg.sender, oldLimit, newLimit);
    }

    function emitFactoryGuardSet(bool guard) external override {
        emit FactoryGuardSet(guard);
    }

    function emitFactoryImplementationSet(
        address oldImplementation,
        address newImplementation
    ) external override {
        emit FactoryImplementationSet(
            msg.sender,
            oldImplementation,
            newImplementation
        );
    }

    /// > [[[[[[[[[[[ Clone functions ]]]]]]]]]]]

    function emitWritingEditionPurchased(
        uint256 tokenId,
        address recipient,
        uint256 price,
        string memory message
    ) external override {
        emit WritingEditionPurchased(
            msg.sender,
            tokenId,
            recipient,
            price,
            message
        );
    }

    function emitTransferEvent(
        address from,
        address to,
        uint256 tokenId
    ) external override {
        emit Transfer(msg.sender, from, to, tokenId);
    }

    function emitRoyaltyChange(
        address oldRoyaltyRecipient,
        uint256 oldRoyaltyBPS,
        address newRoyaltyRecipient,
        uint256 newRoyaltyBPS
    ) external override {
        emit RoyaltyChange(
            msg.sender,
            oldRoyaltyRecipient,
            oldRoyaltyBPS,
            newRoyaltyRecipient,
            newRoyaltyBPS
        );
    }

    function emitRendererSet(address renderer) external override {
        emit RendererSet(msg.sender, renderer);
    }

    function emitWritingEditionLimitSet(uint256 oldLimit, uint256 newLimit)
        external
        override
    {
        emit WritingEditionLimitSet(msg.sender, oldLimit, newLimit);
    }

    function emitPriceSet(uint256 oldPrice, uint256 newPrice)
        external
        override
    {
        emit PriceSet(msg.sender, oldPrice, newPrice);
    }

    function emitFundingRecipientSet(
        address oldFundingRecipient,
        address newFundingRecipient
    ) external override {
        emit FundingRecipientSet(
            msg.sender,
            oldFundingRecipient,
            newFundingRecipient
        );
    }

    function emitBaseDescriptionURISet(
        string memory oldBaseDescriptionURI,
        string memory newBaseDescriptionURI
    ) external override {
        emit BaseDescriptionURISet(
            msg.sender,
            oldBaseDescriptionURI,
            newBaseDescriptionURI
        );
    }
}

File 5 of 23 : IERC1271.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/// @dev Interafce for EIP-1271: Standard Signature Validation Method for Contracts.
interface IERC1271 {
    /// @dev Should return whether the signature provided is valid for the provided hash
    /// @param salt      Hash of the data to be signed
    /// @param signature Signature byte array associated with _hash
    /// MUST return the bytes4 magic value 0x1626ba7e when function passes.
    /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
    /// MUST allow external calls
    function isValidSignature(bytes32 salt, bytes memory signature)
        external
        view
        returns (bytes4 magicValue);
}

File 6 of 23 : Ownable.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IOwnableEvents {
    event OwnershipTransferred(
        address indexed previousOwner,
        address indexed newOwner
    );
}

interface IOwnable {
    function transferOwnership(address nextOwner_) external;

    function cancelOwnershipTransfer() external;

    function acceptOwnership() external;

    function renounceOwnership() external;

    function isOwner() external view returns (bool);

    function isNextOwner() external view returns (bool);
}

contract Ownable is IOwnable, IOwnableEvents {
    address public owner;
    address private nextOwner;

    /// > [[[[[[[[[[[ Modifiers ]]]]]]]]]]]

    modifier onlyOwner() {
        require(isOwner(), "caller is not the owner.");
        _;
    }

    modifier onlyNextOwner() {
        require(isNextOwner(), "current owner must set caller as next owner.");
        _;
    }

    /// @notice Initialize contract by setting the initial owner.
    constructor(address owner_) {
        _setInitialOwner(owner_);
    }

    /// @notice Initiate ownership transfer by setting nextOwner.
    function transferOwnership(address nextOwner_) external override onlyOwner {
        require(nextOwner_ != address(0), "Next owner is the zero address.");

        nextOwner = nextOwner_;
    }

    /// @notice Cancel ownership transfer by deleting nextOwner.
    function cancelOwnershipTransfer() external override onlyOwner {
        delete nextOwner;
    }

    /// @notice Accepts ownership transfer by setting owner.
    function acceptOwnership() external override onlyNextOwner {
        delete nextOwner;

        owner = msg.sender;

        emit OwnershipTransferred(owner, msg.sender);
    }

    /// @notice Renounce ownership by setting owner to zero address.
    function renounceOwnership() external override onlyOwner {
        _renounceOwnership();
    }

    /// @notice Returns true if the caller is the current owner.
    function isOwner() public view override returns (bool) {
        return msg.sender == owner;
    }

    /// @notice Returns true if the caller is the next owner.
    function isNextOwner() public view override returns (bool) {
        return msg.sender == nextOwner;
    }

    /// > [[[[[[[[[[[ Internal Functions ]]]]]]]]]]]

    function _setOwner(address previousOwner, address newOwner) internal {
        owner = newOwner;
        emit OwnershipTransferred(previousOwner, owner);
    }

    function _setInitialOwner(address newOwner) internal {
        owner = newOwner;
        emit OwnershipTransferred(address(0), newOwner);
    }

    function _renounceOwnership() internal {
        emit OwnershipTransferred(owner, address(0));

        owner = address(0);
    }
}

File 7 of 23 : Clones.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/Clones.sol)

pragma solidity ^0.8.0;

/**
 * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for
 * deploying minimal proxy contracts, also known as "clones".
 *
 * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
 * > a minimal bytecode implementation that delegates all calls to a known, fixed address.
 *
 * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
 * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
 * deterministic method.
 *
 * _Available since v3.4._
 */
library Clones {
    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create opcode, which should never revert.
     */
    function clone(address implementation) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
            mstore(add(ptr, 0x13), shl(0x60, implementation))
            mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x36)
        }
        require(instance != address(0), "ERC1167: create failed");
    }

    /**
     * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
     *
     * This function uses the create2 opcode and a `salt` to deterministically deploy
     * the clone. Using the same `implementation` and `salt` multiple time will revert, since
     * the clones cannot be deployed twice at the same address.
     */
    function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
            mstore(add(ptr, 0x13), shl(0x60, implementation))
            mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x36, salt)
        }
        require(instance != address(0), "ERC1167: create2 failed");
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x602d8060093d393df3363d3d373d3d3d363d7300000000000000000000000000)
            mstore(add(ptr, 0x13), shl(0x60, implementation))
            mstore(add(ptr, 0x27), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x37), shl(0x60, deployer))
            mstore(add(ptr, 0x4b), salt)
            mstore(add(ptr, 0x6b), keccak256(ptr, 0x36))
            predicted := keccak256(add(ptr, 0x36), 0x55)
        }
    }

    /**
     * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
     */
    function predictDeterministicAddress(address implementation, bytes32 salt)
        internal
        view
        returns (address predicted)
    {
        return predictDeterministicAddress(implementation, salt, address(this));
    }
}

File 8 of 23 : ECDSA.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.0;

import "../Strings.sol";

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

    function _throwError(RecoverError error) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert("ECDSA: invalid signature");
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert("ECDSA: invalid signature length");
        } else if (error == RecoverError.InvalidSignatureS) {
            revert("ECDSA: invalid signature 's' value");
        } else if (error == RecoverError.InvalidSignatureV) {
            revert("ECDSA: invalid signature 'v' value");
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature` or error string. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     *
     * _Available since v4.3._
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
        // Check the signature length
        // - case 65: r,s,v signature (standard)
        // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else if (signature.length == 64) {
            bytes32 r;
            bytes32 vs;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                vs := mload(add(signature, 0x40))
            }
            return tryRecover(hash, r, vs);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength);
        }
    }

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

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     *
     * _Available since v4.3._
     */
    function tryRecover(
        bytes32 hash,
        bytes32 r,
        bytes32 vs
    ) internal pure returns (address, RecoverError) {
        bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
        uint8 v = uint8((uint256(vs) >> 255) + 27);
        return tryRecover(hash, v, r, s);
    }

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

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

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

        return (signer, RecoverError.NoError);
    }

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

    /**
     * @dev Returns an Ethereum Signed Message, created from a `hash`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
        // 32 is the length in bytes of hash,
        // enforced by the type signature above
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
    }

    /**
     * @dev Returns an Ethereum Signed Message, created from `s`. This
     * produces hash corresponding to the one signed with the
     * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
     * JSON-RPC method as part of EIP-191.
     *
     * See {recover}.
     */
    function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
    }

    /**
     * @dev Returns an Ethereum Signed Typed Data, created from a
     * `domainSeparator` and a `structHash`. This produces hash corresponding
     * to the one signed with the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
     * JSON-RPC method as part of EIP-712.
     *
     * See {recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash));
    }
}

File 9 of 23 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        // On the first call to nonReentrant, _notEntered will be true
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

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

        _;

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

File 10 of 23 : IWritingEditions.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IWritingEditionEvents {
    event RoyaltyChange(
        address indexed oldRoyaltyRecipient,
        uint256 oldRoyaltyBPS,
        address indexed newRoyaltyRecipient,
        uint256 newRoyaltyBPS
    );

    event RendererSet(address indexed renderer);

    event WritingEditionLimitSet(uint256 oldLimit, uint256 newLimit);

    event PriceSet(uint256 price);
}

interface IWritingEditions {
    struct WritingEdition {
        string name;
        string symbol;
        string description;
        string imageURI;
        string contentURI;
        uint256 price;
        uint256 limit;
        address fundingRecipient;
        address renderer;
        uint256 nonce;
        uint16 fee;
    }

    function VERSION() external view returns (uint8);

    function factory() external returns (address);

    function treasuryConfiguration() external returns (address);

    function o11y() external returns (address);

    function baseDescriptionURI() external view returns (string memory);

    function description() external view returns (string memory);

    function totalSupply() external view returns (uint256);

    function price() external view returns (uint256);

    function limit() external view returns (uint256);

    function contentURI() external view returns (string memory);

    function imageURI() external view returns (string memory);

    function fundingRecipient() external returns (address);

    function fee() external returns (uint16);

    function royaltyRecipient() external returns (address);

    function royaltyBPS() external returns (uint256);

    function renderer() external view returns (address);

    function ownerOf(uint256[] memory tokenIds)
        external
        view
        returns (address[] memory owners);

    function initialize(
        address owner_,
        WritingEdition memory edition,
        address recipient,
        string memory message,
        bool _guard
    ) external payable;

    function setFundingRecipient(address fundingRecipient_) external;

    function setPrice(uint256 price_) external;

    function setBaseDescriptionURI(string memory _baseDescriptionURI) external;

    function setLimit(uint256 limit_) external;

    function setMaxLimit() external;

    function setRoyaltyInfo(
        address payable royaltyRecipient_,
        uint256 royaltyPercentage_
    ) external;

    function toggleGuard() external;

    function purchase(address tokenRecipient, string memory message)
        external
        payable
        returns (uint256 tokenId);

    function purchaseThroughFactory(
        address tokenRecipient,
        string memory message
    ) external payable returns (uint256 tokenId);

    function mint(address tokenRecipient) external returns (uint256 tokenId);

    function setRenderer(address renderer_) external;

    function contractURI() external view returns (string memory);
}

File 11 of 23 : IObservability.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IObservabilityEvents {
    /// > [[[[[[[[[[[ Factory events ]]]]]]]]]]]

    event CloneDeployed(
        address indexed factory,
        address indexed owner,
        address indexed clone
    );

    event TributarySet(
        address indexed factory,
        address indexed clone,
        address oldTributary,
        address indexed newTributary
    );

    event FactoryLimitSet(
        address indexed factory,
        uint256 oldLimit,
        uint256 newLimit
    );

    event FactoryGuardSet(bool guard);

    event FactoryImplementationSet(
        address indexed factory,
        address indexed oldImplementation,
        address indexed newImplementation
    );

    /// > [[[[[[[[[[[ Clone events ]]]]]]]]]]]

    event WritingEditionPurchased(
        address indexed clone,
        uint256 tokenId,
        address indexed recipient,
        uint256 price,
        string message
    );

    event Transfer(
        address indexed clone,
        address indexed from,
        address indexed to,
        uint256 tokenId
    );

    event RoyaltyChange(
        address indexed clone,
        address indexed oldRoyaltyRecipient,
        uint256 oldRoyaltyBPS,
        address indexed newRoyaltyRecipient,
        uint256 newRoyaltyBPS
    );

    event RendererSet(address indexed clone, address indexed renderer);

    event WritingEditionLimitSet(
        address indexed clone,
        uint256 oldLimit,
        uint256 newLimit
    );

    event PriceSet(address indexed clone, uint256 oldLimit, uint256 newLimit);

    event FundingRecipientSet(
        address indexed clone,
        address indexed oldFundingRecipient,
        address indexed newFundingRecipient
    );

    event BaseDescriptionURISet(
        address indexed clone,
        string oldBaseDescriptionURI,
        string newBaseDescriptionURI
    );
}

interface IObservability {
    function emitDeploymentEvent(address owner, address clone) external;

    function emitTributarySet(
        address clone,
        address oldTributary,
        address newTributary
    ) external;

    function emitFactoryGuardSet(bool guard) external;

    function emitFactoryImplementationSet(
        address oldImplementation,
        address newImplementation
    ) external;

    function emitFactoryLimitSet(uint256 oldLimit, uint256 newLimit) external;

    function emitTransferEvent(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function emitWritingEditionPurchased(
        uint256 tokenId,
        address recipient,
        uint256 price,
        string memory message
    ) external;

    function emitRoyaltyChange(
        address oldRoyaltyRecipient,
        uint256 oldRoyaltyBPS,
        address newRoyaltyRecipient,
        uint256 newRoyaltyBPS
    ) external;

    function emitRendererSet(address renderer) external;

    function emitWritingEditionLimitSet(uint256 oldLimit, uint256 newLimit)
        external;

    function emitFundingRecipientSet(
        address oldFundingRecipient,
        address newFundingRecipient
    ) external;

    function emitPriceSet(uint256 oldPrice, uint256 newPrice) external;

    function emitBaseDescriptionURISet(
        string memory oldBaseDescriptionURI,
        string memory newBaseDescriptionURI
    ) external;
}

File 12 of 23 : IFeeConfiguration.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IFeeConfigurationEvents {
    event FeeSwitch(bool on);

    event MinimumFee(uint16 fee);

    event MaximumFee(uint16 fee);
}

interface IFeeConfiguration {
    function on() external returns (bool);

    function maximumFee() external returns (uint16);

    function minimumFee() external returns (uint16);

    function switchFee() external;

    function updateMinimumFee(uint16 newFee) external;

    function updateMaximumFee(uint16 newFe) external;

    function valid(uint16) external view returns (bool);
}

File 13 of 23 : IRenderer.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IRenderer {
    function tokenURI(uint256 tokenId) external view returns (string calldata);

    function contractURI() external view returns (string calldata);
}

File 14 of 23 : ITreasuryConfiguration.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface ITreasuryConfigurationEvents {
    event TreasurySet(address indexed treasury, address indexed newTreasury);

    event TributaryRegistrySet(
        address indexed tributaryRegistry,
        address indexed newTributaryRegistry
    );

    event DistributionSet(
        address indexed distribution,
        address indexed newDistribution
    );

    event FeeConfigurationSet(
        address indexed feeConfiguration,
        address indexed newFeeConfiguration
    );
}

interface ITreasuryConfiguration {
    function treasury() external returns (address payable);

    function tributaryRegistry() external returns (address);

    function distribution() external returns (address);

    function feeConfiguration() external returns (address);

    function setTreasury(address payable newTreasury) external;

    function setTributaryRegistry(address newTributaryRegistry) external;

    function setDistribution(address newDistribution) external;

    function setFeeConfiguration(address newFeeConfiguration) external;
}

File 15 of 23 : ITreasury.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface ITreasuryEvents {
    event Transfer(address indexed from, address indexed to, uint256 value);

    event ERC20Transfer(
        address indexed token,
        address indexed from,
        address indexed to,
        uint256 amount
    );

    event ERC721Transfer(
        address indexed token,
        address indexed from,
        address indexed to,
        uint256 tokenId
    );
}

interface ITreasury {
    struct Call {
        // The target of the transaction.
        address target;
        // The value passed into the transaction.
        uint96 value;
        // Any data passed with the call.
        bytes data;
    }

    function treasuryConfiguration() external view returns (address);

    function transferFunds(address payable to, uint256 value) external;

    function transferERC20(
        address token,
        address to,
        uint256 value
    ) external;

    function transferERC721(
        address token,
        address from,
        address to,
        uint256 tokenId
    ) external;

    function contributeWithTributary(address tributary) external payable;

    function contribute(uint256 amount) external payable;
}

File 16 of 23 : ITributaryRegistry.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface ITributaryRegistry {
    function allowedRegistrar(address account) external view returns (bool);

    function producerToTributary(address producer)
        external
        view
        returns (address tributary);

    function singletonProducer(address producer) external view returns (bool);

    function addRegistrar(address registrar) external;

    function removeRegistrar(address registrar) external;

    function addSingletonProducer(address producer) external;

    function removeSingletonProducer(address producer) external;

    function setTributary(address producer, address newTributary) external;
}

File 17 of 23 : ERC721.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

import "./interface/IERC721.sol";
import "../ERC165/ERC165.sol";

/**
 * Based on: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
 */
contract ERC721 is ERC165, IERC721, IERC721Events {
    mapping(uint256 => address) internal _owners;
    mapping(address => uint256) internal _balances;
    mapping(uint256 => address) private _tokenApprovals;
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return
            interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    function balanceOf(address owner)
        external
        view
        virtual
        override
        returns (uint256)
    {
        require(
            owner != address(0),
            "ERC721: balance query for the zero address"
        );
        return _balances[owner];
    }

    function ownerOf(uint256 tokenId) external view virtual returns (address) {
        return _ownerOf(tokenId);
    }

    function _ownerOf(uint256 tokenId) internal view returns (address) {
        address owner = _owners[tokenId];
        require(
            owner != address(0),
            "ERC721: owner query for nonexistent token"
        );
        return owner;
    }

    function approve(address to, uint256 tokenId) external virtual override {
        address owner = _ownerOf(tokenId);
        require(to != owner, "ERC721: approval to current owner");

        require(
            msg.sender == owner || isApprovedForAll(owner, msg.sender),
            "ERC721: approve caller is not owner nor approved for all"
        );

        _approve(to, tokenId);
    }

    function getApproved(uint256 tokenId)
        public
        view
        virtual
        override
        returns (address)
    {
        require(
            _exists(tokenId),
            "ERC721: approved query for nonexistent token"
        );

        return _tokenApprovals[tokenId];
    }

    function setApprovalForAll(address operator, bool approved)
        external
        virtual
        override
    {
        require(operator != msg.sender, "ERC721: approve to caller");

        _operatorApprovals[msg.sender][operator] = approved;
        emit ApprovalForAll(msg.sender, operator, approved);
    }

    function isApprovedForAll(address owner, address operator)
        public
        view
        virtual
        override
        returns (bool)
    {
        return _operatorApprovals[owner][operator];
    }

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external virtual override {
        //solhint-disable-next-line max-line-length
        require(
            _isApprovedOrOwner(msg.sender, tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );

        _transfer(from, to, tokenId);
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external virtual override {
        _safeTransferFrom(from, to, tokenId, "");
    }

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) external virtual override {
        _safeTransferFrom(from, to, tokenId, _data);
    }

    function _safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        require(
            _isApprovedOrOwner(msg.sender, tokenId),
            "ERC721: transfer caller is not owner nor approved"
        );
        _safeTransfer(from, to, tokenId, _data);
    }

    function _safeTransfer(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) internal virtual {
        _transfer(from, to, tokenId);
        require(
            _checkOnERC721Received(from, to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _owners[tokenId] != address(0);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId)
        internal
        view
        virtual
        returns (bool)
    {
        require(
            _exists(tokenId),
            "ERC721: operator query for nonexistent token"
        );
        address owner = _ownerOf(tokenId);
        return (spender == owner ||
            getApproved(tokenId) == spender ||
            isApprovedForAll(owner, spender));
    }

    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);
        require(
            _checkOnERC721Received(address(0), to, tokenId, _data),
            "ERC721: transfer to non ERC721Receiver implementer"
        );
    }

    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0), "ERC721: mint to the zero address");
        require(!_exists(tokenId), "ERC721: token already minted");

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

        _balances[to] += 1;
        _owners[tokenId] = to;

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

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

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

        // Clear approvals
        _approve(address(0), tokenId);

        _balances[owner] -= 1;
        delete _owners[tokenId];

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

    function _transfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {
        require(
            _ownerOf(tokenId) == from,
            "ERC721: transfer of token that is not own"
        );
        require(to != address(0), "ERC721: transfer to the zero address");

        _beforeTokenTransfer(from, to, tokenId);

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

        _balances[from] -= 1;
        _balances[to] += 1;
        _owners[tokenId] = to;

        emit Transfer(from, to, tokenId);
    }

    function _approve(address to, uint256 tokenId) internal virtual {
        _tokenApprovals[tokenId] = to;
        emit Approval(_ownerOf(tokenId), to, tokenId);
    }

    function _checkOnERC721Received(
        address from,
        address to,
        uint256 tokenId,
        bytes memory _data
    ) private returns (bool) {
        if (to.code.length > 0) {
            // slither-disable-next-line unused-return
            try
                IERC721Receiver(to).onERC721Received(
                    msg.sender,
                    from,
                    tokenId,
                    _data
                )
            returns (bytes4 retval) {
                return retval == IERC721Receiver(to).onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert(
                        "ERC721: transfer to non ERC721Receiver implementer"
                    );
                } else {
                    // solhint-disable-next-line no-inline-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } else {
            return true;
        }
    }

    function _beforeTokenTransfer(
        address from,
        address to,
        uint256 tokenId
    ) internal virtual {}
}

File 18 of 23 : ERC165.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

abstract contract ERC165 is IERC165 {
    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override
        returns (bool)
    {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 19 of 23 : IERC721.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IERC721 {
    function balanceOf(address owner) external view returns (uint256 balance);

    function ownerOf(uint256 tokenId) external view returns (address owner);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) external;

    function approve(address to, uint256 tokenId) external;

    function getApproved(uint256 tokenId)
        external
        view
        returns (address operator);

    function setApprovalForAll(address operator, bool _approved) external;

    function isApprovedForAll(address owner, address operator)
        external
        view
        returns (bool);

    function safeTransferFrom(
        address from,
        address to,
        uint256 tokenId,
        bytes calldata data
    ) external;
}

interface IERC721Events {
    event Transfer(
        address indexed from,
        address indexed to,
        uint256 indexed tokenId
    );
    event Approval(
        address indexed owner,
        address indexed approved,
        uint256 indexed tokenId
    );
    event ApprovalForAll(
        address indexed owner,
        address indexed operator,
        bool approved
    );
}

interface IERC721Metadata {
    function name() external view returns (string memory);

    function symbol() external view returns (string memory);

    function tokenURI(uint256 tokenId) external view returns (string memory);
}

interface IERC721Burnable is IERC721 {
    function burn(uint256 tokenId) external;
}

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

interface IERC721Royalties {
    function getFeeRecipients(uint256 id)
        external
        view
        returns (address payable[] memory);

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

File 20 of 23 : IERC2981.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/**
 * @title IERC2981
 * @notice Interface for the NFT Royalty Standard
 */
interface IERC2981 {
    // / bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a

    /**
     * @notice Called with the sale price to determine how much royalty
     *         is owed and to whom.
     * @param _tokenId - the NFT asset queried for royalty information
     * @param _salePrice - the sale price of the NFT asset specified by _tokenId
     * @return receiver - address of who should be sent the royalty payment
     * @return royaltyAmount - the royalty payment amount for _salePrice
     */
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice)
        external
        view
        returns (address receiver, uint256 royaltyAmount);
}

File 21 of 23 : TransactionReentrancyGuard.sol
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

/**
 * @title TransactionReentrancyGuard
 * @notice Transaction level reentrancy guard, used to prevent calling a
 * function multiple times in the same transaction, e.g. minting a token
 * using a multicall contract. The guard accesses a storage variable twice
 * and compares the gas used, taking advantage of EIP-2929 cold/warm storage
 * read costs.
 *
 * From EIP-2929: Gas cost increases for state access opcodes:
 *     "For SLOAD, if the (address, storage_key) pair (where address
 *     is the address of the contract whose storage is being read) is not yet
 *     in accessed_storage_keys, charge COLD_SLOAD_COST gas and add the pair
 *     to accessed_storage_keys. If the pair is already in accessed_storage_keys,
 *     charge WARM_STORAGE_READ_COST gas."
 *
 * Implementation was forked from bertani.eth, after a thread involving these
 * anon solidity giga-brains: [at]rage_pit, [at]transmissions11, 0age.eth.
 */
contract TransactionReentrancyGuard {
    bool public guardOn;

    uint256 internal GUARD = 1;

    /// > [[[[[[[[[[[ Modifiers ]]]]]]]]]]]
    modifier guard() {
        // If guard is on, run guard.
        if (guardOn) {
            _guard();
        }
        _;
    }

    constructor(bool _guardOn) {
        _setGuard(_guardOn);
    }

    function _guard() internal view {
        // Store current gas left.
        uint256 t0 = gasleft();

        // Load GUARD from storage.
        uint256 g = GUARD;

        // Store current gas left.
        uint256 t1 = gasleft();

        // Load GUARD from storage.
        uint256 m = GUARD;

        // Assert the cost of acessing `g` is greater than the
        // cost of accessing `m`, which implies the first SLOAD
        // was charged COLD_SLOAD_COST and the second SLOAD was
        // charged WARM_STORAGE_READ_COST. Hence this is the first
        // time the function has been called.
        require(t1 - gasleft() < t0 - t1);
    }

    function _toggleGuard() internal {
        _setGuard(!guardOn);
    }

    function _setGuard(bool _guardOn) internal {
        guardOn = _guardOn;
    }
}

File 22 of 23 : Base64.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (utils/Base64.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return result;
    }
}

File 23 of 23 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Strings.sol)

pragma solidity ^0.8.0;

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        // Inspired by OraclizeAPI's implementation - MIT licence
        // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

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

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        if (value == 0) {
            return "0x00";
        }
        uint256 temp = value;
        uint256 length = 0;
        while (temp != 0) {
            length++;
            temp >>= 8;
        }
        return toHexString(value, length);
    }

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

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

Settings
{
  "remappings": [
    "@ds/=lib/multicall/lib/ds-test/src/",
    "@std/=lib/multicall/lib/forge-std/src/",
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "multicall/=lib/multicall/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "src/=src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london"
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_treasuryConfiguration","type":"address"},{"internalType":"uint256","name":"_maxLimit","type":"uint256"},{"internalType":"bool","name":"_guardOn","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"string","name":"oldBaseDescriptionURI","type":"string"},{"indexed":false,"internalType":"string","name":"newBaseDescriptionURI","type":"string"}],"name":"BaseDescriptionURISet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"}],"name":"CloneDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"EditionsDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"guard","type":"bool"}],"name":"FactoryGuardSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"FactoryImplementationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"FactoryLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"oldFundingRecipient","type":"address"},{"indexed":true,"internalType":"address","name":"newFundingRecipient","type":"address"}],"name":"FundingRecipientSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldImplementation","type":"address"},{"indexed":true,"internalType":"address","name":"newImplementation","type":"address"}],"name":"NewImplementation","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":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"PriceSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"renderer","type":"address"}],"name":"RendererSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"oldRoyaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldRoyaltyBPS","type":"uint256"},{"indexed":true,"internalType":"address","name":"newRoyaltyRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"newRoyaltyBPS","type":"uint256"}],"name":"RoyaltyChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"factory","type":"address"},{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"address","name":"oldTributary","type":"address"},{"indexed":true,"internalType":"address","name":"newTributary","type":"address"}],"name":"TributarySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldLimit","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newLimit","type":"uint256"}],"name":"WritingEditionLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"clone","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"price","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"WritingEditionPurchased","type":"event"},{"inputs":[],"name":"CREATE_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"baseDescriptionURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cancelOwnershipTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"contentURI","type":"string"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"address","name":"fundingRecipient","type":"address"},{"internalType":"address","name":"renderer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint16","name":"fee","type":"uint16"}],"internalType":"struct IWritingEditions.WritingEdition","name":"edition","type":"tuple"}],"name":"create","outputs":[{"internalType":"address","name":"clone","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"contentURI","type":"string"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"address","name":"fundingRecipient","type":"address"},{"internalType":"address","name":"renderer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint16","name":"fee","type":"uint16"}],"internalType":"struct IWritingEditions.WritingEdition","name":"edition","type":"tuple"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"},{"internalType":"address","name":"tokenRecipient","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"createWithSignature","outputs":[{"internalType":"address","name":"clone","type":"address"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"string","name":"description","type":"string"},{"internalType":"string","name":"imageURI","type":"string"},{"internalType":"string","name":"contentURI","type":"string"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"limit","type":"uint256"},{"internalType":"address","name":"fundingRecipient","type":"address"},{"internalType":"address","name":"renderer","type":"address"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"uint16","name":"fee","type":"uint16"}],"internalType":"struct IWritingEditions.WritingEdition","name":"edition","type":"tuple"}],"name":"getSalt","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardOn","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isNextOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"isValid","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"o11y","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"}],"name":"predictDeterministicAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"clone","type":"address"},{"internalType":"address","name":"tokenRecipient","type":"address"},{"internalType":"string","name":"message","type":"string"}],"name":"purchaseThroughFactory","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"salts","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_guardOn","type":"bool"}],"name":"setGuard","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_implementation","type":"address"}],"name":"setImplementation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxLimit","type":"uint256"}],"name":"setLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"clone","type":"address"},{"internalType":"address","name":"_tributary","type":"address"}],"name":"setTributary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"nextOwner_","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"treasuryConfiguration","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]

61010060405260016080523480156200001757600080fd5b50604051620072da380380620072da8339810160408190526200003a916200028b565b83620000468162000207565b5060016002556001600160a01b038316620000a75760405162461bcd60e51b815260206004820152601860248201527f6d7573742073657420747265617375727920636f6e6669670000000000000000604482015260640160405180910390fd5b6001600160a01b03831660c05260408051808201825260018152603160f81b60209182015281517f2aef22f9d7df5f9d21c56d14029233f3fdaa91917727e1eb68e504d27072d6cd818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc681840152466060820152306080828101919091528351808303909101815260a090910192839052805191012060e0526200014e9062000252565b604051809103906000f0801580156200016b573d6000803e3d6000fd5b506001600160a01b031660a0819052604051309185916200018c9062000260565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103906000f080158015620001c9573d6000803e3d6000fd5b506003805492151560ff196001600160a01b039390931661010002929092166001600160a81b03199093169290921717905560055550620002e59050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b6108f8806200257783390190565b61446b8062002e6f83390190565b80516001600160a01b03811681146200028657600080fd5b919050565b60008060008060808587031215620002a257600080fd5b620002ad856200026e565b9350620002bd602086016200026e565b92506040850151915060608501518015158114620002da57600080fd5b939692955090935050565b60805160a05160c05160e05161222062000357600039600081816102af01526114ee0152600081816104b201526114600152600081816103ae01528181610655015281816107360152818161097601528181610e290152818161124e01526113a90152600061057601526122206000f3fe60806040526004361061019c5760003560e01c8063715018a6116100ec578063ad768cb31161008a578063e6d283a611610064578063e6d283a6146104f4578063ed459df214610524578063f2fde38b14610544578063ffa1ad741461056457600080fd5b8063ad768cb31461048d578063bee0ec0b146104a0578063d784d426146104d457600080fd5b80638f32d59b116100c65780638f32d59b1461041a578063a56600631461043a578063a61788f31461045a578063ad3c6b611461047a57600080fd5b8063715018a6146103d057806379ba5097146103e55780638da5cb5b146103fa57600080fd5b80633644e515116101595780635c60da1b116101335780635c60da1b1461033d57806364d44b1e146103625780636f19d0b614610382578063705bf3681461039c57600080fd5b80633644e5151461029d5780633cb70c2d146102d15780635a5df2b71461031d57600080fd5b806311ba1741146101a15780631552a369146101e85780631a861d261461021857806323452b9c1461022e57806327ea6f2b14610245578063360d0fad14610265575b600080fd5b3480156101ad57600080fd5b506101d57f682b3920f0bf70eb759a59b34e1c6ff903edb5ced39efd66303d66cc0eac774e81565b6040519081526020015b60405180910390f35b3480156101f457600080fd5b506102086102033660046119e7565b6105aa565b60405190151581526020016101df565b34801561022457600080fd5b506101d560055481565b34801561023a57600080fd5b506102436105c5565b005b34801561025157600080fd5b50610243610260366004611a37565b61060a565b34801561027157600080fd5b50610285610280366004611a50565b6106c1565b6040516001600160a01b0390911681526020016101df565b3480156102a957600080fd5b506101d57f000000000000000000000000000000000000000000000000000000000000000081565b3480156102dd57600080fd5b506103106040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b6040516101df9190611ad8565b34801561032957600080fd5b50610285610338366004611cfd565b6106d7565b34801561034957600080fd5b506003546102859061010090046001600160a01b031681565b34801561036e57600080fd5b5061024361037d366004611d32565b6106f5565b34801561038e57600080fd5b506003546102089060ff1681565b3480156103a857600080fd5b506102857f000000000000000000000000000000000000000000000000000000000000000081565b3480156103dc57600080fd5b506102436107ae565b3480156103f157600080fd5b506102436107e2565b34801561040657600080fd5b50600054610285906001600160a01b031681565b34801561042657600080fd5b506000546001600160a01b03163314610208565b34801561044657600080fd5b50610243610455366004611d5b565b610899565b34801561046657600080fd5b506101d5610475366004611d94565b610abe565b610285610488366004611de4565b610aca565b6101d561049b366004611e93565b610d53565b3480156104ac57600080fd5b506102857f000000000000000000000000000000000000000000000000000000000000000081565b3480156104e057600080fd5b506102436104ef366004611ef5565b610dcf565b34801561050057600080fd5b5061020861050f366004611a37565b60046020526000908152604090205460ff1681565b34801561053057600080fd5b506001546001600160a01b03163314610208565b34801561055057600080fd5b5061024361055f366004611ef5565b610eae565b34801561057057600080fd5b506105987f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016101df565b60006105b98686868686610f50565b90505b95945050505050565b6000546001600160a01b031633146105f85760405162461bcd60e51b81526004016105ef90611f12565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b031633146106345760405162461bcd60e51b81526004016105ef90611f12565b600554604051639ea75f2760e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639ea75f2790604401600060405180830381600087803b1580156106a157600080fd5b505af11580156106b5573d6000803e3d6000fd5b50505060059190915550565b60006106ce838330611099565b90505b92915050565b60006106d133836000604051806020016040528060008152506110f6565b6000546001600160a01b0316331461071f5760405162461bcd60e51b81526004016105ef90611f12565b6040516302f848e760e11b815281151560048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561078257600080fd5b505af1158015610796573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146107d85760405162461bcd60e51b81526004016105ef90611f12565b6107e0611412565b565b6001546001600160a01b031633146108515760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016105ef565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fb9190611f49565b6001600160a01b0316336001600160a01b03161461094a5760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016105ef565b600061095461145c565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f00000000000000000000000000000000000000000000000000000000000000008216916332c7791691869185169063a575185690602401602060405180830381865afa1580156109c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ed9190611f49565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b158015610a3e57600080fd5b505af1158015610a52573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b158015610aa157600080fd5b505af1158015610ab5573d6000803e3d6000fd5b50505050505050565b60006106ce83836114e7565b6000600280541415610b1e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105ef565b6002805560a08701513414610b675760405162461bcd60e51b815260206004820152600f60248201526e696e636f72726563742076616c756560881b60448201526064016105ef565b6000610b7389896114e7565b60008181526004602052604090205490915060ff1615610cb357610be6600360019054906101000a90046001600160a01b03168a8a600001518b602001518c6101200151604051602001610bca9493929190611f66565b6040516020818303038152906040528051906020012030611099565b91506000826001600160a01b03163b11610c3a5760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016105ef565b6040516333f0ff8b60e21b81526001600160a01b0383169063cfc3fe2c903490610c6a9088908890600401611fae565b60206040518083038185885af1158015610c88573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cad9190611fd2565b50610d42565b6000818152600460205260409020805460ff19166001179055610cd98982898989610f50565b610d335760405162461bcd60e51b815260206004820152602560248201527f696e76616c6964206f7220756e61626c6520746f20766572696679207369676e604482015264617475726560d81b60648201526084016105ef565b610d3f898986866110f6565b91505b506001600255979650505050505050565b6000836001600160a01b031663cfc3fe2c3485856040518463ffffffff1660e01b8152600401610d84929190611fae565b60206040518083038185885af1158015610da2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610dc79190611fd2565b949350505050565b6000546001600160a01b03163314610df95760405162461bcd60e51b81526004016105ef90611f12565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906322fd9ed790604401600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610ed85760405162461bcd60e51b81526004016105ef90611f12565b6001600160a01b038116610f2e5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016105ef565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b038616610f9a5760405162461bcd60e51b815260206004820152600f60248201526e63616e6e6f742076616c696461746560881b60448201526064016105ef565b6001600160a01b0386163b1561107057604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b03881690631626ba7e9061100d9089908590606501611feb565b602060405180830381865afa925050508015611046575060408051601f3d908101601f1916820190925261104391810190612004565b60015b6110545760009150506105bc565b6001600160e01b031916630b135d3f60e11b1491506105bc9050565b600061107e8686868661163f565b6001600160a01b039081169088161491505095945050505050565b60405172602d8060093d393df3363d3d373d3d3d363d7360681b8152606093841b60138201526f5af43d82803e903d91602b57fd5bf3ff60801b6027820152921b6037830152604b8201526036808220606b830152605591012090565b60e08301516000906001600160a01b031661114c5760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016105ef565b6005541580611170575060008460c0015111801561117057506005548460c0015111155b6111ac5760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016105ef565b60035484516020808701516101208801516040516111fe9561010090046001600160a01b0316946111e3948c949193919201611f66565b60405160208183030381529060405280519060200120611667565b9050600061120a61145c565b90506001600160a01b038116156113165760e0850151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000000000000000000000000000000000000000000000909116906332c7791690606401600060405180830381600087803b15801561129457600080fd5b505af11580156112a8573d6000803e3d6000fd5b5050505060e085015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156112fd57600080fd5b505af1158015611311573d6000803e3d6000fd5b505050505b60035460405163dbdc37f360e01b81526001600160a01b0384169163dbdc37f3913491611353918b918b918b918b9160ff9091169060040161202e565b6000604051808303818588803b15801561136c57600080fd5b505af1158015611380573d6000803e3d6000fd5b50506040516306e9fe9960e41b81526001600160a01b038a8116600483015286811660248301527f0000000000000000000000000000000000000000000000000000000000000000169350636e9fe99092506044019050600060405180830381600087803b1580156113f157600080fd5b505af1158015611405573d6000803e3d6000fd5b5050505050949350505050565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e29190611f49565b905090565b60006106ce7f00000000000000000000000000000000000000000000000000000000000000007f682b3920f0bf70eb759a59b34e1c6ff903edb5ced39efd66303d66cc0eac774e858560000151866020015187606001518860800151604051602001611556949392919061217d565b604051602081830303815290604052805190602001208660c001518760a001518860e001518961010001518a61012001518b61014001516040516020016115f3999897969594939291909889526001600160a01b0397881660208a0152604089019690965260608801949094526080870192909252841660a086015290921660c084015260e083019190915261ffff166101008201526101200190565b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600080600061165087878787611706565b9150915061165d816117f3565b5095945050505050565b600060405172602d8060093d393df3363d3d373d3d3d363d7360681b81528360601b60138201526e5af43d82803e903d91602b57fd5bf360881b6027820152826036826000f59150506001600160a01b0381166106d15760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016105ef565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561173d57506000905060036117ea565b8460ff16601b1415801561175557508460ff16601c14155b1561176657506000905060046117ea565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156117ba573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117e3576000600192509250506117ea565b9150600090505b94509492505050565b6000816004811115611807576118076121d4565b14156118105750565b6001816004811115611824576118246121d4565b14156118725760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016105ef565b6002816004811115611886576118866121d4565b14156118d45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016105ef565b60038160048111156118e8576118e86121d4565b14156119415760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016105ef565b6004816004811115611955576119556121d4565b14156119ae5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016105ef565b50565b6001600160a01b03811681146119ae57600080fd5b80356119d1816119b1565b919050565b803560ff811681146119d157600080fd5b600080600080600060a086880312156119ff57600080fd5b8535611a0a816119b1565b945060208601359350611a1f604087016119d6565b94979396509394606081013594506080013592915050565b600060208284031215611a4957600080fd5b5035919050565b60008060408385031215611a6357600080fd5b8235611a6e816119b1565b946020939093013593505050565b60005b83811015611a97578181015183820152602001611a7f565b83811115611aa6576000848401525b50505050565b60008151808452611ac4816020860160208601611a7c565b601f01601f19169290920160200192915050565b6020815260006106ce6020830184611aac565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715611b2557611b25611aeb565b60405290565b600082601f830112611b3c57600080fd5b813567ffffffffffffffff80821115611b5757611b57611aeb565b604051601f8301601f19908116603f01168101908282118183101715611b7f57611b7f611aeb565b81604052838152866020858801011115611b9857600080fd5b836020870160208301376000602085830101528094505050505092915050565b803561ffff811681146119d157600080fd5b60006101608284031215611bdd57600080fd5b611be5611b01565b9050813567ffffffffffffffff80821115611bff57600080fd5b611c0b85838601611b2b565b83526020840135915080821115611c2157600080fd5b611c2d85838601611b2b565b60208401526040840135915080821115611c4657600080fd5b611c5285838601611b2b565b60408401526060840135915080821115611c6b57600080fd5b611c7785838601611b2b565b60608401526080840135915080821115611c9057600080fd5b50611c9d84828501611b2b565b60808301525060a082013560a082015260c082013560c0820152611cc360e083016119c6565b60e0820152610100611cd68184016119c6565b908201526101208281013590820152610140611cf3818401611bb8565b9082015292915050565b600060208284031215611d0f57600080fd5b813567ffffffffffffffff811115611d2657600080fd5b610dc784828501611bca565b600060208284031215611d4457600080fd5b81358015158114611d5457600080fd5b9392505050565b60008060408385031215611d6e57600080fd5b8235611d79816119b1565b91506020830135611d89816119b1565b809150509250929050565b60008060408385031215611da757600080fd5b8235611db2816119b1565b9150602083013567ffffffffffffffff811115611dce57600080fd5b611dda85828601611bca565b9150509250929050565b600080600080600080600060e0888a031215611dff57600080fd5b8735611e0a816119b1565b9650602088013567ffffffffffffffff80821115611e2757600080fd5b611e338b838c01611bca565b9750611e4160408b016119d6565b965060608a0135955060808a0135945060a08a01359150611e61826119b1565b90925060c08901359080821115611e7757600080fd5b50611e848a828b01611b2b565b91505092959891949750929550565b600080600060608486031215611ea857600080fd5b8335611eb3816119b1565b92506020840135611ec3816119b1565b9150604084013567ffffffffffffffff811115611edf57600080fd5b611eeb86828701611b2b565b9150509250925092565b600060208284031215611f0757600080fd5b8135611d54816119b1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b600060208284031215611f5b57600080fd5b8151611d54816119b1565b6001600160a01b0385168152608060208201819052600090611f8a90830186611aac565b8281036040840152611f9c8186611aac565b91505082606083015295945050505050565b6001600160a01b0383168152604060208201819052600090610dc790830184611aac565b600060208284031215611fe457600080fd5b5051919050565b828152604060208201526000610dc76040830184611aac565b60006020828403121561201657600080fd5b81516001600160e01b031981168114611d5457600080fd5b60018060a01b038616815260a06020820152600085516101608060a085015261205b610200850183611aac565b91506020880151609f19808685030160c08701526120798483611aac565b935060408a01519150808685030160e08701526120968483611aac565b935060608a015191506101008187860301818801526120b58584611aac565b945060808b015192506101208288870301818901526120d48685611aac565b955060a08c015193506101409250838389015260c08c01518589015260e08c0151945061210d6101808901866001600160a01b03169052565b908b01516001600160a01b03166101a08801528a01516101c087015289015161ffff81166101e087015291506121409050565b506001600160a01b038616604084015282810360608401526121628186611aac565b915050612173608083018415159052565b9695505050505050565b6000855161218f818460208a01611a7c565b8551908301906121a3818360208a01611a7c565b85519101906121b6818360208901611a7c565b84519101906121c9818360208801611a7c565b019695505050505050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122039472762c33600bb9c364030aaf7e8f014d849edcb745dac6fcc79fe26f1adbf64736f6c634300080c0033608060405234801561001057600080fd5b506108d8806100206000396000f3fe608060405234801561001057600080fd5b50600436106100cf5760003560e01c80636e9fe9901161008c578063a6d49dca11610066578063a6d49dca14610181578063bbeeea3514610194578063f0091f4f146101a7578063fc0ed66a146101ba57600080fd5b80636e9fe990146101485780639ea75f271461015b578063a13b024a1461016e57600080fd5b806305f091ce146100d45780630ba1ba83146100e957806322fd9ed7146100fc578063271c9e1f1461010f57806332c77916146101225780635115de1414610135575b600080fd5b6100e76100e2366004610528565b6101cd565b005b6100e76100f736600461056d565b610205565b6100e761010a3660046105b1565b610256565b6100e761011d3660046105e4565b610295565b6100e7610130366004610606565b6102d5565b6100e7610143366004610649565b610322565b6100e76101563660046105b1565b61035b565b6100e76101693660046105e4565b61039a565b6100e761017c3660046105e4565b6103d2565b6100e761018f366004610664565b61040a565b6100e76101a2366004610743565b610459565b6100e76101b53660046105b1565b6104ae565b6100e76101c83660046107a4565b6104ed565b60405181151581527f48f558308664f690bb62e2e7859cb0cc0e4206065ee613c4124fe46f2fbba33d9060200160405180910390a150565b60408051848152602081018390526001600160a01b03808516929087169133917f445617be23737f769f001a14b79033ca2d6157166e27ff0fa79cb3d7b1473ce5910160405180910390a450505050565b6040516001600160a01b03808316919084169033907f0a7edb67b6ca4b003a2209d9b62ce843cd9fa2dcc68932f6e6eba54c16c32d0290600090a45050565b604080518381526020810183905233917f4b4a2ff355c13f32702752ac92eeaf13c9b36031372aadda0599e3f0e9278fb591015b60405180910390a25050565b6040516001600160a01b038381168252808316919085169033907f08493ccfa704da7eac9de51bbe498add35885fd4fde0f8cb59a30d544d7a6cea906020015b60405180910390a4505050565b6040516001600160a01b0382169033907f64a09d2d1013a7b08dfa13b95383781d6723b1e504df7e58e16cbda867950b5290600090a350565b6040516001600160a01b03808316919084169033907fe30784b97c85804517b7c35cb82a1ba9f5f5e7a90ceb80ffab76908cb9b1a70890600090a45050565b604080518381526020810183905233917fb59835113815d633ef2c99e436a22350ceb0286c3ebbb62f66592530675525e691016102c9565b604080518381526020810183905233917fd9359f6744c286382115ed720fc5e2e5da40d7aa33113ed0f1a3a4557c87ae4091016102c9565b816001600160a01b0316836001600160a01b0316336001600160a01b03167fd1398bee19313d6bf672ccb116e51f4a1a947e91c757907f51fbb5b5e56c698f8460405161031591815260200190565b826001600160a01b0316336001600160a01b03167f6d127962f8e412a3050dd402e293265d1b21852a77b965abd7b16a1f4de86ed08685856040516104a093929190610855565b60405180910390a350505050565b6040516001600160a01b03808316919084169033907fcfdb5bf35cd540089cd8f95da6d222385981136736f852cfa4203ecc6be705d990600090a45050565b336001600160a01b03167fcc112814b751eaa17155a559b546552f6dacebd853ad2a950b22a929ff94535783836040516102c992919061087d565b60006020828403121561053a57600080fd5b8135801515811461054a57600080fd5b9392505050565b80356001600160a01b038116811461056857600080fd5b919050565b6000806000806080858703121561058357600080fd5b61058c85610551565b9350602085013592506105a160408601610551565b9396929550929360600135925050565b600080604083850312156105c457600080fd5b6105cd83610551565b91506105db60208401610551565b90509250929050565b600080604083850312156105f757600080fd5b50508035926020909101359150565b60008060006060848603121561061b57600080fd5b61062484610551565b925061063260208501610551565b915061064060408501610551565b90509250925092565b60006020828403121561065b57600080fd5b61054a82610551565b60008060006060848603121561067957600080fd5b61068284610551565b925061069060208501610551565b9150604084013590509250925092565b634e487b7160e01b600052604160045260246000fd5b600082601f8301126106c757600080fd5b813567ffffffffffffffff808211156106e2576106e26106a0565b604051601f8301601f19908116603f0116810190828211818310171561070a5761070a6106a0565b8160405283815286602085880101111561072357600080fd5b836020870160208301376000602085830101528094505050505092915050565b6000806000806080858703121561075957600080fd5b8435935061076960208601610551565b925060408501359150606085013567ffffffffffffffff81111561078c57600080fd5b610798878288016106b6565b91505092959194509250565b600080604083850312156107b757600080fd5b823567ffffffffffffffff808211156107cf57600080fd5b6107db868387016106b6565b935060208501359150808211156107f157600080fd5b506107fe858286016106b6565b9150509250929050565b6000815180845260005b8181101561082e57602081850181015186830182015201610812565b81811115610840576000602083870101525b50601f01601f19169290920160200192915050565b8381528260208201526060604082015260006108746060830184610808565b95945050505050565b6040815260006108906040830185610808565b8281036020840152610874818561080856fea26469706673582212208944b489ba8bbcd39da30db625f8682998a971df1eff1b2fcceb6e9dd7a7e69864736f6c634300080c0033610100604052600160028190556080523480156200001c57600080fd5b506040516200446b3803806200446b8339810160408190526200003f91620001f4565b600160006200004e816200018c565b506001805460ff60a01b1916600160a01b831515021790555060016003556001600160a01b038316620000bb5760405162461bcd60e51b815260206004820152601060248201526f6d7573742073657420666163746f727960801b60448201526064015b60405180910390fd5b6001600160a01b0380841660a0528216620001195760405162461bcd60e51b815260206004820152601f60248201527f6d7573742073657420747265617375727920636f6e66696775726174696f6e006044820152606401620000b2565b6001600160a01b0380831660c0528116620001775760405162461bcd60e51b815260206004820152601660248201527f6d75737420736574206f62736572766162696c697479000000000000000000006044820152606401620000b2565b6001600160a01b031660e052506200023e9050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b80516001600160a01b0381168114620001ef57600080fd5b919050565b6000806000606084860312156200020a57600080fd5b6200021584620001d7565b92506200022560208501620001d7565b91506200023560408501620001d7565b90509250925092565b60805160a05160c05160e051614191620002da600039600081816105ff01528181610d1801528181610e5801528181611154015281816113f3015281816114980152818161168401528181611b4a0152818161219c0152612a350152600081816107de01528181612b400152612dd1015260008181610812015281816118d80152818161196701526122c60152600061096b01526141916000f3fe6080604052600436106102ff5760003560e01c806379ba509711610190578063bee0ec0b116100dc578063ddca3f4311610095578063e985e9c51161006f578063e985e9c5146108f9578063ed459df214610919578063f2fde38b14610939578063ffa1ad741461095957600080fd5b8063ddca3f431461088f578063e2e784d5146108c4578063e8a3d485146108e457600080fd5b8063bee0ec0b146107cc578063c45a015514610800578063c7381da314610834578063c87b56dd14610849578063cfc3fe2c14610869578063dbdc37f31461087c57600080fd5b80639c1fd6e011610149578063a4d66daf11610123578063a4d66daf1461076b578063a8d13a2914610781578063b88d4fde14610796578063b9c9d93a146107b657600080fd5b80639c1fd6e014610715578063a035b1fe14610735578063a22cb4651461074b57600080fd5b806379ba50971461066b5780638ada6b0f146106805780638da5cb5b146106a05780638f32d59b146106c057806391b7f5ed146106e057806395d89b411461070057600080fd5b80633cb70c2d1161024f5780636352211e11610208578063705bf368116101e2578063705bf368146105ed57806370a0823114610621578063715018a6146106415780637284e4161461065657600080fd5b80636352211e1461058c5780636a627842146105ac5780636f19d0b6146105cc57600080fd5b80633cb70c2d146104d557806342842e0e146104ea578063475911351461050a5780634c00de82146105375780634dcd14b21461055757806356d3163d1461056c57600080fd5b80631bb534ba116102bc578063241d965111610296578063241d96511461044357806327ea6f2b146104635780632a55205a146104835780633a1b1d57146104c257600080fd5b80631bb534ba146103ee57806323452b9c1461040e57806323b872dd1461042357600080fd5b806301ffc9a71461030457806306fdde0314610339578063081812fc1461035b578063095ea7b314610393578063135d088d146103b557806318160ddd146103ca575b600080fd5b34801561031057600080fd5b5061032461031f366004613119565b61099f565b60405190151581526020015b60405180910390f35b34801561034557600080fd5b5061034e610a0c565b6040516103309190613195565b34801561036757600080fd5b5061037b6103763660046131a8565b610a9a565b6040516001600160a01b039091168152602001610330565b34801561039f57600080fd5b506103b36103ae3660046131e1565b610b34565b005b3480156103c157600080fd5b5061034e610c4a565b3480156103d657600080fd5b506103e0600b5481565b604051908152602001610330565b3480156103fa57600080fd5b5060105461037b906001600160a01b031681565b34801561041a57600080fd5b506103b3610c57565b34801561042f57600080fd5b506103b361043e36600461320d565b610c93565b34801561044f57600080fd5b506103b361045e36600461324e565b610cc4565b34801561046f57600080fd5b506103b361047e3660046131a8565b610d99565b34801561048f57600080fd5b506104a361049e36600461326b565b610ec4565b604080516001600160a01b039093168352602083019190915201610330565b6103e06104d0366004613384565b610ef9565b3480156104e157600080fd5b5061034e610f97565b3480156104f657600080fd5b506103b361050536600461320d565b610fa6565b34801561051657600080fd5b5061052a6105253660046133d4565b610fc1565b604051610330919061347a565b34801561054357600080fd5b5060115461037b906001600160a01b031681565b34801561056357600080fd5b5061034e611097565b34801561057857600080fd5b506103b361058736600461324e565b6110a4565b34801561059857600080fd5b5061037b6105a73660046131a8565b6111b5565b3480156105b857600080fd5b506103e06105c736600461324e565b6111c0565b3480156105d857600080fd5b5060015461032490600160a01b900460ff1681565b3480156105f957600080fd5b5061037b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561062d57600080fd5b506103e061063c36600461324e565b6111f4565b34801561064d57600080fd5b506103b361127b565b34801561066257600080fd5b5061034e6112af565b34801561067757600080fd5b506103b36112f1565b34801561068c57600080fd5b5060135461037b906001600160a01b031681565b3480156106ac57600080fd5b5060005461037b906001600160a01b031681565b3480156106cc57600080fd5b506000546001600160a01b03163314610324565b3480156106ec57600080fd5b506103b36106fb3660046131a8565b6113a8565b34801561070c57600080fd5b5061034e61145f565b34801561072157600080fd5b506103b36107303660046134c7565b61146c565b34801561074157600080fd5b506103e0600e5481565b34801561075757600080fd5b506103b3610766366004613515565b611534565b34801561077757600080fd5b506103e0600f5481565b34801561078d57600080fd5b506103b36115f9565b3480156107a257600080fd5b506103b36107b136600461354e565b61162b565b3480156107c257600080fd5b506103e060125481565b3480156107d857600080fd5b5061037b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561080c57600080fd5b5061037b7f000000000000000000000000000000000000000000000000000000000000000081565b34801561084057600080fd5b506103b361163d565b34801561085557600080fd5b5061034e6108643660046131a8565b6116fa565b6103e0610877366004613384565b6118a3565b6103b361088a3660046135e9565b611934565b34801561089b57600080fd5b506010546108b190600160a01b900461ffff1681565b60405161ffff9091168152602001610330565b3480156108d057600080fd5b506103b36108df3660046131e1565b611abb565b3480156108f057600080fd5b5061034e611c1d565b34801561090557600080fd5b50610324610914366004613794565b611cbe565b34801561092557600080fd5b506001546001600160a01b03163314610324565b34801561094557600080fd5b506103b361095436600461324e565b611cec565b34801561096557600080fd5b5061098d7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610330565b60006001600160e01b031982166380ac58cd60e01b14806109d057506001600160e01b03198216635b5e139f60e01b145b806109eb57506001600160e01b031982166301ffc9a760e01b145b80610a0657506001600160e01b0319821663152a902d60e11b145b92915050565b60088054610a19906137c2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a45906137c2565b8015610a925780601f10610a6757610100808354040283529160200191610a92565b820191906000526020600020905b815481529060010190602001808311610a7557829003601f168201915b505050505081565b6000818152600460205260408120546001600160a01b0316610b185760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600660205260409020546001600160a01b031690565b6000610b3f82611d8e565b9050806001600160a01b0316836001600160a01b03161415610bad5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610b0f565b336001600160a01b0382161480610bc95750610bc98133611cbe565b610c3b5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610b0f565b610c458383611e05565b505050565b600d8054610a19906137c2565b6000546001600160a01b03163314610c815760405162461bcd60e51b8152600401610b0f906137f7565b600180546001600160a01b0319169055565b610c9d3382611e73565b610cb95760405162461bcd60e51b8152600401610b0f9061382e565b610c45838383611f4a565b6000546001600160a01b03163314610cee5760405162461bcd60e51b8152600401610b0f906137f7565b60105460405163f0091f4f60e01b81526001600160a01b03918216600482015282821660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063f0091f4f90604401600060405180830381600087803b158015610d5e57600080fd5b505af1158015610d72573d6000803e3d6000fd5b5050601080546001600160a01b0319166001600160a01b0394909416939093179092555050565b6000546001600160a01b03163314610dc35760405162461bcd60e51b8152600401610b0f906137f7565b600b548110158015610de05750600f541580610de05750600f5481105b610e375760405162461bcd60e51b815260206004820152602260248201527f6c696d6974206d757374206265203c207468616e2063757272656e74206c696d6044820152611a5d60f21b6064820152608401610b0f565b600f5460405163271c9e1f60e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063271c9e1f90604401600060405180830381600087803b158015610ea457600080fd5b505af1158015610eb8573d6000803e3d6000fd5b505050600f9190915550565b600080610ecf6120f5565b9150612710610edc612129565b610ee69085613895565b610ef091906138ca565b90509250929050565b600154600090600160a01b900460ff1615610f1657610f16612143565b60026003541415610f395760405162461bcd60e51b8152600401610b0f906138de565b6002600355600e543414610f815760405162461bcd60e51b815260206004820152600f60248201526e696e636f72726563742076616c756560881b6044820152606401610b0f565b610f8b8383612174565b60016003559392505050565b6060610fa161221e565b905090565b610c458383836040518060200160405280600081525061234a565b6060815167ffffffffffffffff811115610fdd57610fdd61328d565b604051908082528060200260200182016040528015611006578160200160208202803683370190505b50905060005b8251811015611091576004600084838151811061102b5761102b613915565b6020026020010151815260200190815260200160002060009054906101000a90046001600160a01b031682828151811061106757611067613915565b6001600160a01b0390921660209283029190910190910152806110898161392b565b91505061100c565b50919050565b600c8054610a19906137c2565b6000546001600160a01b031633146110ce5760405162461bcd60e51b8152600401610b0f906137f7565b6013546001600160a01b03161561111e5760405162461bcd60e51b81526020600482015260146024820152731c995b99195c995c88185b1c9958591e481cd95d60621b6044820152606401610b0f565b601380546001600160a01b0319166001600160a01b03838116918217909255604051631445778560e21b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000090911690635115de1490602401600060405180830381600087803b15801561119a57600080fd5b505af11580156111ae573d6000803e3d6000fd5b5050505050565b6000610a0682611d8e565b600080546001600160a01b031633146111eb5760405162461bcd60e51b8152600401610b0f906137f7565b610a068261237c565b60006001600160a01b03821661125f5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610b0f565b506001600160a01b031660009081526005602052604090205490565b6000546001600160a01b031633146112a55760405162461bcd60e51b8152600401610b0f906137f7565b6112ad6123ff565b565b60606112b961221e565b6112c246612449565b6112cb30612547565b6040516020016112dd93929190613962565b604051602081830303815290604052905090565b6001546001600160a01b031633146113605760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b6064820152608401610b0f565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b6000546001600160a01b031633146113d25760405162461bcd60e51b8152600401610b0f906137f7565b600e5460405163509d812560e11b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a13b024a90604401600060405180830381600087803b15801561143f57600080fd5b505af1158015611453573d6000803e3d6000fd5b505050600e9190915550565b60098054610a19906137c2565b6000546001600160a01b031633146114965760405162461bcd60e51b8152600401610b0f906137f7565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fc0ed66a6114cd61221e565b836040518363ffffffff1660e01b81526004016114eb9291906139b2565b600060405180830381600087803b15801561150557600080fd5b505af1158015611519573d6000803e3d6000fd5b505082516115309250600a91506020840190613067565b5050565b6001600160a01b03821633141561158d5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610b0f565b3360008181526007602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6000546001600160a01b031633146116235760405162461bcd60e51b8152600401610b0f906137f7565b6112ad6126b0565b6116378484848461234a565b50505050565b6000546001600160a01b031633146116675760405162461bcd60e51b8152600401610b0f906137f7565b600f54600b5460405163271c9e1f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263271c9e1f926116c092600401918252602082015260400190565b600060405180830381600087803b1580156116da57600080fd5b505af11580156116ee573d6000803e3d6000fd5b5050600b54600f555050565b6000818152600460205260409020546060906001600160a01b031661176d5760405162461bcd60e51b815260206004820152602360248201527f4552433732313a20717565727920666f72206e6f6e6578697374656e7420746f60448201526235b2b760e91b6064820152608401610b0f565b6013546001600160a01b0316156117ef5760135460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd90602401600060405180830381865afa1580156117c7573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610a0691908101906139e0565b6060600f5460001461182857611806600f54612449565b6040516020016118169190613a4e565b60405160208183030381529060405290505b6000611878600861183886612449565b846118416112af565b600c600d61184e8b612449565b6040516020016118649796959493929190613b10565b6040516020818303038152906040526126c7565b90508060405160200161188b9190613c5b565b60405160208183030381529060405292505050919050565b6000600260035414156118c85760405162461bcd60e51b8152600401610b0f906138de565b6002600355336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610f815760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b6044820152606401610b0f565b600260035414156119575760405162461bcd60e51b8152600401610b0f906138de565b6002600355336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146119ca5760405162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b6044820152606401610b0f565b835180516119e091600891602090910190613067565b5060208085015180516119f7926009920190613067565b5060608401518051611a1191600d91602090910190613067565b5060808401518051611a2b91600c91602090910190613067565b5060a0840151600e5560c0840151600f5560e0840151601080546001600160a01b039283166001600160a01b03199182161790915561010086015160138054919093169116179055610140840151611a829061281b565b611a8b85612923565b6001600160a01b03831615611aa657611aa48383612174565b505b611aaf8161296e565b50506001600355505050565b6000546001600160a01b03163314611ae55760405162461bcd60e51b8152600401610b0f906137f7565b612710811115611b485760405162461bcd60e51b815260206004820152602860248201527f627073206d757374206265206c657373207468616e206f7220657175616c207460448201526706f2031302c3030360c41b6064820152608401610b0f565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316630ba1ba83611b7f6120f5565b611b87612129565b6040516001600160e01b031960e085901b1681526001600160a01b0392831660048201526024810191909152908516604482015260648101849052608401600060405180830381600087803b158015611bdf57600080fd5b505af1158015611bf3573d6000803e3d6000fd5b5050601180546001600160a01b0319166001600160a01b0395909516949094179093555060125550565b6013546060906001600160a01b031615611cb657601360009054906101000a90046001600160a01b03166001600160a01b031663e8a3d4856040518163ffffffff1660e01b8152600401600060405180830381865afa925050508015611ca557506040513d6000823e601f3d908101601f19168201604052611ca291908101906139e0565b60015b611cb157610fa161298c565b919050565b610fa161298c565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b6000546001600160a01b03163314611d165760405162461bcd60e51b8152600401610b0f906137f7565b6001600160a01b038116611d6c5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e006044820152606401610b0f565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600460205260408120546001600160a01b031680610a065760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610b0f565b600081815260066020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611e3a82611d8e565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600460205260408120546001600160a01b0316611eec5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610b0f565b6000611ef783611d8e565b9050806001600160a01b0316846001600160a01b03161480611f325750836001600160a01b0316611f2784610a9a565b6001600160a01b0316145b80611f425750611f428185611cbe565b949350505050565b826001600160a01b0316611f5d82611d8e565b6001600160a01b031614611fc55760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610b0f565b6001600160a01b0382166120275760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610b0f565b612032838383612a07565b61203d600082611e05565b6001600160a01b0383166000908152600560205260408120805460019290612066908490613ca0565b90915550506001600160a01b0382166000908152600560205260408120805460019290612094908490613cb7565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b6011546000906001600160a01b03161561211957506011546001600160a01b031690565b506010546001600160a01b031690565b600060125460001461213c575060125490565b506103e890565b60005a60025490915060005a60025490915061215f8285613ca0565b5a61216a9084613ca0565b1061163757600080fd5b600061217f8361237c565b600e5460405163bbeeea3560e01b81529192506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163bbeeea35916121d591859188918890600401613ccf565b600060405180830381600087803b1580156121ef57600080fd5b505af1158015612203573d6000803e3d6000fd5b5050601054610a0692506001600160a01b0316905034612a96565b6060600a805461222d906137c2565b1590506122c457600a8054612241906137c2565b80601f016020809104026020016040519081016040528092919081815260200182805461226d906137c2565b80156122ba5780601f1061228f576101008083540402835291602001916122ba565b820191906000526020600020905b81548152906001019060200180831161229d57829003601f168201915b5050505050905090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633cb70c2d6040518163ffffffff1660e01b8152600401600060405180830381865afa158015612322573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610fa191908101906139e0565b6123543383611e73565b6123705760405162461bcd60e51b8152600401610b0f9061382e565b61163784848484612c16565b6000600b6000815461238d9061392b565b9182905550600f5490915015806123a75750600019600f54145b806123be5750600f546123bb906001613cb7565b81105b6123f55760405162461bcd60e51b81526020600482015260086024820152671cdbdb19081bdd5d60c21b6044820152606401610b0f565b611cb18282612c49565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60608161246d5750506040805180820190915260018152600360fc1b602082015290565b8160005b811561249757806124818161392b565b91506124909050600a836138ca565b9150612471565b60008167ffffffffffffffff8111156124b2576124b261328d565b6040519080825280601f01601f1916602001820160405280156124dc576020820181803683370190505b5090505b8415611f42576124f1600183613ca0565b91506124fe600a86613d06565b612509906030613cb7565b60f81b81838151811061251e5761251e613915565b60200101906001600160f81b031916908160001a905350612540600a866138ca565b94506124e0565b60408051602880825260608281019093526000919060208201818036833701905050905060005b6014811015612687576000612584826013613ca0565b61258f906008613895565b61259a906002613dfe565b6125ad906001600160a01b0387166138ca565b60f81b9050600060108260f81c6125c49190613e0a565b60f81b905060008160f81c60106125db9190613e2c565b8360f81c6125e99190613e4d565b60f81b90506125f782612d97565b85612603866002613895565b8151811061261357612613613915565b60200101906001600160f81b031916908160001a90535061263381612d97565b8561263f866002613895565b61264a906001613cb7565b8151811061265a5761265a613915565b60200101906001600160f81b031916908160001a905350505050808061267f9061392b565b91505061256e565b50806040516020016126999190613e70565b604051602081830303815290604052915050919050565b6001546112ad90600160a01b900460ff161561296e565b60608151600014156126e757505060408051602081019091526000815290565b600060405180606001604052806040815260200161411c60409139905060006003845160026127169190613cb7565b61272091906138ca565b61272b906004613895565b67ffffffffffffffff8111156127435761274361328d565b6040519080825280601f01601f19166020018201604052801561276d576020820181803683370190505b509050600182016020820185865187015b808210156127d9576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f811685015184535060018301925061277e565b50506003865106600181146127f5576002811461280857612810565b603d6001830353603d6002830353612810565b603d60018303535b509195945050505050565b6000612825612dcd565b6040516308c090cf60e11b815261ffff841660048201529091506001600160a01b03821690631181219e90602401602060405180830381865afa158015612870573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128949190613e9a565b61290157806001600160a01b0316631a7626e76040518163ffffffff1660e01b81526004016020604051808303816000875af11580156128d8573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128fc9190613eb7565b612903565b815b601060146101000a81548161ffff021916908361ffff1602179055505050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60018054911515600160a01b0260ff60a01b19909216919091179055565b606060006129df600861299d6112af565b600c600d6129b16129ac612129565b612449565b6129c16129bc6120f5565b612547565b6129c961221e565b6040516020016118649796959493929190613ed4565b9050806040516020016129f29190613c5b565b60405160208183030381529060405291505090565b60405163536a4ee560e11b81526001600160a01b0384811660048301528381166024830152604482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a6d49dca90606401600060405180830381600087803b158015612a7957600080fd5b505af1158015612a8d573d6000803e3d6000fd5b50505050505050565b6000612aa0612dcd565b90506001600160a01b03811615801590612b195750806001600160a01b03166367b7c0346040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612af5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b199190613e9a565b15612c0c576000612b3a83601060149054906101000a900461ffff16612e53565b905060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166361d027b36040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612bc29190614037565b90508115801590612bdb57506001600160a01b03811615155b15612c0257612bea8183612e89565b612bfd85612bf88487613ca0565b612e89565b6111ae565b6111ae8585612e89565b610c458383612e89565b612c21848484611f4a565b612c2d84848484612f69565b6116375760405162461bcd60e51b8152600401610b0f90614054565b6001600160a01b038216612c9f5760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610b0f565b6000818152600460205260409020546001600160a01b031615612d045760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610b0f565b612d1060008383612a07565b6001600160a01b0382166000908152600560205260408120805460019290612d39908490613cb7565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000600a60f883901c1015612dbe57612db560f883901c60306140a6565b60f81b92915050565b612db560f883901c60576140a6565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398c47e8c6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612e2f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fa19190614037565b60006127108310612e805761ffff8216612e6f612710856138ca565b612e799190613895565b9050610a06565b50600092915050565b80471015612ed05760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610b0f565b6000826001600160a01b0316825a6040519091906000818181858888f193505050503d8060008114612f1e576040519150601f19603f3d011682016040523d82523d6000602084013e612f23565b606091505b5050905080610c455760405162461bcd60e51b81526020600482015260126024820152711c9958da5c1a595b9d081c995d995c9d195960721b6044820152606401610b0f565b60006001600160a01b0384163b1561305c57604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612fad9033908990889088906004016140cb565b6020604051808303816000875af1925050508015612fe8575060408051601f3d908101601f19168201909252612fe5918101906140fe565b60015b613042573d808015613016576040519150601f19603f3d011682016040523d82523d6000602084013e61301b565b606091505b50805161303a5760405162461bcd60e51b8152600401610b0f90614054565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611f42565b506001949350505050565b828054613073906137c2565b90600052602060002090601f01602090048101928261309557600085556130db565b82601f106130ae57805160ff19168380011785556130db565b828001600101855582156130db579182015b828111156130db5782518255916020019190600101906130c0565b506130e79291506130eb565b5090565b5b808211156130e757600081556001016130ec565b6001600160e01b03198116811461311657600080fd5b50565b60006020828403121561312b57600080fd5b813561313681613100565b9392505050565b60005b83811015613158578181015183820152602001613140565b838111156116375750506000910152565b6000815180845261318181602086016020860161313d565b601f01601f19169290920160200192915050565b6020815260006131366020830184613169565b6000602082840312156131ba57600080fd5b5035919050565b6001600160a01b038116811461311657600080fd5b8035611cb1816131c1565b600080604083850312156131f457600080fd5b82356131ff816131c1565b946020939093013593505050565b60008060006060848603121561322257600080fd5b833561322d816131c1565b9250602084013561323d816131c1565b929592945050506040919091013590565b60006020828403121561326057600080fd5b8135613136816131c1565b6000806040838503121561327e57600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff811182821017156132c7576132c761328d565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156132f6576132f661328d565b604052919050565b600067ffffffffffffffff8211156133185761331861328d565b50601f01601f191660200190565b6000613339613334846132fe565b6132cd565b905082815283838301111561334d57600080fd5b828260208301376000602084830101529392505050565b600082601f83011261337557600080fd5b61313683833560208501613326565b6000806040838503121561339757600080fd5b82356133a2816131c1565b9150602083013567ffffffffffffffff8111156133be57600080fd5b6133ca85828601613364565b9150509250929050565b600060208083850312156133e757600080fd5b823567ffffffffffffffff808211156133ff57600080fd5b818501915085601f83011261341357600080fd5b8135818111156134255761342561328d565b8060051b91506134368483016132cd565b818152918301840191848101908884111561345057600080fd5b938501935b8385101561346e57843582529385019390850190613455565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156134bb5783516001600160a01b031683529284019291840191600101613496565b50909695505050505050565b6000602082840312156134d957600080fd5b813567ffffffffffffffff8111156134f057600080fd5b611f4284828501613364565b801515811461311657600080fd5b8035611cb1816134fc565b6000806040838503121561352857600080fd5b8235613533816131c1565b91506020830135613543816134fc565b809150509250929050565b6000806000806080858703121561356457600080fd5b843561356f816131c1565b9350602085013561357f816131c1565b925060408501359150606085013567ffffffffffffffff8111156135a257600080fd5b8501601f810187136135b357600080fd5b6135c287823560208401613326565b91505092959194509250565b61ffff8116811461311657600080fd5b8035611cb1816135ce565b600080600080600060a0868803121561360157600080fd5b853561360c816131c1565b9450602086013567ffffffffffffffff8082111561362957600080fd5b90870190610160828a03121561363e57600080fd5b6136466132a3565b82358281111561365557600080fd5b6136618b828601613364565b82525060208301358281111561367657600080fd5b6136828b828601613364565b60208301525060408301358281111561369a57600080fd5b6136a68b828601613364565b6040830152506060830135828111156136be57600080fd5b6136ca8b828601613364565b6060830152506080830135828111156136e257600080fd5b6136ee8b828601613364565b60808301525060a083013560a082015260c083013560c082015261371460e084016131d6565b60e08201526101006137278185016131d6565b9082015261012083810135908201526101406137448185016135de565b908201529550613756604089016131d6565b9450606088013591508082111561376c57600080fd5b5061377988828901613364565b9250506137886080870161350a565b90509295509295909350565b600080604083850312156137a757600080fd5b82356137b2816131c1565b91506020830135613543816131c1565b600181811c908216806137d657607f821691505b6020821081141561109157634e487b7160e01b600052602260045260246000fd5b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b60008160001904831182151516156138af576138af61387f565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826138d9576138d96138b4565b500490565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b600052603260045260246000fd5b600060001982141561393f5761393f61387f565b5060010190565b6000815161395881856020860161313d565b9290920192915050565b6000845161397481846020890161313d565b84519083019061398881836020890161313d565b602f60f81b910190815283516139a581600184016020880161313d565b0160010195945050505050565b6040815260006139c56040830185613169565b82810360208401526139d78185613169565b95945050505050565b6000602082840312156139f257600080fd5b815167ffffffffffffffff811115613a0957600080fd5b8201601f81018413613a1a57600080fd5b8051613a28613334826132fe565b818152856020838501011115613a3d57600080fd5b6139d782602083016020860161313d565b602f60f81b815260008251613a6a81600185016020870161313d565b9190910160010192915050565b8054600090600181811c9080831680613a9157607f831692505b6020808410821415613ab357634e487b7160e01b600052602260045260246000fd5b818015613ac75760018114613ad857613b04565b60ff19861689528489019650613b04565b876000528160002060005b86811015613afc5781548b820152908501908301613ae3565b505084890196505b50505050505092915050565b693d913730b6b2911d101160b11b81526000613b2f600a83018a613a77565b600160fd1b81528851613b49816001840160208d0161313d565b8851910190613b5f816001840160208c0161313d565b72111610113232b9b1b934b83a34b7b7111d101160691b600192909101918201528651613b93816014840160208b0161313d565b73222c2022636f6e74656e74223a202261723a2f2f60601b60149290910191820152613bc26028820187613a77565b73222c2022696d616765223a2022697066733a2f2f60601b81529050613beb6014820186613a77565b7f222c202261747472696275746573223a5b7b202274726169745f74797065223a8152730101129b2b934b0b6111610113b30b63ab2911d160651b60208201529050613c4d613c3d6034830186613946565b637d5d207d60e01b815260040190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251613c9381601d85016020870161313d565b91909101601d0192915050565b600082821015613cb257613cb261387f565b500390565b60008219821115613cca57613cca61387f565b500190565b84815260018060a01b0384166020820152826040820152608060608201526000613cfc6080830184613169565b9695505050505050565b600082613d1557613d156138b4565b500690565b600181815b80851115613d55578160001904821115613d3b57613d3b61387f565b80851615613d4857918102915b93841c9390800290613d1f565b509250929050565b600082613d6c57506001610a06565b81613d7957506000610a06565b8160018114613d8f5760028114613d9957613db5565b6001915050610a06565b60ff841115613daa57613daa61387f565b50506001821b610a06565b5060208310610133831016604e8410600b8410161715613dd8575081810a610a06565b613de28383613d1a565b8060001904821115613df657613df661387f565b029392505050565b60006131368383613d5d565b600060ff831680613e1d57613e1d6138b4565b8060ff84160491505092915050565b600060ff821660ff84168160ff0481118215151615613df657613df661387f565b600060ff821660ff841680821015613e6757613e6761387f565b90039392505050565b61060f60f31b815260008251613e8d81600285016020870161313d565b9190910160020192915050565b600060208284031215613eac57600080fd5b8151613136816134fc565b600060208284031215613ec957600080fd5b8151613136816135ce565b693d913730b6b2911d101160b11b81526000613ef3600a83018a613a77565b72111610113232b9b1b934b83a34b7b7111d101160691b81528851613f1f816013840160208d0161313d565b73222c2022636f6e74656e74223a202261723a2f2f60601b60139290910191820152613f4e6027820189613a77565b73222c2022696d616765223a2022697066733a2f2f60601b81529050613f776014820188613a77565b90507f222c202273656c6c65725f6665655f62617369735f706f696e7473223a20000081528551613faf81601e840160208a0161313d565b731610113332b2afb932b1b4b834b2b73a111d101160611b601e92909101918201528451613fe481603284016020890161313d565b61402861401a614014603284860101741116101132bc3a32b93730b62fb634b735911d101160591b815260150190565b87613946565b61227d60f01b815260020190565b9b9a5050505050505050505050565b60006020828403121561404957600080fd5b8151613136816131c1565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600060ff821660ff84168060ff038211156140c3576140c361387f565b019392505050565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090613cfc90830184613169565b60006020828403121561411057600080fd5b81516131368161310056fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220d7e7496affe5f37b45f47bf801871236b411daebbe75a78465fc49260da82e4a64736f6c634300080c0033000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde600000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x60806040526004361061019c5760003560e01c8063715018a6116100ec578063ad768cb31161008a578063e6d283a611610064578063e6d283a6146104f4578063ed459df214610524578063f2fde38b14610544578063ffa1ad741461056457600080fd5b8063ad768cb31461048d578063bee0ec0b146104a0578063d784d426146104d457600080fd5b80638f32d59b116100c65780638f32d59b1461041a578063a56600631461043a578063a61788f31461045a578063ad3c6b611461047a57600080fd5b8063715018a6146103d057806379ba5097146103e55780638da5cb5b146103fa57600080fd5b80633644e515116101595780635c60da1b116101335780635c60da1b1461033d57806364d44b1e146103625780636f19d0b614610382578063705bf3681461039c57600080fd5b80633644e5151461029d5780633cb70c2d146102d15780635a5df2b71461031d57600080fd5b806311ba1741146101a15780631552a369146101e85780631a861d261461021857806323452b9c1461022e57806327ea6f2b14610245578063360d0fad14610265575b600080fd5b3480156101ad57600080fd5b506101d57f682b3920f0bf70eb759a59b34e1c6ff903edb5ced39efd66303d66cc0eac774e81565b6040519081526020015b60405180910390f35b3480156101f457600080fd5b506102086102033660046119e7565b6105aa565b60405190151581526020016101df565b34801561022457600080fd5b506101d560055481565b34801561023a57600080fd5b506102436105c5565b005b34801561025157600080fd5b50610243610260366004611a37565b61060a565b34801561027157600080fd5b50610285610280366004611a50565b6106c1565b6040516001600160a01b0390911681526020016101df565b3480156102a957600080fd5b506101d57faf7e1a4374d6ed205cea6893db2342a239d64b04ad26a4a7987f77492397780981565b3480156102dd57600080fd5b506103106040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b6040516101df9190611ad8565b34801561032957600080fd5b50610285610338366004611cfd565b6106d7565b34801561034957600080fd5b506003546102859061010090046001600160a01b031681565b34801561036e57600080fd5b5061024361037d366004611d32565b6106f5565b34801561038e57600080fd5b506003546102089060ff1681565b3480156103a857600080fd5b506102857f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f81565b3480156103dc57600080fd5b506102436107ae565b3480156103f157600080fd5b506102436107e2565b34801561040657600080fd5b50600054610285906001600160a01b031681565b34801561042657600080fd5b506000546001600160a01b03163314610208565b34801561044657600080fd5b50610243610455366004611d5b565b610899565b34801561046657600080fd5b506101d5610475366004611d94565b610abe565b610285610488366004611de4565b610aca565b6101d561049b366004611e93565b610d53565b3480156104ac57600080fd5b506102857f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde681565b3480156104e057600080fd5b506102436104ef366004611ef5565b610dcf565b34801561050057600080fd5b5061020861050f366004611a37565b60046020526000908152604090205460ff1681565b34801561053057600080fd5b506001546001600160a01b03163314610208565b34801561055057600080fd5b5061024361055f366004611ef5565b610eae565b34801561057057600080fd5b506105987f000000000000000000000000000000000000000000000000000000000000000181565b60405160ff90911681526020016101df565b60006105b98686868686610f50565b90505b95945050505050565b6000546001600160a01b031633146105f85760405162461bcd60e51b81526004016105ef90611f12565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b031633146106345760405162461bcd60e51b81526004016105ef90611f12565b600554604051639ea75f2760e01b81526004810191909152602481018290527f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f6001600160a01b031690639ea75f2790604401600060405180830381600087803b1580156106a157600080fd5b505af11580156106b5573d6000803e3d6000fd5b50505060059190915550565b60006106ce838330611099565b90505b92915050565b60006106d133836000604051806020016040528060008152506110f6565b6000546001600160a01b0316331461071f5760405162461bcd60e51b81526004016105ef90611f12565b6040516302f848e760e11b815281151560048201527f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f6001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561078257600080fd5b505af1158015610796573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146107d85760405162461bcd60e51b81526004016105ef90611f12565b6107e0611412565b565b6001546001600160a01b031633146108515760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016105ef565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156108d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108fb9190611f49565b6001600160a01b0316336001600160a01b03161461094a5760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016105ef565b600061095461145c565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f8216916332c7791691869185169063a575185690602401602060405180830381865afa1580156109c9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109ed9190611f49565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b158015610a3e57600080fd5b505af1158015610a52573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b158015610aa157600080fd5b505af1158015610ab5573d6000803e3d6000fd5b50505050505050565b60006106ce83836114e7565b6000600280541415610b1e5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105ef565b6002805560a08701513414610b675760405162461bcd60e51b815260206004820152600f60248201526e696e636f72726563742076616c756560881b60448201526064016105ef565b6000610b7389896114e7565b60008181526004602052604090205490915060ff1615610cb357610be6600360019054906101000a90046001600160a01b03168a8a600001518b602001518c6101200151604051602001610bca9493929190611f66565b6040516020818303038152906040528051906020012030611099565b91506000826001600160a01b03163b11610c3a5760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016105ef565b6040516333f0ff8b60e21b81526001600160a01b0383169063cfc3fe2c903490610c6a9088908890600401611fae565b60206040518083038185885af1158015610c88573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610cad9190611fd2565b50610d42565b6000818152600460205260409020805460ff19166001179055610cd98982898989610f50565b610d335760405162461bcd60e51b815260206004820152602560248201527f696e76616c6964206f7220756e61626c6520746f20766572696679207369676e604482015264617475726560d81b60648201526084016105ef565b610d3f898986866110f6565b91505b506001600255979650505050505050565b6000836001600160a01b031663cfc3fe2c3485856040518463ffffffff1660e01b8152600401610d84929190611fae565b60206040518083038185885af1158015610da2573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610dc79190611fd2565b949350505050565b6000546001600160a01b03163314610df95760405162461bcd60e51b81526004016105ef90611f12565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f16906322fd9ed790604401600060405180830381600087803b158015610e6d57600080fd5b505af1158015610e81573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610ed85760405162461bcd60e51b81526004016105ef90611f12565b6001600160a01b038116610f2e5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016105ef565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60006001600160a01b038616610f9a5760405162461bcd60e51b815260206004820152600f60248201526e63616e6e6f742076616c696461746560881b60448201526064016105ef565b6001600160a01b0386163b1561107057604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b03881690631626ba7e9061100d9089908590606501611feb565b602060405180830381865afa925050508015611046575060408051601f3d908101601f1916820190925261104391810190612004565b60015b6110545760009150506105bc565b6001600160e01b031916630b135d3f60e11b1491506105bc9050565b600061107e8686868661163f565b6001600160a01b039081169088161491505095945050505050565b60405172602d8060093d393df3363d3d373d3d3d363d7360681b8152606093841b60138201526f5af43d82803e903d91602b57fd5bf3ff60801b6027820152921b6037830152604b8201526036808220606b830152605591012090565b60e08301516000906001600160a01b031661114c5760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016105ef565b6005541580611170575060008460c0015111801561117057506005548460c0015111155b6111ac5760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016105ef565b60035484516020808701516101208801516040516111fe9561010090046001600160a01b0316946111e3948c949193919201611f66565b60405160208183030381529060405280519060200120611667565b9050600061120a61145c565b90506001600160a01b038116156113165760e0850151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f909116906332c7791690606401600060405180830381600087803b15801561129457600080fd5b505af11580156112a8573d6000803e3d6000fd5b5050505060e085015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156112fd57600080fd5b505af1158015611311573d6000803e3d6000fd5b505050505b60035460405163dbdc37f360e01b81526001600160a01b0384169163dbdc37f3913491611353918b918b918b918b9160ff9091169060040161202e565b6000604051808303818588803b15801561136c57600080fd5b505af1158015611380573d6000803e3d6000fd5b50506040516306e9fe9960e41b81526001600160a01b038a8116600483015286811660248301527f0000000000000000000000004c2393aae4f0ad55dfd4ddcfa192f817d1b28d1f169350636e9fe99092506044019050600060405180830381600087803b1580156113f157600080fd5b505af1158015611405573d6000803e3d6000fd5b5050505050949350505050565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde66001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156114be573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114e29190611f49565b905090565b60006106ce7faf7e1a4374d6ed205cea6893db2342a239d64b04ad26a4a7987f7749239778097f682b3920f0bf70eb759a59b34e1c6ff903edb5ced39efd66303d66cc0eac774e858560000151866020015187606001518860800151604051602001611556949392919061217d565b604051602081830303815290604052805190602001208660c001518760a001518860e001518961010001518a61012001518b61014001516040516020016115f3999897969594939291909889526001600160a01b0397881660208a0152604089019690965260608801949094526080870192909252841660a086015290921660c084015260e083019190915261ffff166101008201526101200190565b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b600080600061165087878787611706565b9150915061165d816117f3565b5095945050505050565b600060405172602d8060093d393df3363d3d373d3d3d363d7360681b81528360601b60138201526e5af43d82803e903d91602b57fd5bf360881b6027820152826036826000f59150506001600160a01b0381166106d15760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016105ef565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561173d57506000905060036117ea565b8460ff16601b1415801561175557508460ff16601c14155b1561176657506000905060046117ea565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156117ba573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166117e3576000600192509250506117ea565b9150600090505b94509492505050565b6000816004811115611807576118076121d4565b14156118105750565b6001816004811115611824576118246121d4565b14156118725760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016105ef565b6002816004811115611886576118866121d4565b14156118d45760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016105ef565b60038160048111156118e8576118e86121d4565b14156119415760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016105ef565b6004816004811115611955576119556121d4565b14156119ae5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016105ef565b50565b6001600160a01b03811681146119ae57600080fd5b80356119d1816119b1565b919050565b803560ff811681146119d157600080fd5b600080600080600060a086880312156119ff57600080fd5b8535611a0a816119b1565b945060208601359350611a1f604087016119d6565b94979396509394606081013594506080013592915050565b600060208284031215611a4957600080fd5b5035919050565b60008060408385031215611a6357600080fd5b8235611a6e816119b1565b946020939093013593505050565b60005b83811015611a97578181015183820152602001611a7f565b83811115611aa6576000848401525b50505050565b60008151808452611ac4816020860160208601611a7c565b601f01601f19169290920160200192915050565b6020815260006106ce6020830184611aac565b634e487b7160e01b600052604160045260246000fd5b604051610160810167ffffffffffffffff81118282101715611b2557611b25611aeb565b60405290565b600082601f830112611b3c57600080fd5b813567ffffffffffffffff80821115611b5757611b57611aeb565b604051601f8301601f19908116603f01168101908282118183101715611b7f57611b7f611aeb565b81604052838152866020858801011115611b9857600080fd5b836020870160208301376000602085830101528094505050505092915050565b803561ffff811681146119d157600080fd5b60006101608284031215611bdd57600080fd5b611be5611b01565b9050813567ffffffffffffffff80821115611bff57600080fd5b611c0b85838601611b2b565b83526020840135915080821115611c2157600080fd5b611c2d85838601611b2b565b60208401526040840135915080821115611c4657600080fd5b611c5285838601611b2b565b60408401526060840135915080821115611c6b57600080fd5b611c7785838601611b2b565b60608401526080840135915080821115611c9057600080fd5b50611c9d84828501611b2b565b60808301525060a082013560a082015260c082013560c0820152611cc360e083016119c6565b60e0820152610100611cd68184016119c6565b908201526101208281013590820152610140611cf3818401611bb8565b9082015292915050565b600060208284031215611d0f57600080fd5b813567ffffffffffffffff811115611d2657600080fd5b610dc784828501611bca565b600060208284031215611d4457600080fd5b81358015158114611d5457600080fd5b9392505050565b60008060408385031215611d6e57600080fd5b8235611d79816119b1565b91506020830135611d89816119b1565b809150509250929050565b60008060408385031215611da757600080fd5b8235611db2816119b1565b9150602083013567ffffffffffffffff811115611dce57600080fd5b611dda85828601611bca565b9150509250929050565b600080600080600080600060e0888a031215611dff57600080fd5b8735611e0a816119b1565b9650602088013567ffffffffffffffff80821115611e2757600080fd5b611e338b838c01611bca565b9750611e4160408b016119d6565b965060608a0135955060808a0135945060a08a01359150611e61826119b1565b90925060c08901359080821115611e7757600080fd5b50611e848a828b01611b2b565b91505092959891949750929550565b600080600060608486031215611ea857600080fd5b8335611eb3816119b1565b92506020840135611ec3816119b1565b9150604084013567ffffffffffffffff811115611edf57600080fd5b611eeb86828701611b2b565b9150509250925092565b600060208284031215611f0757600080fd5b8135611d54816119b1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b600060208284031215611f5b57600080fd5b8151611d54816119b1565b6001600160a01b0385168152608060208201819052600090611f8a90830186611aac565b8281036040840152611f9c8186611aac565b91505082606083015295945050505050565b6001600160a01b0383168152604060208201819052600090610dc790830184611aac565b600060208284031215611fe457600080fd5b5051919050565b828152604060208201526000610dc76040830184611aac565b60006020828403121561201657600080fd5b81516001600160e01b031981168114611d5457600080fd5b60018060a01b038616815260a06020820152600085516101608060a085015261205b610200850183611aac565b91506020880151609f19808685030160c08701526120798483611aac565b935060408a01519150808685030160e08701526120968483611aac565b935060608a015191506101008187860301818801526120b58584611aac565b945060808b015192506101208288870301818901526120d48685611aac565b955060a08c015193506101409250838389015260c08c01518589015260e08c0151945061210d6101808901866001600160a01b03169052565b908b01516001600160a01b03166101a08801528a01516101c087015289015161ffff81166101e087015291506121409050565b506001600160a01b038616604084015282810360608401526121628186611aac565b915050612173608083018415159052565b9695505050505050565b6000855161218f818460208a01611a7c565b8551908301906121a3818360208a01611a7c565b85519101906121b6818360208901611a7c565b84519101906121c9818360208801611a7c565b019695505050505050565b634e487b7160e01b600052602160045260246000fdfea264697066735822122039472762c33600bb9c364030aaf7e8f014d849edcb745dac6fcc79fe26f1adbf64736f6c634300080c0033

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

000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde600000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000001

-----Decoded View---------------
Arg [0] : _owner (address): 0xae6FdDA940155D090E3A3DE9b090aD6A392F761a
Arg [1] : _treasuryConfiguration (address): 0x004D8438f4016A96F2D6dDc17808F4e40b47cdE6
Arg [2] : _maxLimit (uint256): 10000
Arg [3] : _guardOn (bool): True

-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a
Arg [1] : 000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde6
Arg [2] : 0000000000000000000000000000000000000000000000000000000000002710
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000001


Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.