ETH Price: $2,934.90 (-0.81%)
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To
Create With Sign...1409038982025-09-09 8:29:33137 days ago1757406573IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000000677380.00010087
Create With Sign...1398040712025-08-14 21:28:39163 days ago1755206919IN
0x2d4b7Ec9...f8eD96a0A
0.00269 ETH0.0000001105120.00010423
Create With Sign...1398040632025-08-14 21:28:23163 days ago1755206903IN
0x2d4b7Ec9...f8eD96a0A
0.00169 ETH0.0000001029590.00010424
Create With Sign...1378641462025-06-30 23:44:29208 days ago1751327069IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000000981380.00010062
Create With Sign...1377539002025-06-28 10:29:37210 days ago1751106577IN
0x2d4b7Ec9...f8eD96a0A
0.00169 ETH0.0000000768980.00010068
Create With Sign...1364668092025-05-29 15:26:35240 days ago1748532395IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000003351650.00010051
Create With Sign...1360355882025-05-19 15:52:33250 days ago1747669953IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000001842450.00012007
Create With Sign...1354683722025-05-06 12:45:21263 days ago1746535521IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000009576350.00010352
Create With Sign...1354683542025-05-06 12:44:45263 days ago1746535485IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000016472160.00010353
Create With Sign...1354683202025-05-06 12:43:37263 days ago1746535417IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000018931440.00010354
Create With Sign...1354683012025-05-06 12:42:59263 days ago1746535379IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000023017840.00010355
Create With Sign...1354682772025-05-06 12:42:11263 days ago1746535331IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000027274860.0001036
Create With Sign...1354682582025-05-06 12:41:33263 days ago1746535293IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.000002714830.00010362
Create With Sign...1354682282025-05-06 12:40:33263 days ago1746535233IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000026454620.00010371
Create With Sign...1354682072025-05-06 12:39:51263 days ago1746535191IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000027308080.00010376
Create With Sign...1354681862025-05-06 12:39:09263 days ago1746535149IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000029050660.00010381
Create With Sign...1335179942025-03-22 9:12:45308 days ago1742634765IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000001907760.0002466
Create With Sign...1335179912025-03-22 9:12:39308 days ago1742634759IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000002106960.0002475
Create With Sign...1335179872025-03-22 9:12:31308 days ago1742634751IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000002046160.0002473
Create With Sign...1329525542025-03-09 7:04:45322 days ago1741503885IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000000856990.00011292
Create With Sign...1320485192025-02-16 8:50:15342 days ago1739695815IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000007193790.00100444
Create With Sign...1315101542025-02-03 21:44:45355 days ago1738619085IN
0x2d4b7Ec9...f8eD96a0A
0.00169 ETH0.000000838170.0010019
Create With Sign...1312239892025-01-28 6:45:55362 days ago1738046755IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000004226710.00010052
Create With Sign...1312239732025-01-28 6:45:23362 days ago1738046723IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000003892580.00010052
Create With Sign...1312239302025-01-28 6:43:57362 days ago1738046637IN
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH0.0000003910310.00010052
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block From To
1409038982025-09-09 8:29:33137 days ago1757406573
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1409038982025-09-09 8:29:33137 days ago1757406573
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1398040712025-08-14 21:28:39163 days ago1755206919
0x2d4b7Ec9...f8eD96a0A
0.00269 ETH
1398040712025-08-14 21:28:39163 days ago1755206919
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1398040632025-08-14 21:28:23163 days ago1755206903
0x2d4b7Ec9...f8eD96a0A
0.00169 ETH
1398040632025-08-14 21:28:23163 days ago1755206903
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1378641462025-06-30 23:44:29208 days ago1751327069
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1378641462025-06-30 23:44:29208 days ago1751327069
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1377539002025-06-28 10:29:37210 days ago1751106577
0x2d4b7Ec9...f8eD96a0A
0.00169 ETH
1377539002025-06-28 10:29:37210 days ago1751106577
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1364668092025-05-29 15:26:35240 days ago1748532395
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1364668092025-05-29 15:26:35240 days ago1748532395
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1360355882025-05-19 15:52:33250 days ago1747669953
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1360355882025-05-19 15:52:33250 days ago1747669953
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354683722025-05-06 12:45:21263 days ago1746535521
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1354683722025-05-06 12:45:21263 days ago1746535521
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354683542025-05-06 12:44:45263 days ago1746535485
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1354683542025-05-06 12:44:45263 days ago1746535485
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354683202025-05-06 12:43:37263 days ago1746535417
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1354683202025-05-06 12:43:37263 days ago1746535417
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354683012025-05-06 12:42:59263 days ago1746535379
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1354683012025-05-06 12:42:59263 days ago1746535379
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354682772025-05-06 12:42:11263 days ago1746535331
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
1354682772025-05-06 12:42:11263 days ago1746535331
0x2d4b7Ec9...f8eD96a0A
 Contract Creation0 ETH
1354682582025-05-06 12:41:33263 days ago1746535293
0x2d4b7Ec9...f8eD96a0A
0.00069 ETH
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
WritingEditionsFactory

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
// 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 = 2;

    /// @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)"
        );

    /// @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 _o11y Observability contract.
    /// @param _maxLimit Max edition limit.
    /// @param _guardOn Guard status.
    constructor(
        address _owner,
        address _treasuryConfiguration,
        address _o11y,
        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)
            )
        );

        // Set implementation contract.
        implementation = address(
            new WritingEditions(address(this), _treasuryConfiguration, _o11y)
        );

        // Set observability contract.
        o11y = _o11y;

        // slither-disable-next-line reentrancy-no-eth
        IObservability(_o11y).emitFactoryImplementationSet(
            // oldImplementation
            address(0),
            // newImplementation
            implementation
        );

        // 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) {
        // 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).purchase{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);
    }

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

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

// 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 = 200;

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

    /// > [[[[[[[[[[[ 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 owner.
        _setInitialOwner(_owner);

        // Store guard status.
        _setGuard(_guardOn);

        // Mint initial token to recipient
        if (tokenRecipient != address(0)) {
            _purchase(tokenRecipient, message);
        }
    }

    /// @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) {
        return _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 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": "',
                        _escapeQuotes(name),
                        " ",
                        Strings.toString(tokenId),
                        editionNumber,
                        '", "description": "',
                        _escapeQuotes(description()),
                        '", "content": "ar://',
                        contentURI,
                        '", "image": "ipfs://',
                        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": "',
                        _escapeQuotes(name),
                        '", "description": "',
                        _escapeQuotes(description()),
                        '", "content": "ar://',
                        contentURI,
                        '", "image": "ipfs://',
                        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 _purchase(
        address tokenRecipient,
        string memory message
    ) internal returns (uint256 tokenId) {
        // Mint token, and get a tokenId.
        tokenId = _getTokenIdAndMint(tokenRecipient);

        // Get fee.
        uint256 flatFeeAmount = 0;
        address feeConfiguration = _getFeeConfiguration();
        if (
            feeConfiguration != address(0) &&
            IFeeConfiguration(feeConfiguration).flatFeeOn()
        ) {
            // Calculate the fee on the current balance
            flatFeeAmount = IFeeConfiguration(feeConfiguration).flatFeeAmount();
        }

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

        _withdraw(fundingRecipient, msg.value, flatFeeAmount);
    }

    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,
        uint256 flatFeeAmount
    ) internal {
        // 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 (flatFeeAmount != 0 && treasury != address(0)) {
            require(amount == price + flatFeeAmount, "invalid amount");

            _sendEther(payable(treasury), flatFeeAmount);

            // Transfer the remaining amount to the recipient.
            _sendEther(payable(fundsRecipient), price);
        } else {
            require(amount == price, "invalid amount");

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

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

    function _escapeQuotes(
        string memory str
    ) internal pure returns (string memory) {
        bytes memory strBytes = bytes(str);
        uint8 quotesCount = 0;
        for (uint8 i = 0; i < strBytes.length; i++) {
            if (strBytes[i] == '"') {
                quotesCount++;
            }
        }
        if (quotesCount > 0) {
            bytes memory escapedBytes = new bytes(
                strBytes.length + (quotesCount)
            );
            uint256 index;
            for (uint8 i = 0; i < strBytes.length; i++) {
                if (strBytes[i] == '"') {
                    escapedBytes[index++] = "\\";
                }
                escapedBytes[index++] = strBytes[i];
            }
            return string(escapedBytes);
        }
        return str;
    }
}

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

// 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,
        uint256 flatFeeAmount
    ) external override {
        emit WritingEditionPurchased(
            msg.sender,
            tokenId,
            recipient,
            price,
            message,
            flatFeeAmount
        );
    }

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create(0, ptr, 0x37)
        }
        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) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
            instance := create2(0, ptr, 0x37, 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) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
            mstore(add(ptr, 0x14), shl(0x60, implementation))
            mstore(add(ptr, 0x28), 0x5af43d82803e903d91602b57fd5bf3ff00000000000000000000000000000000)
            mstore(add(ptr, 0x38), shl(0x60, deployer))
            mstore(add(ptr, 0x4c), salt)
            mstore(add(ptr, 0x6c), keccak256(ptr, 0x37))
            predicted := keccak256(add(ptr, 0x37), 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));
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.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));
    }
}

// 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() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // 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;
    }

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

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

    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 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 royaltyRecipient_,
        uint256 royaltyPercentage_
    ) external;

    function toggleGuard() external;

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

// 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,
        uint256 flatFeeAmount
    );

    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,
        uint256 flatFeeAmount
    ) 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;
}

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;

interface IFeeConfigurationEvents {
    event FeeSwitch(bool on);

    event FlatFeeSwitch(bool on);

    event MinimumFee(uint16 fee);

    event MaximumFee(uint16 fee);

    event FlatFeeAmount(uint256 fee);
}

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

    function flatFeeOn() external returns (bool);

    function flatFeeAmount() external returns (uint256);

    function maximumFee() external returns (uint16);

    function minimumFee() external returns (uint16);

    function switchFee() external;

    function setMinimumFee(uint16 newFee) external;

    function setMaximumFee(uint16 newFe) external;

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return result;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (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/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_treasuryConfiguration","type":"address"},{"internalType":"address","name":"_o11y","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"},{"indexed":false,"internalType":"uint256","name":"flatFeeAmount","type":"uint256"}],"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":"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":"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":"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":[],"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"}]

61010060405260026080523480156200001757600080fd5b506040516200687e3803806200687e8339810160408190526200003a91620002b4565b8462000046816200023e565b5060016002556001600160a01b038416620000a75760405162461bcd60e51b815260206004820152601860248201527f6d7573742073657420747265617375727920636f6e6669670000000000000000604482015260640160405180910390fd5b6001600160a01b03841660c05260408051808201825260018152603160f81b60209182015281517f2aef22f9d7df5f9d21c56d14029233f3fdaa91917727e1eb68e504d27072d6cd818301527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6818401524660608201523060808281018290528451808403909101815260a0909201938490528151919092012060e0529085908590620001549062000289565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103906000f08015801562000191573d6000803e3d6000fd5b5060038054610100600160a81b0319166101006001600160a01b039384168102919091179182905585831660a08190526040516322fd9ed760e01b815260006004820152919092049092166024830152906322fd9ed790604401600060405180830381600087803b1580156200020657600080fd5b505af11580156200021b573d6000803e3d6000fd5b50506003805460ff19169315159390931790925550506005555062000323915050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b61446c806200241283390190565b80516001600160a01b0381168114620002af57600080fd5b919050565b600080600080600060a08688031215620002cd57600080fd5b620002d88662000297565b9450620002e86020870162000297565b9350620002f86040870162000297565b925060608601519150608086015180151581146200031557600080fd5b809150509295509295909350565b60805160a05160c05160e05161207d62000395600039600081816102c701526112df01526000818161048401526114c80152600081816103a60152818161064b0152818161090701528181610b5301528181610cf501528181610f7401526110cf01526000610548015261207d6000f3fe6080604052600436106101815760003560e01c8063705bf368116100d1578063a56600631161008a578063e6d283a611610064578063e6d283a6146104c6578063ed459df2146104f6578063f2fde38b14610516578063ffa1ad741461053657600080fd5b8063a566006314610452578063bee0ec0b14610472578063d784d426146104a657600080fd5b8063705bf36814610394578063715018a6146103c857806379ba5097146103dd5780638da5cb5b146103f25780638f32d59b14610412578063949da7921461043257600080fd5b806330b844541161013e5780633cb70c2d116101185780633cb70c2d146102e95780635c60da1b1461033557806364d44b1e1461035a5780636f19d0b61461037a57600080fd5b806330b8445414610282578063360d0fad146102955780633644e515146102b557600080fd5b8063089dc13b1461018657806311ba1741146101c35780631552a369146102055780631a861d261461023557806323452b9c1461024b57806327ea6f2b14610262575b600080fd5b34801561019257600080fd5b506101a66101a1366004611ad2565b61057c565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101cf57600080fd5b506101f77f49679996e28ae3d719dbc33eac28d7aebde3d77d1283ed79b042f8ad99e1fecc81565b6040519081526020016101ba565b34801561021157600080fd5b50610225610220366004611b20565b6105a0565b60405190151581526020016101ba565b34801561024157600080fd5b506101f760055481565b34801561025757600080fd5b506102606105bb565b005b34801561026e57600080fd5b5061026061027d366004611b70565b610600565b6101a6610290366004611b89565b6106b7565b3480156102a157600080fd5b506101a66102b0366004611c38565b6108b2565b3480156102c157600080fd5b506101f77f000000000000000000000000000000000000000000000000000000000000000081565b3480156102f557600080fd5b506103286040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b6040516101ba9190611cb4565b34801561034157600080fd5b506003546101a69061010090046001600160a01b031681565b34801561036657600080fd5b50610260610375366004611cc7565b6108c6565b34801561038657600080fd5b506003546102259060ff1681565b3480156103a057600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b3480156103d457600080fd5b5061026061097f565b3480156103e957600080fd5b506102606109b3565b3480156103fe57600080fd5b506000546101a6906001600160a01b031681565b34801561041e57600080fd5b506000546001600160a01b03163314610225565b34801561043e57600080fd5b506101f761044d366004611ce9565b610a6a565b34801561045e57600080fd5b5061026061046d366004611d39565b610a76565b34801561047e57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b3480156104b257600080fd5b506102606104c1366004611d72565b610c9b565b3480156104d257600080fd5b506102256104e1366004611b70565b60046020526000908152604090205460ff1681565b34801561050257600080fd5b506001546001600160a01b03163314610225565b34801561052257600080fd5b50610260610531366004611d72565b610d7a565b34801561054257600080fd5b5061056a7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016101ba565b600061059a3383600060405180602001604052806000815250610e1c565b92915050565b60006105af8686868686611138565b90505b95945050505050565b6000546001600160a01b031633146105ee5760405162461bcd60e51b81526004016105e590611d8f565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461062a5760405162461bcd60e51b81526004016105e590611d8f565b600554604051639ea75f2760e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561069757600080fd5b505af11580156106ab573d6000803e3d6000fd5b50505060059190915550565b60006106c1611281565b60006106cd89896112d8565b60008181526004602052604090205490915060ff161561080d57610740600360019054906101000a90046001600160a01b03168a8a600001518b602001518c61012001516040516020016107249493929190611dc6565b604051602081830303815290604052805190602001203061141c565b91506000826001600160a01b03163b116107945760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016105e5565b604051633a1b1d5760e01b81526001600160a01b03831690633a1b1d579034906107c49088908890600401611e0e565b60206040518083038185885af11580156107e2573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906108079190611e32565b5061089c565b6000818152600460205260409020805460ff191660011790556108338982898989611138565b61088d5760405162461bcd60e51b815260206004820152602560248201527f696e76616c6964206f7220756e61626c6520746f20766572696679207369676e604482015264617475726560d81b60648201526084016105e5565b61089989898686610e1c565b91505b506108a76001600255565b979650505050505050565b60006108bf83833061141c565b9392505050565b6000546001600160a01b031633146108f05760405162461bcd60e51b81526004016105e590611d8f565b6040516302f848e760e11b815281151560048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561095357600080fd5b505af1158015610967573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146109a95760405162461bcd60e51b81526004016105e590611d8f565b6109b161147a565b565b6001546001600160a01b03163314610a225760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016105e5565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b60006108bf83836112d8565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad89190611e4b565b6001600160a01b0316336001600160a01b031614610b275760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016105e5565b6000610b316114c4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f00000000000000000000000000000000000000000000000000000000000000008216916332c7791691869185169063a575185690602401602060405180830381865afa158015610ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bca9190611e4b565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b158015610c1b57600080fd5b505af1158015610c2f573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b03163314610cc55760405162461bcd60e51b81526004016105e590611d8f565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906322fd9ed790604401600060405180830381600087803b158015610d3957600080fd5b505af1158015610d4d573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610da45760405162461bcd60e51b81526004016105e590611d8f565b6001600160a01b038116610dfa5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016105e5565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60e08301516000906001600160a01b0316610e725760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016105e5565b6005541580610e96575060008460c00151118015610e9657506005548460c0015111155b610ed25760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016105e5565b6003548451602080870151610120880151604051610f249561010090046001600160a01b031694610f09948c949193919201611dc6565b6040516020818303038152906040528051906020012061154f565b90506000610f306114c4565b90506001600160a01b0381161561103c5760e0850151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000000000000000000000000000000000000000000000909116906332c7791690606401600060405180830381600087803b158015610fba57600080fd5b505af1158015610fce573d6000803e3d6000fd5b5050505060e085015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b15801561102357600080fd5b505af1158015611037573d6000803e3d6000fd5b505050505b600354604051630d09de2d60e41b81526001600160a01b0384169163d09de2d0913491611079918b918b918b918b9160ff90911690600401611e68565b6000604051808303818588803b15801561109257600080fd5b505af11580156110a6573d6000803e3d6000fd5b50506040516306e9fe9960e41b81526001600160a01b038a8116600483015286811660248301527f0000000000000000000000000000000000000000000000000000000000000000169350636e9fe99092506044019050600060405180830381600087803b15801561111757600080fd5b505af115801561112b573d6000803e3d6000fd5b5050505050949350505050565b60006001600160a01b0386166111825760405162461bcd60e51b815260206004820152600f60248201526e63616e6e6f742076616c696461746560881b60448201526064016105e5565b6001600160a01b0386163b1561125857604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b03881690631626ba7e906111f59089908590606501611f97565b602060405180830381865afa92505050801561122e575060408051601f3d908101601f1916820190925261122b91810190611fb0565b60015b61123c5760009150506105b2565b6001600160e01b031916630b135d3f60e11b1491506105b29050565b6000611266868686866115ef565b6001600160a01b039081169088161491505095945050505050565b60028054036112d25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105e5565b60028055565b60006108bf7f00000000000000000000000000000000000000000000000000000000000000007f49679996e28ae3d719dbc33eac28d7aebde3d77d1283ed79b042f8ad99e1fecc8585600001518660200151876060015188608001516040516020016113479493929190611fda565b604051602081830303815290604052805190602001208660c001518760a001518860e001518961010001518a61012001516040516020016113d09897969594939291909788526001600160a01b039687166020890152604088019590955260608701939093526080860191909152831660a085015290911660c083015260e08201526101000190565b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611526573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154a9190611e4b565b905090565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b03811661059a5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016105e5565b600080600061160087878787611617565b9150915061160d81611704565b5095945050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561164e57506000905060036116fb565b8460ff16601b1415801561166657508460ff16601c14155b1561167757506000905060046116fb565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156116cb573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166116f4576000600192509250506116fb565b9150600090505b94509492505050565b600081600481111561171857611718612031565b036117205750565b600181600481111561173457611734612031565b036117815760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016105e5565b600281600481111561179557611795612031565b036117e25760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016105e5565b60038160048111156117f6576117f6612031565b0361184e5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016105e5565b600481600481111561186257611862612031565b036118ba5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016105e5565b50565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff811182821017156118f7576118f76118bd565b60405290565b600082601f83011261190e57600080fd5b813567ffffffffffffffff80821115611929576119296118bd565b604051601f8301601f19908116603f01168101908282118183101715611951576119516118bd565b8160405283815286602085880101111561196a57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146118ba57600080fd5b80356119aa8161198a565b919050565b600061014082840312156119c257600080fd5b6119ca6118d3565b9050813567ffffffffffffffff808211156119e457600080fd5b6119f0858386016118fd565b83526020840135915080821115611a0657600080fd5b611a12858386016118fd565b60208401526040840135915080821115611a2b57600080fd5b611a37858386016118fd565b60408401526060840135915080821115611a5057600080fd5b611a5c858386016118fd565b60608401526080840135915080821115611a7557600080fd5b50611a82848285016118fd565b60808301525060a082013560a082015260c082013560c0820152611aa860e0830161199f565b60e0820152610100611abb81840161199f565b818301525061012080830135818301525092915050565b600060208284031215611ae457600080fd5b813567ffffffffffffffff811115611afb57600080fd5b611b07848285016119af565b949350505050565b803560ff811681146119aa57600080fd5b600080600080600060a08688031215611b3857600080fd5b8535611b438161198a565b945060208601359350611b5860408701611b0f565b94979396509394606081013594506080013592915050565b600060208284031215611b8257600080fd5b5035919050565b600080600080600080600060e0888a031215611ba457600080fd5b8735611baf8161198a565b9650602088013567ffffffffffffffff80821115611bcc57600080fd5b611bd88b838c016119af565b9750611be660408b01611b0f565b965060608a0135955060808a0135945060a08a01359150611c068261198a565b90925060c08901359080821115611c1c57600080fd5b50611c298a828b016118fd565b91505092959891949750929550565b60008060408385031215611c4b57600080fd5b8235611c568161198a565b946020939093013593505050565b60005b83811015611c7f578181015183820152602001611c67565b50506000910152565b60008151808452611ca0816020860160208601611c64565b601f01601f19169290920160200192915050565b6020815260006108bf6020830184611c88565b600060208284031215611cd957600080fd5b813580151581146108bf57600080fd5b60008060408385031215611cfc57600080fd5b8235611d078161198a565b9150602083013567ffffffffffffffff811115611d2357600080fd5b611d2f858286016119af565b9150509250929050565b60008060408385031215611d4c57600080fd5b8235611d578161198a565b91506020830135611d678161198a565b809150509250929050565b600060208284031215611d8457600080fd5b81356108bf8161198a565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b6001600160a01b0385168152608060208201819052600090611dea90830186611c88565b8281036040840152611dfc8186611c88565b91505082606083015295945050505050565b6001600160a01b0383168152604060208201819052600090611b0790830184611c88565b600060208284031215611e4457600080fd5b5051919050565b600060208284031215611e5d57600080fd5b81516108bf8161198a565b60018060a01b038616815260a06020820152600085516101408060a0850152611e956101e0850183611c88565b91506020880151609f19808685030160c0870152611eb38483611c88565b935060408a01519150808685030160e0870152611ed08483611c88565b935060608a01519150610100818786030181880152611eef8584611c88565b945060808b01519250610120828887030181890152611f0e8685611c88565b955060a08c01518589015260c08c015161016089015260e08c01519450611f416101808901866001600160a01b03169052565b908b01516001600160a01b039081166101a0890152908b01516101c0880152891660408701525050508281036060840152611f7c8186611c88565b915050611f8d608083018415159052565b9695505050505050565b828152604060208201526000611b076040830184611c88565b600060208284031215611fc257600080fd5b81516001600160e01b0319811681146108bf57600080fd5b60008551611fec818460208a01611c64565b855190830190612000818360208a01611c64565b8551910190612013818360208901611c64565b8451910190612026818360208801611c64565b019695505050505050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220efb303927abd81e930f0222dd93758055b0b004795461d5cd2535e81f987f49064736f6c63430008110033610100604052600160025560c86080523480156200001c57600080fd5b506040516200446c3803806200446c8339810160408190526200003f91620001f4565b600160006200004e816200018c565b506001805460ff60a01b1916600160a01b831515021790555060016003556001600160a01b038316620000bb5760405162461bcd60e51b815260206004820152601060248201526f6d7573742073657420666163746f727960801b60448201526064015b60405180910390fd5b6001600160a01b0380841660a0528216620001195760405162461bcd60e51b815260206004820152601f60248201527f6d7573742073657420747265617375727920636f6e66696775726174696f6e006044820152606401620000b2565b6001600160a01b0380831660c0528116620001775760405162461bcd60e51b815260206004820152601660248201527f6d75737420736574206f62736572766162696c697479000000000000000000006044820152606401620000b2565b6001600160a01b031660e052506200023e9050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b80516001600160a01b0381168114620001ef57600080fd5b919050565b6000806000606084860312156200020a57600080fd5b6200021584620001d7565b92506200022560208501620001d7565b91506200023560408501620001d7565b90509250925092565b60805160a05160c05160e051614199620002d3600039600081816105c901528181610c9901528181610dd90152818161107201528181611311015281816113b60152818161159d01528181611a5b015281816121580152612abc0152600081816107a801528181612b210152612ba70152600081816107dc01528181611868015261228e015260006108ed01526141996000f3fe6080604052600436106102c95760003560e01c80637284e41611610175578063b88d4fde116100dc578063d09de2d011610095578063e985e9c51161006f578063e985e9c51461087b578063ed459df21461089b578063f2fde38b146108bb578063ffa1ad74146108db57600080fd5b8063d09de2d014610833578063e2e784d514610846578063e8a3d4851461086657600080fd5b8063b88d4fde14610760578063b9c9d93a14610780578063bee0ec0b14610796578063c45a0155146107ca578063c7381da3146107fe578063c87b56dd1461081357600080fd5b806395d89b411161012e57806395d89b41146106ca5780639c1fd6e0146106df578063a035b1fe146106ff578063a22cb46514610715578063a4d66daf14610735578063a8d13a291461074b57600080fd5b80637284e4161461062057806379ba5097146106355780638ada6b0f1461064a5780638da5cb5b1461066a5780638f32d59b1461068a57806391b7f5ed146106aa57600080fd5b80633a1b1d571161023457806356d3163d116101ed5780636f19d0b6116101c75780636f19d0b614610596578063705bf368146105b757806370a08231146105eb578063715018a61461060b57600080fd5b806356d3163d146105365780636352211e146105565780636a6278421461057657600080fd5b80633a1b1d571461048c5780633cb70c2d1461049f57806342842e0e146104b457806347591135146104d45780634c00de82146105015780634dcd14b21461052157600080fd5b80631bb534ba116102865780631bb534ba146103b857806323452b9c146103d857806323b872dd146103ed578063241d96511461040d57806327ea6f2b1461042d5780632a55205a1461044d57600080fd5b806301ffc9a7146102ce57806306fdde0314610303578063081812fc14610325578063095ea7b31461035d578063135d088d1461037f57806318160ddd14610394575b600080fd5b3480156102da57600080fd5b506102ee6102e93660046130ac565b610921565b60405190151581526020015b60405180910390f35b34801561030f57600080fd5b5061031861098e565b6040516102fa9190613120565b34801561033157600080fd5b50610345610340366004613133565b610a1c565b6040516001600160a01b0390911681526020016102fa565b34801561036957600080fd5b5061037d61037836600461316c565b610ab6565b005b34801561038b57600080fd5b50610318610bcb565b3480156103a057600080fd5b506103aa600b5481565b6040519081526020016102fa565b3480156103c457600080fd5b50601054610345906001600160a01b031681565b3480156103e457600080fd5b5061037d610bd8565b3480156103f957600080fd5b5061037d610408366004613198565b610c14565b34801561041957600080fd5b5061037d6104283660046131d9565b610c45565b34801561043957600080fd5b5061037d610448366004613133565b610d1a565b34801561045957600080fd5b5061046d6104683660046131f6565b610e45565b604080516001600160a01b0390931683526020830191909152016102fa565b6103aa61049a36600461330c565b610e7b565b3480156104ab57600080fd5b50610318610eb6565b3480156104c057600080fd5b5061037d6104cf366004613198565b610ec5565b3480156104e057600080fd5b506104f46104ef36600461335b565b610ee0565b6040516102fa9190613400565b34801561050d57600080fd5b50601154610345906001600160a01b031681565b34801561052d57600080fd5b50610318610fb5565b34801561054257600080fd5b5061037d6105513660046131d9565b610fc2565b34801561056257600080fd5b50610345610571366004613133565b6110d3565b34801561058257600080fd5b506103aa6105913660046131d9565b6110de565b3480156105a257600080fd5b506001546102ee90600160a01b900460ff1681565b3480156105c357600080fd5b506103457f000000000000000000000000000000000000000000000000000000000000000081565b3480156105f757600080fd5b506103aa6106063660046131d9565b611112565b34801561061757600080fd5b5061037d611199565b34801561062c57600080fd5b506103186111cd565b34801561064157600080fd5b5061037d61120f565b34801561065657600080fd5b50601354610345906001600160a01b031681565b34801561067657600080fd5b50600054610345906001600160a01b031681565b34801561069657600080fd5b506000546001600160a01b031633146102ee565b3480156106b657600080fd5b5061037d6106c5366004613133565b6112c6565b3480156106d657600080fd5b5061031861137d565b3480156106eb57600080fd5b5061037d6106fa36600461344d565b61138a565b34801561070b57600080fd5b506103aa600e5481565b34801561072157600080fd5b5061037d61073036600461349a565b61144e565b34801561074157600080fd5b506103aa600f5481565b34801561075757600080fd5b5061037d611512565b34801561076c57600080fd5b5061037d61077b3660046134d3565b611544565b34801561078c57600080fd5b506103aa60125481565b3480156107a257600080fd5b506103457f000000000000000000000000000000000000000000000000000000000000000081565b3480156107d657600080fd5b506103457f000000000000000000000000000000000000000000000000000000000000000081565b34801561080a57600080fd5b5061037d611556565b34801561081f57600080fd5b5061031861082e366004613133565b611613565b61037d610841366004613552565b611855565b34801561085257600080fd5b5061037d61086136600461316c565b611992565b34801561087257600080fd5b50610318611adf565b34801561088757600080fd5b506102ee6108963660046136ea565b611b80565b3480156108a757600080fd5b506001546001600160a01b031633146102ee565b3480156108c757600080fd5b5061037d6108d63660046131d9565b611bae565b3480156108e757600080fd5b5061090f7f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff90911681526020016102fa565b60006001600160e01b031982166380ac58cd60e01b148061095257506001600160e01b03198216635b5e139f60e01b145b8061096d57506001600160e01b031982166301ffc9a760e01b145b8061098857506001600160e01b0319821663152a902d60e11b145b92915050565b6008805461099b90613718565b80601f01602080910402602001604051908101604052809291908181526020018280546109c790613718565b8015610a145780601f106109e957610100808354040283529160200191610a14565b820191906000526020600020905b8154815290600101906020018083116109f757829003601f168201915b505050505081565b6000818152600460205260408120546001600160a01b0316610a9a5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600660205260409020546001600160a01b031690565b6000610ac182611c50565b9050806001600160a01b0316836001600160a01b031603610b2e5760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610a91565b336001600160a01b0382161480610b4a5750610b4a8133611b80565b610bbc5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610a91565b610bc68383611cc7565b505050565b600d805461099b90613718565b6000546001600160a01b03163314610c025760405162461bcd60e51b8152600401610a919061374c565b600180546001600160a01b0319169055565b610c1e3382611d35565b610c3a5760405162461bcd60e51b8152600401610a9190613783565b610bc6838383611e0c565b6000546001600160a01b03163314610c6f5760405162461bcd60e51b8152600401610a919061374c565b60105460405163f0091f4f60e01b81526001600160a01b03918216600482015282821660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063f0091f4f90604401600060405180830381600087803b158015610cdf57600080fd5b505af1158015610cf3573d6000803e3d6000fd5b5050601080546001600160a01b0319166001600160a01b0394909416939093179092555050565b6000546001600160a01b03163314610d445760405162461bcd60e51b8152600401610a919061374c565b600b548110158015610d615750600f541580610d615750600f5481105b610db85760405162461bcd60e51b815260206004820152602260248201527f6c696d6974206d757374206265203c207468616e2063757272656e74206c696d6044820152611a5d60f21b6064820152608401610a91565b600f5460405163271c9e1f60e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063271c9e1f90604401600060405180830381600087803b158015610e2557600080fd5b505af1158015610e39573d6000803e3d6000fd5b505050600f9190915550565b6011546012546001600160a01b039091169060009061271090610e6890856137ea565b610e729190613817565b90509250929050565b600154600090600160a01b900460ff1615610e9857610e98611fb7565b610ea0611fe8565b610eaa8383612041565b90506109886001600355565b6060610ec06121e6565b905090565b610bc683838360405180602001604052806000815250612312565b606081516001600160401b03811115610efb57610efb613218565b604051908082528060200260200182016040528015610f24578160200160208202803683370190505b50905060005b8251811015610faf5760046000848381518110610f4957610f4961382b565b6020026020010151815260200190815260200160002060009054906101000a90046001600160a01b0316828281518110610f8557610f8561382b565b6001600160a01b039092166020928302919091019091015280610fa781613841565b915050610f2a565b50919050565b600c805461099b90613718565b6000546001600160a01b03163314610fec5760405162461bcd60e51b8152600401610a919061374c565b6013546001600160a01b03161561103c5760405162461bcd60e51b81526020600482015260146024820152731c995b99195c995c88185b1c9958591e481cd95d60621b6044820152606401610a91565b601380546001600160a01b0319166001600160a01b03838116918217909255604051631445778560e21b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000090911690635115de1490602401600060405180830381600087803b1580156110b857600080fd5b505af11580156110cc573d6000803e3d6000fd5b5050505050565b600061098882611c50565b600080546001600160a01b031633146111095760405162461bcd60e51b8152600401610a919061374c565b61098882612344565b60006001600160a01b03821661117d5760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610a91565b506001600160a01b031660009081526005602052604090205490565b6000546001600160a01b031633146111c35760405162461bcd60e51b8152600401610a919061374c565b6111cb6123c7565b565b60606111d76121e6565b6111e046612411565b6111e930612511565b6040516020016111fb93929190613876565b604051602081830303815290604052905090565b6001546001600160a01b0316331461127e5760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b6064820152608401610a91565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b6000546001600160a01b031633146112f05760405162461bcd60e51b8152600401610a919061374c565b600e5460405163509d812560e11b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a13b024a90604401600060405180830381600087803b15801561135d57600080fd5b505af1158015611371573d6000803e3d6000fd5b505050600e9190915550565b6009805461099b90613718565b6000546001600160a01b031633146113b45760405162461bcd60e51b8152600401610a919061374c565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fc0ed66a6113eb6121e6565b836040518363ffffffff1660e01b81526004016114099291906138c6565b600060405180830381600087803b15801561142357600080fd5b505af1158015611437573d6000803e3d6000fd5b5050505080600a908161144a9190613942565b5050565b336001600160a01b038316036114a65760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610a91565b3360008181526007602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6000546001600160a01b0316331461153c5760405162461bcd60e51b8152600401610a919061374c565b6111cb61267a565b61155084848484612312565b50505050565b6000546001600160a01b031633146115805760405162461bcd60e51b8152600401610a919061374c565b600f54600b5460405163271c9e1f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263271c9e1f926115d992600401918252602082015260400190565b600060405180830381600087803b1580156115f357600080fd5b505af1158015611607573d6000803e3d6000fd5b5050600b54600f555050565b6000818152600460205260409020546060906001600160a01b03166116865760405162461bcd60e51b815260206004820152602360248201527f4552433732313a20717565727920666f72206e6f6e6578697374656e7420746f60448201526235b2b760e91b6064820152608401610a91565b6013546001600160a01b0316156117085760135460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd90602401600060405180830381865afa1580156116e0573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526109889190810190613a01565b6060600f546000146117415761171f600f54612411565b60405160200161172f9190613a6e565b60405160208183030381529060405290505b600061182a6117d96008805461175690613718565b80601f016020809104026020016040519081016040528092919081815260200182805461178290613718565b80156117cf5780601f106117a4576101008083540402835291602001916117cf565b820191906000526020600020905b8154815290600101906020018083116117b257829003601f168201915b5050505050612691565b6117e286612411565b846117f36117ee6111cd565b612691565b600c600d6118008b612411565b6040516020016118169796959493929190613b0a565b604051602081830303815290604052612847565b90508060405160200161183d9190613c66565b60405160208183030381529060405292505050919050565b61185d611fe8565b336001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016146118cb5760405162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b6044820152606401610a91565b83516008906118da9082613942565b5060208401516009906118ed9082613942565b506060840151600d906119009082613942565b506080840151600c906119139082613942565b5060a0840151600e5560c0840151600f5560e0840151601080546001600160a01b039283166001600160a01b0319918216179091556101008601516013805491909316911617905561196485612999565b61196d816129e4565b6001600160a01b03831615611988576119868383612041565b505b6110cc6001600355565b6000546001600160a01b031633146119bc5760405162461bcd60e51b8152600401610a919061374c565b612710811115611a1f5760405162461bcd60e51b815260206004820152602860248201527f627073206d757374206265206c657373207468616e206f7220657175616c207460448201526706f2031302c3030360c41b6064820152608401610a91565b601154601254604051630ba1ba8360e01b81526001600160a01b03928316600482015260248101919091528382166044820152606481018390527f000000000000000000000000000000000000000000000000000000000000000090911690630ba1ba8390608401600060405180830381600087803b158015611aa157600080fd5b505af1158015611ab5573d6000803e3d6000fd5b5050601180546001600160a01b0319166001600160a01b0395909516949094179093555060125550565b6013546060906001600160a01b031615611b7857601360009054906101000a90046001600160a01b03166001600160a01b031663e8a3d4856040518163ffffffff1660e01b8152600401600060405180830381865afa925050508015611b6757506040513d6000823e601f3d908101601f19168201604052611b649190810190613a01565b60015b611b7357610ec0612a02565b919050565b610ec0612a02565b6001600160a01b03918216600090815260076020908152604080832093909416825291909152205460ff1690565b6000546001600160a01b03163314611bd85760405162461bcd60e51b8152600401610a919061374c565b6001600160a01b038116611c2e5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e006044820152606401610a91565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000818152600460205260408120546001600160a01b0316806109885760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610a91565b600081815260066020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611cfc82611c50565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600460205260408120546001600160a01b0316611dae5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610a91565b6000611db983611c50565b9050806001600160a01b0316846001600160a01b03161480611df45750836001600160a01b0316611de984610a1c565b6001600160a01b0316145b80611e045750611e048185611b80565b949350505050565b826001600160a01b0316611e1f82611c50565b6001600160a01b031614611e875760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610a91565b6001600160a01b038216611ee95760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610a91565b611ef4838383612a8e565b611eff600082611cc7565b6001600160a01b0383166000908152600560205260408120805460019290611f28908490613cab565b90915550506001600160a01b0382166000908152600560205260408120805460019290611f56908490613cbe565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b60005a60025490915060005a600254909150611fd38285613cab565b5a611fde9084613cab565b1061155057600080fd5b60026003540361203a5760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610a91565b6002600355565b600061204c83612344565b9050600080612059612b1d565b90506001600160a01b038116158015906120d25750806001600160a01b031663da8b5a566040518163ffffffff1660e01b81526004016020604051808303816000875af11580156120ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906120d29190613cd1565b1561213e57806001600160a01b031663ff0c44da6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612117573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061213b9190613cee565b91505b600e5460405163ca9d19eb60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ca9d19eb916121949187918a91908a908990600401613d07565b600060405180830381600087803b1580156121ae57600080fd5b505af11580156121c2573d6000803e3d6000fd5b50506010546121de92506001600160a01b031690503484612ba3565b505092915050565b6060600a80546121f590613718565b15905061228c57600a805461220990613718565b80601f016020809104026020016040519081016040528092919081815260200182805461223590613718565b80156122825780601f1061225757610100808354040283529160200191612282565b820191906000526020600020905b81548152906001019060200180831161226557829003601f168201915b5050505050905090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633cb70c2d6040518163ffffffff1660e01b8152600401600060405180830381865afa1580156122ea573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610ec09190810190613a01565b61231c3383611d35565b6123385760405162461bcd60e51b8152600401610a9190613783565b61155084848484612cfb565b6000600b6000815461235590613841565b9182905550600f54909150158061236f5750600019600f54145b806123865750600f54612383906001613cbe565b81105b6123bd5760405162461bcd60e51b81526020600482015260086024820152671cdbdb19081bdd5d60c21b6044820152606401610a91565b611b738282612d2e565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b6060816000036124385750506040805180820190915260018152600360fc1b602082015290565b8160005b8115612462578061244c81613841565b915061245b9050600a83613817565b915061243c565b6000816001600160401b0381111561247c5761247c613218565b6040519080825280601f01601f1916602001820160405280156124a6576020820181803683370190505b5090505b8415611e04576124bb600183613cab565b91506124c8600a86613d46565b6124d3906030613cbe565b60f81b8183815181106124e8576124e861382b565b60200101906001600160f81b031916908160001a90535061250a600a86613817565b94506124aa565b60408051602880825260608281019093526000919060208201818036833701905050905060005b601481101561265157600061254e826013613cab565b6125599060086137ea565b612564906002613e3e565b612577906001600160a01b038716613817565b60f81b9050600060108260f81c61258e9190613e4a565b60f81b905060008160f81c60106125a59190613e6c565b8360f81c6125b39190613e8f565b60f81b90506125c182612e7c565b856125cd8660026137ea565b815181106125dd576125dd61382b565b60200101906001600160f81b031916908160001a9053506125fd81612e7c565b856126098660026137ea565b612614906001613cbe565b815181106126245761262461382b565b60200101906001600160f81b031916908160001a905350505050808061264990613841565b915050612538565b50806040516020016126639190613ea8565b604051602081830303815290604052915050919050565b6001546111cb90600160a01b900460ff16156129e4565b6060816000805b82518160ff1610156126f257828160ff16815181106126b9576126b961382b565b01602001516001600160f81b031916601160f91b036126e057816126dc81613ed2565b9250505b806126ea81613ed2565b915050612698565b5060ff81161561283f5760008160ff16835161270e9190613cbe565b6001600160401b0381111561272557612725613218565b6040519080825280601f01601f19166020018201604052801561274f576020820181803683370190505b5090506000805b84518160ff16101561283457848160ff16815181106127775761277761382b565b01602001516001600160f81b031916601160f91b036127ca57601760fa1b83836127a081613841565b9450815181106127b2576127b261382b565b60200101906001600160f81b031916908160001a9053505b848160ff16815181106127df576127df61382b565b01602001516001600160f81b03191683836127f981613841565b94508151811061280b5761280b61382b565b60200101906001600160f81b031916908160001a9053508061282c81613ed2565b915050612756565b509095945050505050565b509192915050565b6060815160000361286657505060408051602081019091526000815290565b600060405180606001604052806040815260200161412460409139905060006003845160026128959190613cbe565b61289f9190613817565b6128aa9060046137ea565b6001600160401b038111156128c1576128c1613218565b6040519080825280601f01601f1916602001820160405280156128eb576020820181803683370190505b509050600182016020820185865187015b80821015612957576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f81168501518453506001830192506128fc565b505060038651066001811461297357600281146129865761298e565b603d6001830353603d600283035361298e565b603d60018303535b509195945050505050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60018054911515600160a01b0260ff60a01b19909216919091179055565b60606000612a66612a196008805461175690613718565b612a246117ee6111cd565b600c600d612a33601254612411565b601154612a48906001600160a01b0316612511565b612a506121e6565b6040516020016118169796959493929190613ef1565b905080604051602001612a799190613c66565b60405160208183030381529060405291505090565b60405163536a4ee560e11b81526001600160a01b0384811660048301528381166024830152604482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a6d49dca90606401600060405180830381600087803b158015612b0057600080fd5b505af1158015612b14573d6000803e3d6000fd5b50505050505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398c47e8c6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612b7f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec09190614041565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166361d027b36040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612c05573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c299190614041565b90508115801590612c4257506001600160a01b03811615155b15612caf5781600e54612c559190613cbe565b8314612c945760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610a91565b612c9e8183612eb2565b612caa84600e54612eb2565b611550565b600e548314612cf15760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610a91565b6115508484612eb2565b612d06848484611e0c565b612d1284848484612f92565b6115505760405162461bcd60e51b8152600401610a919061405e565b6001600160a01b038216612d845760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610a91565b6000818152600460205260409020546001600160a01b031615612de95760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610a91565b612df560008383612a8e565b6001600160a01b0382166000908152600560205260408120805460019290612e1e908490613cbe565b909155505060008181526004602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b6000600a60f883901c1015612ea357612e9a60f883901c60306140b0565b60f81b92915050565b612e9a60f883901c60576140b0565b80471015612ef95760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610a91565b6000826001600160a01b0316825a6040519091906000818181858888f193505050503d8060008114612f47576040519150601f19603f3d011682016040523d82523d6000602084013e612f4c565b606091505b5050905080610bc65760405162461bcd60e51b81526020600482015260126024820152711c9958da5c1a595b9d081c995d995c9d195960721b6044820152606401610a91565b60006001600160a01b0384163b1561308857604051630a85bd0160e11b81526001600160a01b0385169063150b7a0290612fd69033908990889088906004016140c9565b6020604051808303816000875af1925050508015613011575060408051601f3d908101601f1916820190925261300e91810190614106565b60015b61306e573d80801561303f576040519150601f19603f3d011682016040523d82523d6000602084013e613044565b606091505b5080516000036130665760405162461bcd60e51b8152600401610a919061405e565b805181602001fd5b6001600160e01b031916630a85bd0160e11b149050611e04565b506001949350505050565b6001600160e01b0319811681146130a957600080fd5b50565b6000602082840312156130be57600080fd5b81356130c981613093565b9392505050565b60005b838110156130eb5781810151838201526020016130d3565b50506000910152565b6000815180845261310c8160208601602086016130d0565b601f01601f19169290920160200192915050565b6020815260006130c960208301846130f4565b60006020828403121561314557600080fd5b5035919050565b6001600160a01b03811681146130a957600080fd5b8035611b738161314c565b6000806040838503121561317f57600080fd5b823561318a8161314c565b946020939093013593505050565b6000806000606084860312156131ad57600080fd5b83356131b88161314c565b925060208401356131c88161314c565b929592945050506040919091013590565b6000602082840312156131eb57600080fd5b81356130c98161314c565b6000806040838503121561320957600080fd5b50508035926020909101359150565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b038111828210171561325157613251613218565b60405290565b604051601f8201601f191681016001600160401b038111828210171561327f5761327f613218565b604052919050565b60006001600160401b038211156132a0576132a0613218565b50601f01601f191660200190565b60006132c16132bc84613287565b613257565b90508281528383830111156132d557600080fd5b828260208301376000602084830101529392505050565b600082601f8301126132fd57600080fd5b6130c9838335602085016132ae565b6000806040838503121561331f57600080fd5b823561332a8161314c565b915060208301356001600160401b0381111561334557600080fd5b613351858286016132ec565b9150509250929050565b6000602080838503121561336e57600080fd5b82356001600160401b038082111561338557600080fd5b818501915085601f83011261339957600080fd5b8135818111156133ab576133ab613218565b8060051b91506133bc848301613257565b81815291830184019184810190888411156133d657600080fd5b938501935b838510156133f4578435825293850193908501906133db565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156134415783516001600160a01b03168352928401929184019160010161341c565b50909695505050505050565b60006020828403121561345f57600080fd5b81356001600160401b0381111561347557600080fd5b611e04848285016132ec565b80151581146130a957600080fd5b8035611b7381613481565b600080604083850312156134ad57600080fd5b82356134b88161314c565b915060208301356134c881613481565b809150509250929050565b600080600080608085870312156134e957600080fd5b84356134f48161314c565b935060208501356135048161314c565b92506040850135915060608501356001600160401b0381111561352657600080fd5b8501601f8101871361353757600080fd5b613546878235602084016132ae565b91505092959194509250565b600080600080600060a0868803121561356a57600080fd5b85356135758161314c565b945060208601356001600160401b038082111561359157600080fd5b90870190610140828a0312156135a657600080fd5b6135ae61322e565b8235828111156135bd57600080fd5b6135c98b8286016132ec565b8252506020830135828111156135de57600080fd5b6135ea8b8286016132ec565b60208301525060408301358281111561360257600080fd5b61360e8b8286016132ec565b60408301525060608301358281111561362657600080fd5b6136328b8286016132ec565b60608301525060808301358281111561364a57600080fd5b6136568b8286016132ec565b60808301525060a083013560a082015260c083013560c082015261367c60e08401613161565b60e082015261010061368f818501613161565b90820152610120838101359082015295506136ac60408901613161565b945060608801359150808211156136c257600080fd5b506136cf888289016132ec565b9250506136de6080870161348f565b90509295509295909350565b600080604083850312156136fd57600080fd5b82356137088161314c565b915060208301356134c88161314c565b600181811c9082168061372c57607f821691505b602082108103610faf57634e487b7160e01b600052602260045260246000fd5b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610988576109886137d4565b634e487b7160e01b600052601260045260246000fd5b60008261382657613826613801565b500490565b634e487b7160e01b600052603260045260246000fd5b600060018201613853576138536137d4565b5060010190565b6000815161386c8185602086016130d0565b9290920192915050565b600084516138888184602089016130d0565b84519083019061389c8183602089016130d0565b602f60f81b910190815283516138b98160018401602088016130d0565b0160010195945050505050565b6040815260006138d960408301856130f4565b82810360208401526138eb81856130f4565b95945050505050565b601f821115610bc657600081815260208120601f850160051c8101602086101561391b5750805b601f850160051c820191505b8181101561393a57828155600101613927565b505050505050565b81516001600160401b0381111561395b5761395b613218565b61396f816139698454613718565b846138f4565b602080601f8311600181146139a4576000841561398c5750858301515b600019600386901b1c1916600185901b17855561393a565b600085815260208120601f198616915b828110156139d3578886015182559484019460019091019084016139b4565b50858210156139f15787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208284031215613a1357600080fd5b81516001600160401b03811115613a2957600080fd5b8201601f81018413613a3a57600080fd5b8051613a486132bc82613287565b818152856020838501011115613a5d57600080fd5b6138eb8260208301602086016130d0565b602f60f81b815260008251613a8a8160018501602087016130d0565b9190910160010192915050565b60008154613aa481613718565b60018281168015613abc5760018114613ad157613b00565b60ff1984168752821515830287019450613b00565b8560005260208060002060005b85811015613af75781548a820152908401908201613ade565b50505082870194505b5050505092915050565b693d913730b6b2911d101160b11b81528751600090613b3081600a850160208d016130d0565b600160fd1b600a918401918201528851613b5181600b840160208d016130d0565b8851910190613b6781600b840160208c016130d0565b72111610113232b9b1b934b83a34b7b7111d101160691b600b92909101918201528651613b9b81601e840160208b016130d0565b73222c2022636f6e74656e74223a202261723a2f2f60601b601e9290910191820152613bca6032820187613a97565b73222c2022696d616765223a2022697066733a2f2f60601b81529050613c58613c48613c42613bfc6014850189613a97565b7f222c202261747472696275746573223a5b7b202274726169745f74797065223a8152730101129b2b934b0b6111610113b30b63ab2911d160651b602082015260340190565b8661385a565b637d5d207d60e01b815260040190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251613c9e81601d8501602087016130d0565b91909101601d0192915050565b81810381811115610988576109886137d4565b80820180821115610988576109886137d4565b600060208284031215613ce357600080fd5b81516130c981613481565b600060208284031215613d0057600080fd5b5051919050565b85815260018060a01b038516602082015283604082015260a060608201526000613d3460a08301856130f4565b90508260808301529695505050505050565b600082613d5557613d55613801565b500690565b600181815b80851115613d95578160001904821115613d7b57613d7b6137d4565b80851615613d8857918102915b93841c9390800290613d5f565b509250929050565b600082613dac57506001610988565b81613db957506000610988565b8160018114613dcf5760028114613dd957613df5565b6001915050610988565b60ff841115613dea57613dea6137d4565b50506001821b610988565b5060208310610133831016604e8410600b8410161715613e18575081810a610988565b613e228383613d5a565b8060001904821115613e3657613e366137d4565b029392505050565b60006130c98383613d9d565b600060ff831680613e5d57613e5d613801565b8060ff84160491505092915050565b60ff8181168382160290811690818114613e8857613e886137d4565b5092915050565b60ff8281168282160390811115610988576109886137d4565b61060f60f31b815260008251613ec58160028501602087016130d0565b9190910160020192915050565b600060ff821660ff8103613ee857613ee86137d4565b60010192915050565b693d913730b6b2911d101160b11b81528751600090613f1781600a850160208d016130d0565b72111610113232b9b1b934b83a34b7b7111d101160691b600a918401918201528851613f4a81601d840160208d016130d0565b73222c2022636f6e74656e74223a202261723a2f2f60601b601d9290910191820152613f796031820189613a97565b73222c2022696d616765223a2022697066733a2f2f60601b81529050613fa26014820188613a97565b90507f222c202273656c6c65725f6665655f62617369735f706f696e7473223a20000081528551613fda81601e840160208a016130d0565b731610113332b2afb932b1b4b834b2b73a111d101160611b601e9290910191820152613c58614033613c42614012603285018961385a565b741116101132bc3a32b93730b62fb634b735911d101160591b815260150190565b61227d60f01b815260020190565b60006020828403121561405357600080fd5b81516130c98161314c565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60ff8181168382160190811115610988576109886137d4565b6001600160a01b03858116825284166020820152604081018390526080606082018190526000906140fc908301846130f4565b9695505050505050565b60006020828403121561411857600080fd5b81516130c98161309356fe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa26469706673582212200174dd34f8330e1cf22a1dcd8e36c6f82d8d20ff757e59313be324cd95b2432b64736f6c63430008110033000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000001

Deployed Bytecode

0x6080604052600436106101815760003560e01c8063705bf368116100d1578063a56600631161008a578063e6d283a611610064578063e6d283a6146104c6578063ed459df2146104f6578063f2fde38b14610516578063ffa1ad741461053657600080fd5b8063a566006314610452578063bee0ec0b14610472578063d784d426146104a657600080fd5b8063705bf36814610394578063715018a6146103c857806379ba5097146103dd5780638da5cb5b146103f25780638f32d59b14610412578063949da7921461043257600080fd5b806330b844541161013e5780633cb70c2d116101185780633cb70c2d146102e95780635c60da1b1461033557806364d44b1e1461035a5780636f19d0b61461037a57600080fd5b806330b8445414610282578063360d0fad146102955780633644e515146102b557600080fd5b8063089dc13b1461018657806311ba1741146101c35780631552a369146102055780631a861d261461023557806323452b9c1461024b57806327ea6f2b14610262575b600080fd5b34801561019257600080fd5b506101a66101a1366004611ad2565b61057c565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156101cf57600080fd5b506101f77f49679996e28ae3d719dbc33eac28d7aebde3d77d1283ed79b042f8ad99e1fecc81565b6040519081526020016101ba565b34801561021157600080fd5b50610225610220366004611b20565b6105a0565b60405190151581526020016101ba565b34801561024157600080fd5b506101f760055481565b34801561025757600080fd5b506102606105bb565b005b34801561026e57600080fd5b5061026061027d366004611b70565b610600565b6101a6610290366004611b89565b6106b7565b3480156102a157600080fd5b506101a66102b0366004611c38565b6108b2565b3480156102c157600080fd5b506101f77ffc8812d16d81e68c24359b3fd51d5169ab9eebe0cf94bdcea5b04b02d58358a981565b3480156102f557600080fd5b506103286040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b6040516101ba9190611cb4565b34801561034157600080fd5b506003546101a69061010090046001600160a01b031681565b34801561036657600080fd5b50610260610375366004611cc7565b6108c6565b34801561038657600080fd5b506003546102259060ff1681565b3480156103a057600080fd5b506101a67f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07281565b3480156103d457600080fd5b5061026061097f565b3480156103e957600080fd5b506102606109b3565b3480156103fe57600080fd5b506000546101a6906001600160a01b031681565b34801561041e57600080fd5b506000546001600160a01b03163314610225565b34801561043e57600080fd5b506101f761044d366004611ce9565b610a6a565b34801561045e57600080fd5b5061026061046d366004611d39565b610a76565b34801561047e57600080fd5b506101a67f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde681565b3480156104b257600080fd5b506102606104c1366004611d72565b610c9b565b3480156104d257600080fd5b506102256104e1366004611b70565b60046020526000908152604090205460ff1681565b34801561050257600080fd5b506001546001600160a01b03163314610225565b34801561052257600080fd5b50610260610531366004611d72565b610d7a565b34801561054257600080fd5b5061056a7f000000000000000000000000000000000000000000000000000000000000000281565b60405160ff90911681526020016101ba565b600061059a3383600060405180602001604052806000815250610e1c565b92915050565b60006105af8686868686611138565b90505b95945050505050565b6000546001600160a01b031633146105ee5760405162461bcd60e51b81526004016105e590611d8f565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461062a5760405162461bcd60e51b81526004016105e590611d8f565b600554604051639ea75f2760e01b81526004810191909152602481018290527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561069757600080fd5b505af11580156106ab573d6000803e3d6000fd5b50505060059190915550565b60006106c1611281565b60006106cd89896112d8565b60008181526004602052604090205490915060ff161561080d57610740600360019054906101000a90046001600160a01b03168a8a600001518b602001518c61012001516040516020016107249493929190611dc6565b604051602081830303815290604052805190602001203061141c565b91506000826001600160a01b03163b116107945760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016105e5565b604051633a1b1d5760e01b81526001600160a01b03831690633a1b1d579034906107c49088908890600401611e0e565b60206040518083038185885af11580156107e2573d6000803e3d6000fd5b50505050506040513d601f19601f820116820180604052508101906108079190611e32565b5061089c565b6000818152600460205260409020805460ff191660011790556108338982898989611138565b61088d5760405162461bcd60e51b815260206004820152602560248201527f696e76616c6964206f7220756e61626c6520746f20766572696679207369676e604482015264617475726560d81b60648201526084016105e5565b61089989898686610e1c565b91505b506108a76001600255565b979650505050505050565b60006108bf83833061141c565b9392505050565b6000546001600160a01b031633146108f05760405162461bcd60e51b81526004016105e590611d8f565b6040516302f848e760e11b815281151560048201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561095357600080fd5b505af1158015610967573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146109a95760405162461bcd60e51b81526004016105e590611d8f565b6109b161147a565b565b6001546001600160a01b03163314610a225760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016105e5565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b60006108bf83836112d8565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ad89190611e4b565b6001600160a01b0316336001600160a01b031614610b275760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016105e5565b6000610b316114c4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0728216916332c7791691869185169063a575185690602401602060405180830381865afa158015610ba6573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610bca9190611e4b565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b158015610c1b57600080fd5b505af1158015610c2f573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b158015610c7e57600080fd5b505af1158015610c92573d6000803e3d6000fd5b50505050505050565b6000546001600160a01b03163314610cc55760405162461bcd60e51b81526004016105e590611d8f565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07216906322fd9ed790604401600060405180830381600087803b158015610d3957600080fd5b505af1158015610d4d573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610da45760405162461bcd60e51b81526004016105e590611d8f565b6001600160a01b038116610dfa5760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016105e5565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b60e08301516000906001600160a01b0316610e725760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016105e5565b6005541580610e96575060008460c00151118015610e9657506005548460c0015111155b610ed25760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016105e5565b6003548451602080870151610120880151604051610f249561010090046001600160a01b031694610f09948c949193919201611dc6565b6040516020818303038152906040528051906020012061154f565b90506000610f306114c4565b90506001600160a01b0381161561103c5760e0850151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072909116906332c7791690606401600060405180830381600087803b158015610fba57600080fd5b505af1158015610fce573d6000803e3d6000fd5b5050505060e085015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b15801561102357600080fd5b505af1158015611037573d6000803e3d6000fd5b505050505b600354604051630d09de2d60e41b81526001600160a01b0384169163d09de2d0913491611079918b918b918b918b9160ff90911690600401611e68565b6000604051808303818588803b15801561109257600080fd5b505af11580156110a6573d6000803e3d6000fd5b50506040516306e9fe9960e41b81526001600160a01b038a8116600483015286811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072169350636e9fe99092506044019050600060405180830381600087803b15801561111757600080fd5b505af115801561112b573d6000803e3d6000fd5b5050505050949350505050565b60006001600160a01b0386166111825760405162461bcd60e51b815260206004820152600f60248201526e63616e6e6f742076616c696461746560881b60448201526064016105e5565b6001600160a01b0386163b1561125857604080516020810185905280820184905260f886901b6001600160f81b0319166060820152815160418183030181526061820192839052630b135d3f60e11b9092526001600160a01b03881690631626ba7e906111f59089908590606501611f97565b602060405180830381865afa92505050801561122e575060408051601f3d908101601f1916820190925261122b91810190611fb0565b60015b61123c5760009150506105b2565b6001600160e01b031916630b135d3f60e11b1491506105b29050565b6000611266868686866115ef565b6001600160a01b039081169088161491505095945050505050565b60028054036112d25760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016105e5565b60028055565b60006108bf7ffc8812d16d81e68c24359b3fd51d5169ab9eebe0cf94bdcea5b04b02d58358a97f49679996e28ae3d719dbc33eac28d7aebde3d77d1283ed79b042f8ad99e1fecc8585600001518660200151876060015188608001516040516020016113479493929190611fda565b604051602081830303815290604052805190602001208660c001518760a001518860e001518961010001518a61012001516040516020016113d09897969594939291909788526001600160a01b039687166020890152604088019590955260608701939093526080860191909152831660a085015290911660c083015260e08201526101000190565b60408051601f19818403018152828252805160209182012061190160f01b8483015260228401949094526042808401949094528151808403909401845260629092019052815191012090565b604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b8152606093841b60148201526f5af43d82803e903d91602b57fd5bf3ff60801b6028820152921b6038830152604c8201526037808220606c830152605591012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde66001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015611526573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061154a9190611e4b565b905090565b6000604051733d602d80600a3d3981f3363d3d373d3d3d363d7360601b81528360601b60148201526e5af43d82803e903d91602b57fd5bf360881b6028820152826037826000f59150506001600160a01b03811661059a5760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016105e5565b600080600061160087878787611617565b9150915061160d81611704565b5095945050505050565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561164e57506000905060036116fb565b8460ff16601b1415801561166657508460ff16601c14155b1561167757506000905060046116fb565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa1580156116cb573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166116f4576000600192509250506116fb565b9150600090505b94509492505050565b600081600481111561171857611718612031565b036117205750565b600181600481111561173457611734612031565b036117815760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e6174757265000000000000000060448201526064016105e5565b600281600481111561179557611795612031565b036117e25760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e6774680060448201526064016105e5565b60038160048111156117f6576117f6612031565b0361184e5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b60648201526084016105e5565b600481600481111561186257611862612031565b036118ba5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202776272076616c604482015261756560f01b60648201526084016105e5565b50565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff811182821017156118f7576118f76118bd565b60405290565b600082601f83011261190e57600080fd5b813567ffffffffffffffff80821115611929576119296118bd565b604051601f8301601f19908116603f01168101908282118183101715611951576119516118bd565b8160405283815286602085880101111561196a57600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146118ba57600080fd5b80356119aa8161198a565b919050565b600061014082840312156119c257600080fd5b6119ca6118d3565b9050813567ffffffffffffffff808211156119e457600080fd5b6119f0858386016118fd565b83526020840135915080821115611a0657600080fd5b611a12858386016118fd565b60208401526040840135915080821115611a2b57600080fd5b611a37858386016118fd565b60408401526060840135915080821115611a5057600080fd5b611a5c858386016118fd565b60608401526080840135915080821115611a7557600080fd5b50611a82848285016118fd565b60808301525060a082013560a082015260c082013560c0820152611aa860e0830161199f565b60e0820152610100611abb81840161199f565b818301525061012080830135818301525092915050565b600060208284031215611ae457600080fd5b813567ffffffffffffffff811115611afb57600080fd5b611b07848285016119af565b949350505050565b803560ff811681146119aa57600080fd5b600080600080600060a08688031215611b3857600080fd5b8535611b438161198a565b945060208601359350611b5860408701611b0f565b94979396509394606081013594506080013592915050565b600060208284031215611b8257600080fd5b5035919050565b600080600080600080600060e0888a031215611ba457600080fd5b8735611baf8161198a565b9650602088013567ffffffffffffffff80821115611bcc57600080fd5b611bd88b838c016119af565b9750611be660408b01611b0f565b965060608a0135955060808a0135945060a08a01359150611c068261198a565b90925060c08901359080821115611c1c57600080fd5b50611c298a828b016118fd565b91505092959891949750929550565b60008060408385031215611c4b57600080fd5b8235611c568161198a565b946020939093013593505050565b60005b83811015611c7f578181015183820152602001611c67565b50506000910152565b60008151808452611ca0816020860160208601611c64565b601f01601f19169290920160200192915050565b6020815260006108bf6020830184611c88565b600060208284031215611cd957600080fd5b813580151581146108bf57600080fd5b60008060408385031215611cfc57600080fd5b8235611d078161198a565b9150602083013567ffffffffffffffff811115611d2357600080fd5b611d2f858286016119af565b9150509250929050565b60008060408385031215611d4c57600080fd5b8235611d578161198a565b91506020830135611d678161198a565b809150509250929050565b600060208284031215611d8457600080fd5b81356108bf8161198a565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b6001600160a01b0385168152608060208201819052600090611dea90830186611c88565b8281036040840152611dfc8186611c88565b91505082606083015295945050505050565b6001600160a01b0383168152604060208201819052600090611b0790830184611c88565b600060208284031215611e4457600080fd5b5051919050565b600060208284031215611e5d57600080fd5b81516108bf8161198a565b60018060a01b038616815260a06020820152600085516101408060a0850152611e956101e0850183611c88565b91506020880151609f19808685030160c0870152611eb38483611c88565b935060408a01519150808685030160e0870152611ed08483611c88565b935060608a01519150610100818786030181880152611eef8584611c88565b945060808b01519250610120828887030181890152611f0e8685611c88565b955060a08c01518589015260c08c015161016089015260e08c01519450611f416101808901866001600160a01b03169052565b908b01516001600160a01b039081166101a0890152908b01516101c0880152891660408701525050508281036060840152611f7c8186611c88565b915050611f8d608083018415159052565b9695505050505050565b828152604060208201526000611b076040830184611c88565b600060208284031215611fc257600080fd5b81516001600160e01b0319811681146108bf57600080fd5b60008551611fec818460208a01611c64565b855190830190612000818360208a01611c64565b8551910190612013818360208901611c64565b8451910190612026818360208801611c64565b019695505050505050565b634e487b7160e01b600052602160045260246000fdfea2646970667358221220efb303927abd81e930f0222dd93758055b0b004795461d5cd2535e81f987f49064736f6c63430008110033

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

000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000027100000000000000000000000000000000000000000000000000000000000000001

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

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


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

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

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