Overview
ETH Balance
0 ETH
ETH Value
$0.00Token Holdings
More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 8,319 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Create With Sign... | 129196368 | 1 hr ago | IN | 0.00069 ETH | 0.000001438446 | ||||
Create With Sign... | 129194136 | 2 hrs ago | IN | 0.00069 ETH | 0.000001875822 | ||||
Create With Sign... | 129192689 | 3 hrs ago | IN | 0.00069 ETH | 0.000002925722 | ||||
Create With Sign... | 129184744 | 8 hrs ago | IN | 0.00069 ETH | 0.000000552123 | ||||
Create With Sign... | 129184500 | 8 hrs ago | IN | 0.00069 ETH | 0.000000714264 | ||||
Create With Sign... | 129176401 | 12 hrs ago | IN | 0.00069 ETH | 0.000001390729 | ||||
Create With Sign... | 129173353 | 14 hrs ago | IN | 0.00069 ETH | 0.000002833594 | ||||
Create With Sign... | 129171360 | 15 hrs ago | IN | 0.00069 ETH | 0.000001671746 | ||||
Create With Sign... | 129171360 | 15 hrs ago | IN | 0.00069 ETH | 0.0000017434 | ||||
Create With Sign... | 129167026 | 18 hrs ago | IN | 0.00069 ETH | 0.000006939637 | ||||
Create With Sign... | 129164945 | 19 hrs ago | IN | 0.00069 ETH | 0.000085560483 | ||||
Create With Sign... | 129163836 | 19 hrs ago | IN | 0.00069 ETH | 0.000001820723 | ||||
Create With Sign... | 129162362 | 20 hrs ago | IN | 0.00069 ETH | 0.000000810028 | ||||
Create With Sign... | 129162295 | 20 hrs ago | IN | 0.01929 ETH | 0.000001284519 | ||||
Create With Sign... | 129159680 | 22 hrs ago | IN | 0.00069 ETH | 0.000000459072 | ||||
Create With Sign... | 129159649 | 22 hrs ago | IN | 0.00069 ETH | 0.000000772489 | ||||
Create With Sign... | 129154014 | 25 hrs ago | IN | 0.00069 ETH | 0.000000742008 | ||||
Create With Sign... | 129148546 | 28 hrs ago | IN | 0.00069 ETH | 0.00000069176 | ||||
Create With Sign... | 129138979 | 33 hrs ago | IN | 0.00069 ETH | 0.000000714691 | ||||
Create With Sign... | 129136992 | 34 hrs ago | IN | 0.00069 ETH | 0.00000269898 | ||||
Create With Sign... | 129135524 | 35 hrs ago | IN | 0.00069 ETH | 0.000001094918 | ||||
Create With Sign... | 129135188 | 35 hrs ago | IN | 0.00069 ETH | 0.000001596356 | ||||
Create With Sign... | 129132860 | 37 hrs ago | IN | 0.00069 ETH | 0.00000141768 | ||||
Create With Sign... | 129132811 | 37 hrs ago | IN | 0.00069 ETH | 0.000001631514 | ||||
Create With Sign... | 129127677 | 39 hrs ago | IN | 0.00069 ETH | 0.000015409554 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block | From | To | |||
---|---|---|---|---|---|---|
129196368 | 1 hr ago | 0.00069 ETH | ||||
129196368 | 1 hr ago | Contract Creation | 0 ETH | |||
129194136 | 2 hrs ago | 0.00069 ETH | ||||
129194136 | 2 hrs ago | Contract Creation | 0 ETH | |||
129192689 | 3 hrs ago | 0.00069 ETH | ||||
129192689 | 3 hrs ago | Contract Creation | 0 ETH | |||
129184744 | 8 hrs ago | 0.00069 ETH | ||||
129184744 | 8 hrs ago | Contract Creation | 0 ETH | |||
129184500 | 8 hrs ago | 0.00069 ETH | ||||
129184500 | 8 hrs ago | Contract Creation | 0 ETH | |||
129176401 | 12 hrs ago | 0.00069 ETH | ||||
129176401 | 12 hrs ago | Contract Creation | 0 ETH | |||
129173353 | 14 hrs ago | 0.00069 ETH | ||||
129173353 | 14 hrs ago | Contract Creation | 0 ETH | |||
129171360 | 15 hrs ago | 0.00069 ETH | ||||
129171360 | 15 hrs ago | 0.00069 ETH | ||||
129171360 | 15 hrs ago | Contract Creation | 0 ETH | |||
129167026 | 18 hrs ago | 0.00069 ETH | ||||
129167026 | 18 hrs ago | Contract Creation | 0 ETH | |||
129164945 | 19 hrs ago | 0.00069 ETH | ||||
129164945 | 19 hrs ago | Contract Creation | 0 ETH | |||
129163836 | 19 hrs ago | 0.00069 ETH | ||||
129163836 | 19 hrs ago | Contract Creation | 0 ETH | |||
129162362 | 20 hrs ago | 0.00069 ETH | ||||
129162362 | 20 hrs ago | Contract Creation | 0 ETH |
Loading...
Loading
Contract Name:
WritingEditionsFactory
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity 0.8.19; /// > [[[[[[[[[[[ Imports ]]]]]]]]]]] import "./WritingEditions.sol"; import "./interface/IWritingEditionsFactory.sol"; import "../observability/Observability.sol"; import "../lib/Ownable.sol"; /// > [[[[[[[[[[[ External Library Imports ]]]]]]]]]]] import "openzeppelin-contracts/contracts/proxy/Clones.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 = 3; /// @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 Max limit. uint256 public override maxLimit; /// > [[[[[[[[[[[ 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; // 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) ); } /// > [[[[[[[[[[[ 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), "", 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, address mintReferral ) external payable override nonReentrant returns (address clone) { // Generate salt from parameters. bytes32 salt = keccak256( abi.encode(owner, edition.name, edition.symbol, edition.nonce) ); // If the clone has been deployed, purchase instead of deploying. if (salts[salt]) { clone = Clones.predictDeterministicAddress( implementation, salt, address(this) ); require(clone.code.length > 0, "invalid clone address"); // slither-disable-next-line unused-return WritingEditions(clone).purchase{value: msg.value}( tokenRecipient, message, mintReferral ); } else { salts[salt] = true; clone = _deployCloneAndInitializeWithSignature( owner, edition, v, r, s, tokenRecipient, message, mintReferral ); } } /// > [[[[[[[[[[[ 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 ]]]]]]]]]]] /// @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, address mintReferral ) internal returns (address clone) { clone = _validateEditionAndDeployClone(owner, edition); // Initialize clone. WritingEditions(clone).initialize{value: msg.value}( owner, edition, tokenRecipient, message, mintReferral, guardOn ); } function _deployCloneAndInitializeWithSignature( address owner, WritingEditions.WritingEdition memory edition, uint8 v, bytes32 r, bytes32 s, address tokenRecipient, string memory message, address mintReferral ) internal returns (address clone) { bytes32 salt = keccak256( abi.encode(owner, edition.name, edition.symbol, edition.nonce) ); clone = _validateEditionAndDeployClone(owner, edition); // Initialize clone with signature. WritingEditions(clone).initializeWithSignature{value: msg.value}( owner, salt, abi.encodePacked(r, s, v), edition, tokenRecipient, message, mintReferral, guardOn, msg.sender ); } function _validateEditionAndDeployClone( address owner, WritingEditions.WritingEdition memory edition ) 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 ); } 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 "../ERC7015/ERC7015.sol"; import "../observability/interface/IObservability.sol"; import "../fee-configuration/interface/IFeeConfiguration.sol"; import "../renderer/interface/IRenderer.sol"; import "../treasury/Rewards.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 ERC7015, Ownable, TransactionReentrancyGuard, ReentrancyGuard, ERC721, IERC721Metadata, IERC2981, IWritingEditions, IWritingEditionEvents, IObservabilityEvents { /// > [[[[[[[[[[[ Version ]]]]]]]]]]] /// @notice Version. uint8 public immutable override VERSION = 3; /// > [[[[[[[[[[[ 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; /// > [[[[[[[[[[[ Rewards ]]]]]]]]]]] /// @notice Address for the first minter if they are not the creator. address public override firstMinter; /// > [[[[[[[[[[[ 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) ERC7015("WritingEditions", "1") { // 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 with a signature by storing edition parameters. /// Called only by the factory. Mints the first edition to `tokenRecipient`. /// @param _creator the token creator /// @param structHash the salt parameter for ERC7015 /// @param signature the signature of the creator /// @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 initializeWithSignature( address _creator, bytes32 structHash, bytes calldata signature, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn, address sender ) external payable override nonReentrant { _validateSignature(_creator, structHash, signature); // Set first minter. if (sender != _creator) { firstMinter = sender; } _initialize( _creator, edition, tokenRecipient, message, mintReferral, _guardOn ); } /// @notice Initialize a clone by storing edition parameters. Called only /// by the factory. Mints the first edition to `tokenRecipient`. /// @param _creator 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 _creator, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn ) external payable override nonReentrant { _initialize( _creator, edition, tokenRecipient, message, mintReferral, _guardOn ); } function _initialize( address _creator, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn ) internal { // 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(_creator); // Store guard status. _setGuard(_guardOn); // Mint initial token to recipient if (tokenRecipient != address(0)) { _purchase(tokenRecipient, message, mintReferral); } } /// @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. /// @param mintReferral the account that referred the purchase. /// @return tokenId the id of the minted token. function purchase( address tokenRecipient, string memory message, address mintReferral ) external payable override guard nonReentrant returns (uint256 tokenId) { return _purchase(tokenRecipient, message, mintReferral); } /// > [[[[[[[[[[[ 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, address mintReferral ) 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, mintReferral); } 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, address mintReferral ) 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"); ( uint256 creatorReward, uint256 fee, uint256 mintReferralReward, uint256 firstMinterReward ) = Rewards.getRewards( mintReferral, price == 0, // free mint? flatFeeAmount ); if (firstMinter == address(0)) { // If the first minter is the creator, send them first minter reward. _sendEther( payable(fundsRecipient), price + creatorReward + firstMinterReward ); } else { _sendEther(payable(firstMinter), firstMinterReward); _sendEther(payable(fundsRecipient), price + creatorReward); } // Send fee to treasury. _sendEther(payable(treasury), fee); emit RewardsDistributed( creatorReward, fee, mintReferralReward, firstMinterReward, fundsRecipient, // creator mintReferral, firstMinter ); // Send referral reward to referral. if (mintReferralReward != 0) { _sendEther(payable(mintReferral), mintReferralReward); } } 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 baseDescriptionURI() external view returns (string memory); function predictDeterministicAddress( address implementation_, bytes32 salt ) external view returns (address); 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, address mintReferral ) 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; 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.9.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 { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 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 { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 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(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 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.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// 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); event RewardsDistributed( uint256 creatorReward, uint256 fee, uint256 mintReferralReward, uint256 firstMinterReward, address creator, address mintReferral, address firstMinter ); } 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 firstMinter() external view returns (address); function ownerOf( uint256[] memory tokenIds ) external view returns (address[] memory owners); function initializeWithSignature( address _creator, bytes32 structHash, bytes calldata signature, WritingEdition memory edition, address tokenRecipient, string memory message, address mintReferral, bool _guardOn, address sender ) external payable; function initialize( address _creator, WritingEdition memory edition, address recipient, string memory message, address mintReferral, 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, address mintReferral ) 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: MIT pragma solidity ^0.8.0; import "openzeppelin-contracts/contracts/utils/cryptography/EIP712.sol"; import "openzeppelin-contracts/contracts/utils/cryptography/ECDSA.sol"; import "openzeppelin-contracts/contracts/interfaces/IERC1271.sol"; import "solady/utils/LibString.sol"; /// @notice ERC-7015: NFT Creator Attribution /// @notice https://eips.ethereum.org/EIPS/eip-7015 /// @author indreams.eth abstract contract ERC7015 is EIP712 { error InvalidCreatorAttributionSignature(); error NameAndVersionTooLong(); error InvalidCreator(); event CreatorAttribution( bytes32 structHash, string domainName, string version, address creator, bytes signature ); /// @notice Define magic value to verify smart contract signatures (ERC1271). bytes4 internal constant MAGIC_VALUE = bytes4(keccak256("isValidSignature(bytes32,bytes)")); bytes32 public constant TYPEHASH = keccak256("TokenCreation(bytes32 structHash)"); bytes32 public immutable packedDomainNameAndVersion; constructor( string memory name, string memory version ) EIP712(name, version) { packedDomainNameAndVersion = LibString.packTwo(name, version); if (packedDomainNameAndVersion == bytes32(0)) revert NameAndVersionTooLong(); } function _validateSignature( address creator, bytes32 structHash, bytes calldata signature ) internal { if (!_isValid(structHash, creator, signature)) revert InvalidCreatorAttributionSignature(); (string memory _domainName, string memory _domainVersion) = LibString .unpackTwo(packedDomainNameAndVersion); emit CreatorAttribution( structHash, _domainName, _domainVersion, creator, signature ); } function _isValid( bytes32 structHash, address signer, bytes calldata signature ) internal view returns (bool) { if (signer == address(0)) revert InvalidCreator(); bytes32 digest = _hashTypedDataV4( keccak256(abi.encode(TYPEHASH, structHash)) ); // If the signer is a contract, attempt to validate the // signature using EIP-1271. if (signer.code.length != 0) { // slither-disable-next-line unused-return try IERC1271(signer).isValidSignature(digest, signature) returns ( // slither-disable-next-line uninitialized-local bytes4 magicValue ) { return MAGIC_VALUE == magicValue; } catch { return false; } } address recoveredSigner = ECDSA.recover(digest, signature); return recoveredSigner == signer; } }
// 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; /** * @title Rewards * @notice Calculates rewards for free and paid mints */ library Rewards { error FeeTooLowError(); /// @notice 40% creator reward for free mint uint256 constant _FREE_CREATOR_REWARD_PERCENTAGE = 40_00; /// @notice 10% mint referral reward for free mint uint256 constant _FREE_MINT_REFERRAL_REWARD_PERCENTAGE = 10_00; /// @notice 30% fee for free mint uint256 constant _FREE_FEE_PERCENTAGE = 30_00; /// @notice 20% first minter reward for free mint uint256 constant _FREE_FIRST_MINTER_REWARD_PECENTAGE = 20_00; /// @notice 25% creator referral reward for paid mint uint256 constant _PAID_MINT_REFERRAL_REWARD_PERCENTAGE = 25_00; /// @notice 50% mint referral reward for paid mint uint256 constant _PAID_FEE_PERCENTAGE = 50_00; /// @notice 25% fee for paid mint uint256 constant _PAID_FIRST_MINTER_REWARD_PERCENTAGE = 25_00; function getRewards( address mintReferral, bool freeMint, uint256 flatFeeAmount ) external pure returns (uint256, uint256, uint256, uint256) { if (flatFeeAmount < 10) revert FeeTooLowError(); if (freeMint) { return _getFreeMintRewards(mintReferral, flatFeeAmount); } return _getPaidMintRewards(mintReferral, flatFeeAmount); } function _getFreeMintRewards( address mintReferral, uint256 flatFeeAmount ) internal pure returns (uint256, uint256, uint256, uint256) { uint256 mintReferralReward = 0; uint256 creatorReward = (flatFeeAmount * _FREE_CREATOR_REWARD_PERCENTAGE) / 100_00; uint256 firstMinterReward = (flatFeeAmount * _FREE_FIRST_MINTER_REWARD_PECENTAGE) / 100_00; uint256 fee = (flatFeeAmount * _FREE_FEE_PERCENTAGE) / 100_00; if (mintReferral != address(0)) mintReferralReward = (flatFeeAmount * _FREE_MINT_REFERRAL_REWARD_PERCENTAGE) / 100_00; else fee += (flatFeeAmount * _FREE_MINT_REFERRAL_REWARD_PERCENTAGE) / 100_00; return (creatorReward, fee, mintReferralReward, firstMinterReward); } function _getPaidMintRewards( address mintReferral, uint256 flatFeeAmount ) internal pure returns (uint256, uint256, uint256, uint256) { uint256 mintReferralReward = 0; uint256 fee = (flatFeeAmount * _PAID_FEE_PERCENTAGE) / 100_00; uint256 firstMinterReward = (flatFeeAmount * _PAID_FIRST_MINTER_REWARD_PERCENTAGE) / 100_00; if (mintReferral != address(0)) mintReferralReward = (flatFeeAmount * _PAID_MINT_REFERRAL_REWARD_PERCENTAGE) / 100_00; else fee += (flatFeeAmount * _PAID_MINT_REFERRAL_REWARD_PERCENTAGE) / 100_00; return (0, fee, mintReferralReward, firstMinterReward); } }
// 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.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.8; import "./ECDSA.sol"; import "../ShortStrings.sol"; import "../../interfaces/IERC5267.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. * * _Available since v3.4._ * * @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment */ abstract contract EIP712 is IERC5267 { using ShortStrings for *; bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to // invalidate the cached domain separator if the chain id changes. bytes32 private immutable _cachedDomainSeparator; uint256 private immutable _cachedChainId; address private immutable _cachedThis; bytes32 private immutable _hashedName; bytes32 private immutable _hashedVersion; ShortString private immutable _name; ShortString private immutable _version; string private _nameFallback; string private _versionFallback; /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ constructor(string memory name, string memory version) { _name = name.toShortStringWithFallback(_nameFallback); _version = version.toShortStringWithFallback(_versionFallback); _hashedName = keccak256(bytes(name)); _hashedVersion = keccak256(bytes(version)); _cachedChainId = block.chainid; _cachedDomainSeparator = _buildDomainSeparator(); _cachedThis = address(this); } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { if (address(this) == _cachedThis && block.chainid == _cachedChainId) { return _cachedDomainSeparator; } else { return _buildDomainSeparator(); } } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {EIP-5267}. * * _Available since v4.9._ */ function eip712Domain() public view virtual override returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { return ( hex"0f", // 01111 _name.toStringWithFallback(_nameFallback), _version.toStringWithFallback(_versionFallback), block.chainid, address(this), bytes32(0), new uint256[](0) ); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 // Deprecated in v4.8 } 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"); } } /** * @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) { 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 { 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 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 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @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 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC1271.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC1271 standard signature validation method for * contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271]. * * _Available since v4.1._ */ interface IERC1271 { /** * @dev Should return whether the signature provided is valid for the provided data * @param hash Hash of the data to be signed * @param signature Signature byte array associated with _data */ function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Library for converting numbers into strings and other string operations. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibString.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/LibString.sol) library LibString { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The `length` of the output is too small to contain all the hex digits. error HexLengthInsufficient(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The constant returned when the `search` is not found in the string. uint256 internal constant NOT_FOUND = type(uint256).max; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* DECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the base 10 decimal representation of `value`. function toString(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // The maximum value of a uint256 contains 78 digits (1 byte per digit), but // we allocate 0xa0 bytes to keep the free memory pointer 32-byte word aligned. // We will need 1 word for the trailing zeros padding, 1 word for the length, // and 3 words for a maximum of 78 digits. str := add(mload(0x40), 0x80) // Update the free memory pointer to allocate. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end of the memory to calculate the length later. let end := str let w := not(0) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 1)`. // Write the character to the pointer. // The ASCII index of the '0' character is 48. mstore8(str, add(48, mod(temp, 10))) // Keep dividing `temp` until zero. temp := div(temp, 10) if iszero(temp) { break } } let length := sub(end, str) // Move the pointer 32 bytes leftwards to make room for the length. str := sub(str, 0x20) // Store the length. mstore(str, length) } } /// @dev Returns the base 10 decimal representation of `value`. function toString(int256 value) internal pure returns (string memory str) { if (value >= 0) { return toString(uint256(value)); } unchecked { str = toString(uint256(-value)); } /// @solidity memory-safe-assembly assembly { // We still have some spare memory space on the left, // as we have allocated 3 words (96 bytes) for up to 78 digits. let length := mload(str) // Load the string length. mstore(str, 0x2d) // Store the '-' character. str := sub(str, 1) // Move back the string pointer by a byte. mstore(str, add(length, 1)) // Update the string length. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HEXADECIMAL OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2 + 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexString(uint256 value, uint256 length) internal pure returns (string memory str) { str = toHexStringNoPrefix(value, length); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`, /// left-padded to an input length of `length` bytes. /// The output is prefixed with "0x" encoded using 2 hexadecimal digits per byte, /// giving a total length of `length * 2` bytes. /// Reverts if `length` is too small for the output to contain all the digits. function toHexStringNoPrefix(uint256 value, uint256 length) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, `length * 2` bytes // for the digits, 0x02 bytes for the prefix, and 0x20 bytes for the length. // We add 0x20 to the total and round down to a multiple of 0x20. // (0x20 + 0x20 + 0x02 + 0x20) = 0x62. str := add(mload(0x40), and(add(shl(1, length), 0x42), not(0x1f))) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let start := sub(str, add(length, length)) let w := not(1) // Tsk. let temp := value // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for {} 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(xor(str, start)) { break } } if temp { // Store the function selector of `HexLengthInsufficient()`. mstore(0x00, 0x2194895a) // Revert with (offset, size). revert(0x1c, 0x04) } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2 + 2` bytes. function toHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x". /// The output excludes leading "0" from the `toHexString` output. /// `0x00: "0x0", 0x01: "0x1", 0x12: "0x12", 0x123: "0x123"`. function toMinimalHexString(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := add(mload(str), 2) // Compute the length. mstore(add(str, o), 0x3078) // Write the "0x" prefix, accounting for leading zero. str := sub(add(str, o), 2) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output excludes leading "0" from the `toHexStringNoPrefix` output. /// `0x00: "0", 0x01: "1", 0x12: "12", 0x123: "123"`. function toMinimalHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let o := eq(byte(0, mload(add(str, 0x20))), 0x30) // Whether leading zero is present. let strLength := mload(str) // Get the length. str := add(str, o) // Move the pointer, accounting for leading zero. mstore(str, sub(strLength, o)) // Write the length, accounting for leading zero. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. /// As address are 20 bytes long, the output will left-padded to have /// a length of `20 * 2` bytes. function toHexStringNoPrefix(uint256 value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x40 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x40) is 0xa0. str := add(mload(0x40), 0x80) // Allocate the memory. mstore(0x40, add(str, 0x20)) // Zeroize the slot after the string. mstore(str, 0) // Cache the end to calculate the length later. let end := str // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let w := not(1) // Tsk. // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let temp := value } 1 {} { str := add(str, w) // `sub(str, 2)`. mstore8(add(str, 1), mload(and(temp, 15))) mstore8(str, mload(and(shr(4, temp), 15))) temp := shr(8, temp) if iszero(temp) { break } } // Compute the string's length. let strLength := sub(end, str) // Move the pointer and write the length. str := sub(str, 0x20) mstore(str, strLength) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x", encoded using 2 hexadecimal digits per byte, /// and the alphabets are capitalized conditionally according to /// https://eips.ethereum.org/EIPS/eip-55 function toHexStringChecksummed(address value) internal pure returns (string memory str) { str = toHexString(value); /// @solidity memory-safe-assembly assembly { let mask := shl(6, div(not(0), 255)) // `0b010000000100000000 ...` let o := add(str, 0x22) let hashed := and(keccak256(o, 40), mul(34, mask)) // `0b10001000 ... ` let t := shl(240, 136) // `0b10001000 << 240` for { let i := 0 } 1 {} { mstore(add(i, i), mul(t, byte(i, hashed))) i := add(i, 1) if eq(i, 20) { break } } mstore(o, xor(mload(o), shr(1, and(mload(0x00), and(mload(o), mask))))) o := add(o, 0x20) mstore(o, xor(mload(o), shr(1, and(mload(0x20), and(mload(o), mask))))) } } /// @dev Returns the hexadecimal representation of `value`. /// The output is prefixed with "0x" and encoded using 2 hexadecimal digits per byte. function toHexString(address value) internal pure returns (string memory str) { str = toHexStringNoPrefix(value); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hexadecimal representation of `value`. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(address value) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { str := mload(0x40) // Allocate the memory. // We need 0x20 bytes for the trailing zeros padding, 0x20 bytes for the length, // 0x02 bytes for the prefix, and 0x28 bytes for the digits. // The next multiple of 0x20 above (0x20 + 0x20 + 0x02 + 0x28) is 0x80. mstore(0x40, add(str, 0x80)) // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) str := add(str, 2) mstore(str, 40) let o := add(str, 0x20) mstore(add(o, 40), 0) value := shl(96, value) // We write the string from rightmost digit to leftmost digit. // The following is essentially a do-while loop that also handles the zero case. for { let i := 0 } 1 {} { let p := add(o, add(i, i)) let temp := byte(i, value) mstore8(add(p, 1), mload(and(temp, 15))) mstore8(p, mload(shr(4, temp))) i := add(i, 1) if eq(i, 20) { break } } } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexString(bytes memory raw) internal pure returns (string memory str) { str = toHexStringNoPrefix(raw); /// @solidity memory-safe-assembly assembly { let strLength := add(mload(str), 2) // Compute the length. mstore(str, 0x3078) // Write the "0x" prefix. str := sub(str, 2) // Move the pointer. mstore(str, strLength) // Write the length. } } /// @dev Returns the hex encoded string from the raw bytes. /// The output is encoded using 2 hexadecimal digits per byte. function toHexStringNoPrefix(bytes memory raw) internal pure returns (string memory str) { /// @solidity memory-safe-assembly assembly { let length := mload(raw) str := add(mload(0x40), 2) // Skip 2 bytes for the optional prefix. mstore(str, add(length, length)) // Store the length of the output. // Store "0123456789abcdef" in scratch space. mstore(0x0f, 0x30313233343536373839616263646566) let o := add(str, 0x20) let end := add(raw, length) for {} iszero(eq(raw, end)) {} { raw := add(raw, 1) mstore8(add(o, 1), mload(and(mload(raw), 15))) mstore8(o, mload(and(shr(4, mload(raw)), 15))) o := add(o, 2) } mstore(o, 0) // Zeroize the slot after the string. mstore(0x40, add(o, 0x20)) // Allocate the memory. } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RUNE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the number of UTF characters in the string. function runeCount(string memory s) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { if mload(s) { mstore(0x00, div(not(0), 255)) mstore(0x20, 0x0202020202020202020202020202020202020202020202020303030304040506) let o := add(s, 0x20) let end := add(o, mload(s)) for { result := 1 } 1 { result := add(result, 1) } { o := add(o, byte(0, mload(shr(250, mload(o))))) if iszero(lt(o, end)) { break } } } } } /// @dev Returns if this string is a 7-bit ASCII string. /// (i.e. all characters codes are in [0..127]) function is7BitASCII(string memory s) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let mask := shl(7, div(not(0), 255)) result := 1 let n := mload(s) if n { let o := add(s, 0x20) let end := add(o, n) let last := mload(end) mstore(end, 0) for {} 1 {} { if and(mask, mload(o)) { result := 0 break } o := add(o, 0x20) if iszero(lt(o, end)) { break } } mstore(end, last) } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* BYTE STRING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ // For performance and bytecode compactness, all indices of the following operations // are byte (ASCII) offsets, not UTF character offsets. /// @dev Returns `subject` all occurrences of `search` replaced with `replacement`. function replace(string memory subject, string memory search, string memory replacement) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) let replacementLength := mload(replacement) subject := add(subject, 0x20) search := add(search, 0x20) replacement := add(replacement, 0x20) result := add(mload(0x40), 0x20) let subjectEnd := add(subject, subjectLength) if iszero(gt(searchLength, subjectLength)) { let subjectSearchEnd := add(sub(subjectEnd, searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Copy the `replacement` one word at a time. for { let o := 0 } 1 {} { mstore(add(result, o), mload(add(replacement, o))) o := add(o, 0x20) if iszero(lt(o, replacementLength)) { break } } result := add(result, replacementLength) subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } mstore(result, t) result := add(result, 1) subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } } let resultRemainder := result result := add(mload(0x40), 0x20) let k := add(sub(resultRemainder, result), sub(subjectEnd, subject)) // Copy the rest of the string one word at a time. for {} lt(subject, subjectEnd) {} { mstore(resultRemainder, mload(subject)) resultRemainder := add(resultRemainder, 0x20) subject := add(subject, 0x20) } result := sub(result, 0x20) let last := add(add(result, 0x20), k) // Zeroize the slot after the string. mstore(last, 0) mstore(0x40, add(last, 0x20)) // Allocate the memory. mstore(result, k) // Store the length. } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { let subjectLength := mload(subject) } 1 {} { if iszero(mload(search)) { if iszero(gt(from, subjectLength)) { result := from break } result := subjectLength break } let searchLength := mload(search) let subjectStart := add(subject, 0x20) result := not(0) // Initialize to `NOT_FOUND`. subject := add(subjectStart, from) let end := add(sub(add(subjectStart, subjectLength), searchLength), 1) let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(add(search, 0x20)) if iszero(and(lt(subject, end), lt(from, subjectLength))) { break } if iszero(lt(searchLength, 0x20)) { for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if iszero(shr(m, xor(mload(subject), s))) { if eq(keccak256(subject, searchLength), h) { result := sub(subject, subjectStart) break } } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } for {} 1 {} { if iszero(shr(m, xor(mload(subject), s))) { result := sub(subject, subjectStart) break } subject := add(subject, 1) if iszero(lt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from left to right. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function indexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = indexOf(subject, search, 0); } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left, starting from `from`. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search, uint256 from) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { result := not(0) // Initialize to `NOT_FOUND`. let searchLength := mload(search) if gt(searchLength, mload(subject)) { break } let w := result let fromMax := sub(mload(subject), searchLength) if iszero(gt(fromMax, from)) { from := fromMax } let end := add(add(subject, 0x20), w) subject := add(add(subject, 0x20), from) if iszero(gt(subject, end)) { break } // As this function is not too often used, // we shall simply use keccak256 for smaller bytecode size. for { let h := keccak256(add(search, 0x20), searchLength) } 1 {} { if eq(keccak256(subject, searchLength), h) { result := sub(subject, add(end, 1)) break } subject := add(subject, w) // `sub(subject, 1)`. if iszero(gt(subject, end)) { break } } break } } } /// @dev Returns the byte index of the first location of `search` in `subject`, /// searching from right to left. /// Returns `NOT_FOUND` (i.e. `type(uint256).max`) if the `search` is not found. function lastIndexOf(string memory subject, string memory search) internal pure returns (uint256 result) { result = lastIndexOf(subject, search, uint256(int256(-1))); } /// @dev Returns whether `subject` starts with `search`. function startsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( iszero(gt(searchLength, mload(subject))), eq( keccak256(add(subject, 0x20), searchLength), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns whether `subject` ends with `search`. function endsWith(string memory subject, string memory search) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { let searchLength := mload(search) let subjectLength := mload(subject) // Whether `search` is not longer than `subject`. let withinRange := iszero(gt(searchLength, subjectLength)) // Just using keccak256 directly is actually cheaper. // forgefmt: disable-next-item result := and( withinRange, eq( keccak256( // `subject + 0x20 + max(subjectLength - searchLength, 0)`. add(add(subject, 0x20), mul(withinRange, sub(subjectLength, searchLength))), searchLength ), keccak256(add(search, 0x20), searchLength) ) ) } } /// @dev Returns `subject` repeated `times`. function repeat(string memory subject, uint256 times) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(or(iszero(times), iszero(subjectLength))) { subject := add(subject, 0x20) result := mload(0x40) let output := add(result, 0x20) for {} 1 {} { // Copy the `subject` one word at a time. for { let o := 0 } 1 {} { mstore(add(output, o), mload(add(subject, o))) o := add(o, 0x20) if iszero(lt(o, subjectLength)) { break } } output := add(output, subjectLength) times := sub(times, 1) if iszero(times) { break } } mstore(output, 0) // Zeroize the slot after the string. let resultLength := sub(output, add(result, 0x20)) mstore(result, resultLength) // Store the length. // Allocate the memory. mstore(0x40, add(result, add(resultLength, 0x20))) } } } /// @dev Returns a copy of `subject` sliced from `start` to `end` (exclusive). /// `start` and `end` are byte offsets. function slice(string memory subject, uint256 start, uint256 end) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) if iszero(gt(subjectLength, end)) { end := subjectLength } if iszero(gt(subjectLength, start)) { start := subjectLength } if lt(start, end) { result := mload(0x40) let resultLength := sub(end, start) mstore(result, resultLength) subject := add(subject, start) let w := not(0x1f) // Copy the `subject` one word at a time, backwards. for { let o := and(add(resultLength, 0x1f), w) } 1 {} { mstore(add(result, o), mload(add(subject, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(result, 0x20), resultLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(result, and(add(resultLength, 0x3f), w))) } } } /// @dev Returns a copy of `subject` sliced from `start` to the end of the string. /// `start` is a byte offset. function slice(string memory subject, uint256 start) internal pure returns (string memory result) { result = slice(subject, start, uint256(int256(-1))); } /// @dev Returns all the indices of `search` in `subject`. /// The indices are byte offsets. function indicesOf(string memory subject, string memory search) internal pure returns (uint256[] memory result) { /// @solidity memory-safe-assembly assembly { let subjectLength := mload(subject) let searchLength := mload(search) if iszero(gt(searchLength, subjectLength)) { subject := add(subject, 0x20) search := add(search, 0x20) result := add(mload(0x40), 0x20) let subjectStart := subject let subjectSearchEnd := add(sub(add(subject, subjectLength), searchLength), 1) let h := 0 if iszero(lt(searchLength, 0x20)) { h := keccak256(search, searchLength) } let m := shl(3, sub(0x20, and(searchLength, 0x1f))) let s := mload(search) for {} 1 {} { let t := mload(subject) // Whether the first `searchLength % 32` bytes of // `subject` and `search` matches. if iszero(shr(m, xor(t, s))) { if h { if iszero(eq(keccak256(subject, searchLength), h)) { subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } continue } } // Append to `result`. mstore(result, sub(subject, subjectStart)) result := add(result, 0x20) // Advance `subject` by `searchLength`. subject := add(subject, searchLength) if searchLength { if iszero(lt(subject, subjectSearchEnd)) { break } continue } } subject := add(subject, 1) if iszero(lt(subject, subjectSearchEnd)) { break } } let resultEnd := result // Assign `result` to the free memory pointer. result := mload(0x40) // Store the length of `result`. mstore(result, shr(5, sub(resultEnd, add(result, 0x20)))) // Allocate memory for result. // We allocate one more word, so this array can be recycled for {split}. mstore(0x40, add(resultEnd, 0x20)) } } } /// @dev Returns a arrays of strings based on the `delimiter` inside of the `subject` string. function split(string memory subject, string memory delimiter) internal pure returns (string[] memory result) { uint256[] memory indices = indicesOf(subject, delimiter); /// @solidity memory-safe-assembly assembly { let w := not(0x1f) let indexPtr := add(indices, 0x20) let indicesEnd := add(indexPtr, shl(5, add(mload(indices), 1))) mstore(add(indicesEnd, w), mload(subject)) mstore(indices, add(mload(indices), 1)) let prevIndex := 0 for {} 1 {} { let index := mload(indexPtr) mstore(indexPtr, 0x60) if iszero(eq(index, prevIndex)) { let element := mload(0x40) let elementLength := sub(index, prevIndex) mstore(element, elementLength) // Copy the `subject` one word at a time, backwards. for { let o := and(add(elementLength, 0x1f), w) } 1 {} { mstore(add(element, o), mload(add(add(subject, prevIndex), o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } // Zeroize the slot after the string. mstore(add(add(element, 0x20), elementLength), 0) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, add(element, and(add(elementLength, 0x3f), w))) // Store the `element` into the array. mstore(indexPtr, element) } prevIndex := add(index, mload(delimiter)) indexPtr := add(indexPtr, 0x20) if iszero(lt(indexPtr, indicesEnd)) { break } } result := indices if iszero(mload(delimiter)) { result := add(indices, 0x20) mstore(result, sub(mload(indices), 2)) } } } /// @dev Returns a concatenated string of `a` and `b`. /// Cheaper than `string.concat()` and does not de-align the free memory pointer. function concat(string memory a, string memory b) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let w := not(0x1f) result := mload(0x40) let aLength := mload(a) // Copy `a` one word at a time, backwards. for { let o := and(add(aLength, 0x20), w) } 1 {} { mstore(add(result, o), mload(add(a, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let bLength := mload(b) let output := add(result, aLength) // Copy `b` one word at a time, backwards. for { let o := and(add(bLength, 0x20), w) } 1 {} { mstore(add(output, o), mload(add(b, o))) o := add(o, w) // `sub(o, 0x20)`. if iszero(o) { break } } let totalLength := add(aLength, bLength) let last := add(add(result, 0x20), totalLength) // Zeroize the slot after the string. mstore(last, 0) // Stores the length. mstore(result, totalLength) // Allocate memory for the length and the bytes, // rounded up to a multiple of 32. mstore(0x40, and(add(last, 0x1f), w)) } } /// @dev Returns a copy of the string in either lowercase or UPPERCASE. /// WARNING! This function is only compatible with 7-bit ASCII strings. function toCase(string memory subject, bool toUpper) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let length := mload(subject) if length { result := add(mload(0x40), 0x20) subject := add(subject, 1) let flags := shl(add(70, shl(5, toUpper)), 0x3ffffff) let w := not(0) for { let o := length } 1 {} { o := add(o, w) let b := and(0xff, mload(add(subject, o))) mstore8(add(result, o), xor(b, and(shr(b, flags), 0x20))) if iszero(o) { break } } result := mload(0x40) mstore(result, length) // Store the length. let last := add(add(result, 0x20), length) mstore(last, 0) // Zeroize the slot after the string. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } } /// @dev Returns a string from a small bytes32 string. /// `smallString` must be null terminated, or behavior will be undefined. function fromSmallString(bytes32 smallString) internal pure returns (string memory result) { if (smallString == bytes32(0)) return result; /// @solidity memory-safe-assembly assembly { result := mload(0x40) let n := 0 for {} 1 {} { n := add(n, 1) if iszero(byte(n, smallString)) { break } // Scan for '\0'. } mstore(result, n) let o := add(result, 0x20) mstore(o, smallString) mstore(add(o, n), 0) mstore(0x40, add(result, 0x40)) } } /// @dev Returns a lowercased copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function lower(string memory subject) internal pure returns (string memory result) { result = toCase(subject, false); } /// @dev Returns an UPPERCASED copy of the string. /// WARNING! This function is only compatible with 7-bit ASCII strings. function upper(string memory subject) internal pure returns (string memory result) { result = toCase(subject, true); } /// @dev Escapes the string to be used within HTML tags. function escapeHTML(string memory s) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) // Store the bytes of the packed offsets and strides into the scratch space. // `packed = (stride << 5) | offset`. Max offset is 20. Max stride is 6. mstore(0x1f, 0x900094) mstore(0x08, 0xc0000000a6ab) // Store ""&'<>" into the scratch space. mstore(0x00, shl(64, 0x2671756f743b26616d703b262333393b266c743b2667743b)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) // Not in `["\"","'","&","<",">"]`. if iszero(and(shl(c, 1), 0x500000c400000000)) { mstore8(result, c) result := add(result, 1) continue } let t := shr(248, mload(c)) mstore(result, mload(and(t, 0x1f))) result := add(result, shr(5, t)) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. /// If `addDoubleQuotes` is true, the result will be enclosed in double-quotes. function escapeJSON(string memory s, bool addDoubleQuotes) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { let end := add(s, mload(s)) result := add(mload(0x40), 0x20) if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } // Store "\\u0000" in scratch space. // Store "0123456789abcdef" in scratch space. // Also, store `{0x08:"b", 0x09:"t", 0x0a:"n", 0x0c:"f", 0x0d:"r"}`. // into the scratch space. mstore(0x15, 0x5c75303030303031323334353637383961626364656662746e006672) // Bitmask for detecting `["\"","\\"]`. let e := or(shl(0x22, 1), shl(0x5c, 1)) for {} iszero(eq(s, end)) {} { s := add(s, 1) let c := and(mload(s), 0xff) if iszero(lt(c, 0x20)) { if iszero(and(shl(c, 1), e)) { // Not in `["\"","\\"]`. mstore8(result, c) result := add(result, 1) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), c) result := add(result, 2) continue } if iszero(and(shl(c, 1), 0x3700)) { // Not in `["\b","\t","\n","\f","\d"]`. mstore8(0x1d, mload(shr(4, c))) // Hex value. mstore8(0x1e, mload(and(c, 15))) // Hex value. mstore(result, mload(0x19)) // "\\u00XX". result := add(result, 6) continue } mstore8(result, 0x5c) // "\\". mstore8(add(result, 1), mload(add(c, 8))) result := add(result, 2) } if addDoubleQuotes { mstore8(result, 34) result := add(1, result) } let last := result mstore(last, 0) // Zeroize the slot after the string. result := mload(0x40) mstore(result, sub(last, add(result, 0x20))) // Store the length. mstore(0x40, add(last, 0x20)) // Allocate the memory. } } /// @dev Escapes the string to be used within double-quotes in a JSON. function escapeJSON(string memory s) internal pure returns (string memory result) { result = escapeJSON(s, false); } /// @dev Returns whether `a` equals `b`. function eq(string memory a, string memory b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { result := eq(keccak256(add(a, 0x20), mload(a)), keccak256(add(b, 0x20), mload(b))) } } /// @dev Returns whether `a` equals `b`. For small strings up to 32 bytes. /// `b` must be null terminated, or behavior will be undefined. function eqs(string memory a, bytes32 b) internal pure returns (bool result) { /// @solidity memory-safe-assembly assembly { // These should be evaluated on compile time, as far as possible. let x := and(b, add(not(b), 1)) let r := or(shl(8, iszero(b)), shl(7, iszero(iszero(shr(128, x))))) r := or(r, shl(6, iszero(iszero(shr(64, shr(r, x)))))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffff, shr(r, x)))) r := or(r, shl(3, lt(0xff, shr(r, x)))) result := gt(eq(mload(a), sub(32, shr(3, r))), shr(r, xor(b, mload(add(a, 0x20))))) } } /// @dev Packs a single string with its length into a single word. /// Returns `bytes32(0)` if the length is zero or greater than 31. function packOne(string memory a) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { // We don't need to zero right pad the string, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes. mload(add(a, 0x1f)), // `length != 0 && length < 32`. Abuses underflow. // Assumes that the length is valid and within the block gas limit. lt(sub(mload(a), 1), 0x1f) ) } } /// @dev Unpacks a string packed using {packOne}. /// Returns the empty string if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packOne}, the output behavior is undefined. function unpackOne(bytes32 packed) internal pure returns (string memory result) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. result := mload(0x40) // Allocate 2 words (1 for the length, 1 for the bytes). mstore(0x40, add(result, 0x40)) // Zeroize the length slot. mstore(result, 0) // Store the length and bytes. mstore(add(result, 0x1f), packed) // Right pad with zeroes. mstore(add(add(result, 0x20), mload(result)), 0) } } /// @dev Packs two strings with their lengths into a single word. /// Returns `bytes32(0)` if combined length is zero or greater than 30. function packTwo(string memory a, string memory b) internal pure returns (bytes32 result) { /// @solidity memory-safe-assembly assembly { let aLength := mload(a) // We don't need to zero right pad the strings, // since this is our own custom non-standard packing scheme. result := mul( // Load the length and the bytes of `a` and `b`. or( shl(shl(3, sub(0x1f, aLength)), mload(add(a, aLength))), mload(sub(add(b, 0x1e), aLength)) ), // `totalLength != 0 && totalLength < 31`. Abuses underflow. // Assumes that the lengths are valid and within the block gas limit. lt(sub(add(aLength, mload(b)), 1), 0x1e) ) } } /// @dev Unpacks strings packed using {packTwo}. /// Returns the empty strings if `packed` is `bytes32(0)`. /// If `packed` is not an output of {packTwo}, the output behavior is undefined. function unpackTwo(bytes32 packed) internal pure returns (string memory resultA, string memory resultB) { /// @solidity memory-safe-assembly assembly { // Grab the free memory pointer. resultA := mload(0x40) resultB := add(resultA, 0x40) // Allocate 2 words for each string (1 for the length, 1 for the byte). Total 4 words. mstore(0x40, add(resultB, 0x40)) // Zeroize the length slots. mstore(resultA, 0) mstore(resultB, 0) // Store the lengths and bytes. mstore(add(resultA, 0x1f), packed) mstore(add(resultB, 0x1f), mload(add(add(resultA, 0x20), mload(resultA)))) // Right pad with zeroes. mstore(add(add(resultA, 0x20), mload(resultA)), 0) mstore(add(add(resultB, 0x20), mload(resultB)), 0) } } /// @dev Directly returns `a` without copying. function directReturn(string memory a) internal pure { assembly { // Assumes that the string does not start from the scratch space. let retStart := sub(a, 0x20) let retSize := add(mload(a), 0x40) // Right pad with zeroes. Just in case the string is produced // by a method that doesn't zero right pad. mstore(add(retStart, retSize), 0) // Store the return offset. mstore(retStart, 0x20) // End the transaction, returning the string. return(retStart, retSize) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/ShortStrings.sol) pragma solidity ^0.8.8; import "./StorageSlot.sol"; // | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA | // | length | 0x BB | type ShortString is bytes32; /** * @dev This library provides functions to convert short memory strings * into a `ShortString` type that can be used as an immutable variable. * * Strings of arbitrary length can be optimized using this library if * they are short enough (up to 31 bytes) by packing them with their * length (1 byte) in a single EVM word (32 bytes). Additionally, a * fallback mechanism can be used for every other case. * * Usage example: * * ```solidity * contract Named { * using ShortStrings for *; * * ShortString private immutable _name; * string private _nameFallback; * * constructor(string memory contractName) { * _name = contractName.toShortStringWithFallback(_nameFallback); * } * * function name() external view returns (string memory) { * return _name.toStringWithFallback(_nameFallback); * } * } * ``` */ library ShortStrings { // Used as an identifier for strings longer than 31 bytes. bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF; error StringTooLong(string str); error InvalidShortString(); /** * @dev Encode a string of at most 31 chars into a `ShortString`. * * This will trigger a `StringTooLong` error is the input string is too long. */ function toShortString(string memory str) internal pure returns (ShortString) { bytes memory bstr = bytes(str); if (bstr.length > 31) { revert StringTooLong(str); } return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length)); } /** * @dev Decode a `ShortString` back to a "normal" string. */ function toString(ShortString sstr) internal pure returns (string memory) { uint256 len = byteLength(sstr); // using `new string(len)` would work locally but is not memory safe. string memory str = new string(32); /// @solidity memory-safe-assembly assembly { mstore(str, len) mstore(add(str, 0x20), sstr) } return str; } /** * @dev Return the length of a `ShortString`. */ function byteLength(ShortString sstr) internal pure returns (uint256) { uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF; if (result > 31) { revert InvalidShortString(); } return result; } /** * @dev Encode a string into a `ShortString`, or write it to storage if it is too long. */ function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) { if (bytes(value).length < 32) { return toShortString(value); } else { StorageSlot.getStringSlot(store).value = value; return ShortString.wrap(_FALLBACK_SENTINEL); } } /** * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}. */ function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) { if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { return toString(value); } else { return store; } } /** * @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}. * * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of * actual characters as the UTF-8 encoding of a single character can span over multiple bytes. */ function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) { if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) { return byteLength(value); } else { return bytes(store).length; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.0; interface IERC5267 { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ```solidity * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._ * _Available since v4.9 for `string`, `bytes`._ */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } }
{ "remappings": [ "solmate/=lib/solmate/src/", "ds-test/=lib/ds-test/src/", "openzeppelin-contracts/=lib/openzeppelin-contracts/", "multicall/=lib/multicall/src/", "forge-std/=lib/forge-std/src/", "solady/=lib/solady/src/", "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/", "openzeppelin/=lib/openzeppelin-contracts/contracts/" ], "optimizer": { "enabled": true, "runs": 200 }, "metadata": { "useLiteralContent": false, "bytecodeHash": "ipfs", "appendCBOR": true }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "paris", "libraries": { "src/treasury/Rewards.sol": { "Rewards": "0x77bcbbb7783ea8c43ae1893e35c76daf7ec153e3" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"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":"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"},{"internalType":"address","name":"mintReferral","type":"address"}],"name":"createWithSignature","outputs":[{"internalType":"address","name":"clone","type":"address"}],"stateMutability":"payable","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":[],"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"}]
Contract Creation Code
60e060405260036080523480156200001657600080fd5b50604051620073c3380380620073c3833981016040819052620000399162000227565b846200004581620001b1565b5060016002556001600160a01b038416620000a65760405162461bcd60e51b815260206004820152601860248201527f6d7573742073657420747265617375727920636f6e6669670000000000000000604482015260640160405180910390fd5b6001600160a01b03841660c052604051309085908590620000c790620001fc565b6001600160a01b03938416815291831660208301529091166040820152606001604051809103906000f08015801562000104573d6000803e3d6000fd5b5060038054610100600160a81b0319166101006001600160a01b039384168102919091179182905585831660a08190526040516322fd9ed760e01b815260006004820152919092049092166024830152906322fd9ed790604401600060405180830381600087803b1580156200017957600080fd5b505af11580156200018e573d6000803e3d6000fd5b50506003805460ff19169315159390931790925550506005555062000296915050565b600080546001600160a01b0319166001600160a01b03831690811782556040519091907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b6156c58062001cfe83390190565b80516001600160a01b03811681146200022257600080fd5b919050565b600080600080600060a086880312156200024057600080fd5b6200024b866200020a565b94506200025b602087016200020a565b93506200026b604087016200020a565b925060608601519150608086015180151581146200028857600080fd5b809150509295509295909350565b60805160a05160c051611a07620002f7600039600081816103900152610dc80152600081816102bf0152818161053e015281816105ff0152818161083f01528181610b6f015281816111150152611204015260006104540152611a076000f3fe6080604052600436106101355760003560e01c806379ba5097116100ab578063bee0ec0b1161006f578063bee0ec0b1461037e578063d784d426146103b2578063e6d283a6146103d2578063ed459df214610402578063f2fde38b14610422578063ffa1ad741461044257600080fd5b806379ba5097146102f65780638da5cb5b1461030b5780638f32d59b1461032b578063a56600631461034b578063ae3605ed1461036b57600080fd5b80633cb70c2d116100fd5780633cb70c2d146101f25780635c60da1b1461023e57806364d44b1e146102635780636f19d0b614610283578063705bf368146102ad578063715018a6146102e157600080fd5b8063089dc13b1461013a5780631a861d261461017757806323452b9c1461019b57806327ea6f2b146101b2578063360d0fad146101d2575b600080fd5b34801561014657600080fd5b5061015a61015536600461151c565b610488565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018357600080fd5b5061018d60055481565b60405190815260200161016e565b3480156101a757600080fd5b506101b06104ae565b005b3480156101be57600080fd5b506101b06101cd366004611559565b6104f3565b3480156101de57600080fd5b5061015a6101ed366004611572565b6105aa565b3480156101fe57600080fd5b506102316040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b60405161016e91906115e4565b34801561024a57600080fd5b5060035461015a9061010090046001600160a01b031681565b34801561026f57600080fd5b506101b061027e3660046115f7565b6105be565b34801561028f57600080fd5b5060035461029d9060ff1681565b604051901515815260200161016e565b3480156102b957600080fd5b5061015a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156102ed57600080fd5b506101b0610677565b34801561030257600080fd5b506101b06106ab565b34801561031757600080fd5b5060005461015a906001600160a01b031681565b34801561033757600080fd5b506000546001600160a01b0316331461029d565b34801561035757600080fd5b506101b0610366366004611619565b610762565b61015a610379366004611652565b610987565b34801561038a57600080fd5b5061015a7f000000000000000000000000000000000000000000000000000000000000000081565b3480156103be57600080fd5b506101b06103cd366004611719565b610b15565b3480156103de57600080fd5b5061029d6103ed366004611559565b60046020526000908152604090205460ff1681565b34801561040e57600080fd5b506001546001600160a01b0316331461029d565b34801561042e57600080fd5b506101b061043d366004611719565b610bf4565b34801561044e57600080fd5b506104767f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161016e565b60006104a833836000604051806020016040528060008152506000610c96565b92915050565b6000546001600160a01b031633146104e15760405162461bcd60e51b81526004016104d890611736565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461051d5760405162461bcd60e51b81526004016104d890611736565b600554604051639ea75f2760e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561058a57600080fd5b505af115801561059e573d6000803e3d6000fd5b50505060059190915550565b60006105b7838330610d1e565b9392505050565b6000546001600160a01b031633146105e85760405162461bcd60e51b81526004016104d890611736565b6040516302f848e760e11b815281151560048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146106a15760405162461bcd60e51b81526004016104d890611736565b6106a9610d7a565b565b6001546001600160a01b0316331461071a5760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016104d8565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c4919061176d565b6001600160a01b0316336001600160a01b0316146108135760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016104d8565b600061081d610dc4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f00000000000000000000000000000000000000000000000000000000000000008216916332c7791691869185169063a575185690602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b6919061176d565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b15801561090757600080fd5b505af115801561091b573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b50505050505050565b6000610991610e4f565b60008989600001518a602001518b61012001516040516020016109b7949392919061178a565b60408051601f1981840301815291815281516020928301206000818152600490935291205490915060ff1615610ad257600354610a039061010090046001600160a01b03168230610d1e565b91506000826001600160a01b03163b11610a575760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016104d8565b6040516321a6e7dd60e11b81526001600160a01b0383169063434dcfba903490610a89908990899089906004016117d2565b60206040518083038185885af1158015610aa7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610acc9190611807565b50610afe565b6000818152600460205260409020805460ff19166001179055610afb8a8a8a8a8a8a8a8a610ea6565b91505b50610b096001600255565b98975050505050505050565b6000546001600160a01b03163314610b3f5760405162461bcd60e51b81526004016104d890611736565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f000000000000000000000000000000000000000000000000000000000000000016906322fd9ed790604401600060405180830381600087803b158015610bb357600080fd5b505af1158015610bc7573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610c1e5760405162461bcd60e51b81526004016104d890611736565b6001600160a01b038116610c745760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016104d8565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000610ca28686610fbd565b60035460405162c76e0b60e11b81529192506001600160a01b0383169163018edc16913491610ce3918b918b918b918b918b9160ff909116906004016118f7565b6000604051808303818588803b158015610cfc57600080fd5b505af1158015610d10573d6000803e3d6000fd5b505050505095945050505050565b60405160388101919091526f5af43d82803e903d91602b57fd5bf3ff60248201526014810192909252733d602d80600a3d3981f3363d3d373d3d3d363d73825260588201526037600c8201206078820152605560439091012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e4a919061176d565b905090565b6002805403610ea05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104d8565b60028055565b6000808989600001518a602001518b6101200151604051602001610ecd949392919061178a565b604051602081830303815290604052805190602001209050610eef8a8a610fbd565b9150816001600160a01b031663240e6f89348c848b8b8e604051602001610f3693929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040528e8b8b8b600360009054906101000a900460ff16336040518b63ffffffff1660e01b8152600401610f7e9998979695949392919061194f565b6000604051808303818588803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50505050505098975050505050505050565b60e08101516000906001600160a01b03166110135760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016104d8565b6005541580611037575060008260c0015111801561103757506005548260c0015111155b6110735760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016104d8565b60035482516020808501516101208601516040516110c59561010090046001600160a01b0316946110aa948a94919391920161178a565b60405160208183030381529060405280519060200120611267565b905060006110d1610dc4565b90506001600160a01b038116156111dd5760e0830151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000000000000000000000000000000000000000000000909116906332c7791690606401600060405180830381600087803b15801561115b57600080fd5b505af115801561116f573d6000803e3d6000fd5b5050505060e083015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505b6040516306e9fe9960e41b81526001600160a01b03858116600483015283811660248301527f00000000000000000000000000000000000000000000000000000000000000001690636e9fe99090604401600060405180830381600087803b15801561124857600080fd5b505af115801561125c573d6000803e3d6000fd5b505050505092915050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166104a85760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016104d8565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561133e5761133e611304565b60405290565b600082601f83011261135557600080fd5b813567ffffffffffffffff8082111561137057611370611304565b604051601f8301601f19908116603f0116810190828211818310171561139857611398611304565b816040528381528660208588010111156113b157600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146113e657600080fd5b50565b80356113f4816113d1565b919050565b6000610140828403121561140c57600080fd5b61141461131a565b9050813567ffffffffffffffff8082111561142e57600080fd5b61143a85838601611344565b8352602084013591508082111561145057600080fd5b61145c85838601611344565b6020840152604084013591508082111561147557600080fd5b61148185838601611344565b6040840152606084013591508082111561149a57600080fd5b6114a685838601611344565b606084015260808401359150808211156114bf57600080fd5b506114cc84828501611344565b60808301525060a082013560a082015260c082013560c08201526114f260e083016113e9565b60e08201526101006115058184016113e9565b818301525061012080830135818301525092915050565b60006020828403121561152e57600080fd5b813567ffffffffffffffff81111561154557600080fd5b611551848285016113f9565b949350505050565b60006020828403121561156b57600080fd5b5035919050565b6000806040838503121561158557600080fd5b8235611590816113d1565b946020939093013593505050565b6000815180845260005b818110156115c4576020818501810151868301820152016115a8565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006105b7602083018461159e565b60006020828403121561160957600080fd5b813580151581146105b757600080fd5b6000806040838503121561162c57600080fd5b8235611637816113d1565b91506020830135611647816113d1565b809150509250929050565b600080600080600080600080610100898b03121561166f57600080fd5b883561167a816113d1565b9750602089013567ffffffffffffffff8082111561169757600080fd5b6116a38c838d016113f9565b985060408b0135915060ff821682146116bb57600080fd5b81975060608b0135965060808b013595506116d860a08c016113e9565b945060c08b01359150808211156116ee57600080fd5b506116fb8b828c01611344565b92505061170a60e08a016113e9565b90509295985092959890939650565b60006020828403121561172b57600080fd5b81356105b7816113d1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60006020828403121561177f57600080fd5b81516105b7816113d1565b6001600160a01b03851681526080602082018190526000906117ae9083018661159e565b82810360408401526117c0818661159e565b91505082606083015295945050505050565b600060018060a01b038086168352606060208401526117f4606084018661159e565b9150808416604084015250949350505050565b60006020828403121561181957600080fd5b5051919050565b600061014082518185526118368286018261159e565b91505060208301518482036020860152611850828261159e565b9150506040830151848203604086015261186a828261159e565b91505060608301518482036060860152611884828261159e565b9150506080830151848203608086015261189e828261159e565b91505060a083015160a085015260c083015160c085015260e08301516118cf60e08601826001600160a01b03169052565b50610100838101516001600160a01b0316908501526101209283015192909301919091525090565b600060018060a01b03808916835260c0602084015261191960c0840189611820565b81881660408501528381036060850152611933818861159e565b959091166080840152505090151560a090910152949350505050565b6001600160a01b038a81168252602082018a90526101206040830181905260009161197c8483018c61159e565b91508382036060850152611990828b611820565b9150808916608085015283820360a08501526119ac828961159e565b96811660c085015294151560e08401525050911661010090910152969550505050505056fea2646970667358221220994653b8c585513601d17ed8f59a59c1d00c08ad547d70fe216ece47cf44764e64736f6c6343000813003361020060405260016004556003610180523480156200001d57600080fd5b50604051620056c5380380620056c58339810160408190526200004091620003c6565b600160006040518060400160405280600f81526020016e57726974696e6745646974696f6e7360881b815250604051806040016040528060018152602001603160f81b81525081816200009e600083620002e160201b90919060201c565b61012052620000af816001620002e1565b61014052815160208084019190912060e052815190820120610100524660a0526200013d60e05161010051604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201529081019290925260608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60805250503060c05281518151818303601e9081015183860151601f85900360031b1b1760001992909301919091011002610160819052620001925760405163fa05c4d760e01b815260040160405180910390fd5b50620001a09050816200031a565b506003805460ff60a01b1916600160a01b831515021790555060016005556001600160a01b0383166200020d5760405162461bcd60e51b815260206004820152601060248201526f6d7573742073657420666163746f727960801b60448201526064015b60405180910390fd5b6001600160a01b038084166101a05282166200026c5760405162461bcd60e51b815260206004820152601f60248201527f6d7573742073657420747265617375727920636f6e66696775726174696f6e00604482015260640162000204565b6001600160a01b038083166101c0528116620002cb5760405162461bcd60e51b815260206004820152601660248201527f6d75737420736574206f62736572766162696c69747900000000000000000000604482015260640162000204565b6001600160a01b03166101e05250620005f69050565b60006020835110156200030157620002f98362000366565b905062000314565b816200030e8482620004b5565b5060ff90505b92915050565b600280546001600160a01b0319166001600160a01b0383169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b600080829050601f8151111562000394578260405163305a27a960e01b815260040162000204919062000581565b8051620003a182620005d1565b179392505050565b80516001600160a01b0381168114620003c157600080fd5b919050565b600080600060608486031215620003dc57600080fd5b620003e784620003a9565b9250620003f760208501620003a9565b91506200040760408501620003a9565b90509250925092565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200043b57607f821691505b6020821081036200045c57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620004b057600081815260208120601f850160051c810160208610156200048b5750805b601f850160051c820191505b81811015620004ac5782815560010162000497565b5050505b505050565b81516001600160401b03811115620004d157620004d162000410565b620004e981620004e2845462000426565b8462000462565b602080601f831160018114620005215760008415620005085750858301515b600019600386901b1c1916600185901b178555620004ac565b600085815260208120601f198616915b82811015620005525788860151825594840194600190910190840162000531565b5085821015620005715787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b600060208083528351808285015260005b81811015620005b05785810183015185820160400152820162000592565b506000604082860101526040601f19601f8301168501019250505092915050565b805160208083015191908110156200045c5760001960209190910360031b1b16919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c0516101e051614fdb620006ea600039600081816106ca01528181610e7801528181610fb801528181611259015281816115850152818161162a0152818161181101528181611b92015281816125ef0152612e350152600081816108d10152818161304301526130c901526000818161090501528181611deb01526123f101526000610a370152600081816109c301526122ba015260006114ea015260006114c001526000613959015260006139310152600061388c015260006138b6015260006138e00152614fdb6000f3fe6080604052600436106103505760003560e01c806370a08231116101c6578063a8d13a29116100f7578063e2e784d511610095578063ebc905571161006f578063ebc90557146109b1578063ed459df2146109e5578063f2fde38b14610a05578063ffa1ad7414610a2557600080fd5b8063e2e784d51461095c578063e8a3d4851461097c578063e985e9c51461099157600080fd5b8063bee0ec0b116100d1578063bee0ec0b146108bf578063c45a0155146108f3578063c7381da314610927578063c87b56dd1461093c57600080fd5b8063a8d13a2914610874578063b88d4fde14610889578063b9c9d93a146108a957600080fd5b80638f32d59b116101645780639c1fd6e01161013e5780639c1fd6e014610808578063a035b1fe14610828578063a22cb4651461083e578063a4d66daf1461085e57600080fd5b80638f32d59b146107b357806391b7f5ed146107d357806395d89b41146107f357600080fd5b806379ba5097116101a057806379ba50971461073657806384b0196e1461074b5780638ada6b0f146107735780638da5cb5b1461079357600080fd5b806370a08231146106ec578063715018a61461070c5780637284e4161461072157600080fd5b806327ea6f2b116102a05780634c00de821161023e5780636352211e116102185780636352211e146106575780636a627842146106775780636f19d0b614610697578063705bf368146106b857600080fd5b80634c00de82146106025780634dcd14b21461062257806356d3163d1461063757600080fd5b80633cb70c2d1161027a5780633cb70c2d1461058d57806342842e0e146105a2578063434dcfba146105c257806347591135146105d557600080fd5b806327ea6f2b146104fa5780632a55205a1461051a5780633b639e6f1461055957600080fd5b8063135d088d1161030d57806323452b9c116102e757806323452b9c1461049257806323b872dd146104a7578063240e6f89146104c7578063241d9651146104da57600080fd5b8063135d088d1461043957806318160ddd1461044e5780631bb534ba1461047257600080fd5b8063018edc161461035557806301ffc9a71461036a57806306fdde031461039f578063081812fc146103c1578063095ea7b3146103f957806309cf81c714610419575b600080fd5b610368610363366004613e4f565b610a6b565b005b34801561037657600080fd5b5061038a610385366004613f15565b610a93565b60405190151581526020015b60405180910390f35b3480156103ab57600080fd5b506103b4610b00565b6040516103969190613f82565b3480156103cd57600080fd5b506103e16103dc366004613f95565b610b8e565b6040516001600160a01b039091168152602001610396565b34801561040557600080fd5b50610368610414366004613fae565b610c28565b34801561042557600080fd5b506016546103e1906001600160a01b031681565b34801561044557600080fd5b506103b4610d3d565b34801561045a57600080fd5b50610464600d5481565b604051908152602001610396565b34801561047e57600080fd5b506012546103e1906001600160a01b031681565b34801561049e57600080fd5b50610368610d4a565b3480156104b357600080fd5b506103686104c2366004613fda565b610d86565b6103686104d536600461405c565b610db7565b3480156104e657600080fd5b506103686104f5366004614147565b610e24565b34801561050657600080fd5b50610368610515366004613f95565b610ef9565b34801561052657600080fd5b5061053a610535366004614164565b611024565b604080516001600160a01b039093168352602083019190915201610396565b34801561056557600080fd5b506104647f2ef51e411d530420ce5b1e9bccba7925a7d50a82a8ec0b09d8d096e59ebb9f7b81565b34801561059957600080fd5b506103b461105a565b3480156105ae57600080fd5b506103686105bd366004613fda565b611069565b6104646105d0366004614186565b611084565b3480156105e157600080fd5b506105f56105f03660046141e9565b6110c7565b604051610396919061428e565b34801561060e57600080fd5b506013546103e1906001600160a01b031681565b34801561062e57600080fd5b506103b461119c565b34801561064357600080fd5b50610368610652366004614147565b6111a9565b34801561066357600080fd5b506103e1610672366004613f95565b6112ba565b34801561068357600080fd5b50610464610692366004614147565b6112c5565b3480156106a357600080fd5b5060035461038a90600160a01b900460ff1681565b3480156106c457600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b3480156106f857600080fd5b50610464610707366004614147565b6112fb565b34801561071857600080fd5b50610368611382565b34801561072d57600080fd5b506103b46113b6565b34801561074257600080fd5b506103686113f8565b34801561075757600080fd5b506107606114b2565b60405161039697969594939291906142db565b34801561077f57600080fd5b506015546103e1906001600160a01b031681565b34801561079f57600080fd5b506002546103e1906001600160a01b031681565b3480156107bf57600080fd5b506002546001600160a01b0316331461038a565b3480156107df57600080fd5b506103686107ee366004613f95565b61153a565b3480156107ff57600080fd5b506103b46115f1565b34801561081457600080fd5b50610368610823366004614371565b6115fe565b34801561083457600080fd5b5061046460105481565b34801561084a57600080fd5b506103686108593660046143a5565b6116c2565b34801561086a57600080fd5b5061046460115481565b34801561088057600080fd5b50610368611786565b34801561089557600080fd5b506103686108a43660046143de565b6117b8565b3480156108b557600080fd5b5061046460145481565b3480156108cb57600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b3480156108ff57600080fd5b506103e17f000000000000000000000000000000000000000000000000000000000000000081565b34801561093357600080fd5b506103686117ca565b34801561094857600080fd5b506103b4610957366004613f95565b611887565b34801561096857600080fd5b50610368610977366004613fae565b611ac9565b34801561098857600080fd5b506103b4611c16565b34801561099d57600080fd5b5061038a6109ac36600461445d565b611cb7565b3480156109bd57600080fd5b506104647f000000000000000000000000000000000000000000000000000000000000000081565b3480156109f157600080fd5b506003546001600160a01b0316331461038a565b348015610a1157600080fd5b50610368610a20366004614147565b611ce5565b348015610a3157600080fd5b50610a597f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610396565b610a73611d87565b610a81868686868686611de0565b610a8b6001600555565b505050505050565b60006001600160e01b031982166380ac58cd60e01b1480610ac457506001600160e01b03198216635b5e139f60e01b145b80610adf57506001600160e01b031982166301ffc9a760e01b145b80610afa57506001600160e01b0319821663152a902d60e11b145b92915050565b600a8054610b0d9061448b565b80601f0160208091040260200160405190810160405280929190818152602001828054610b399061448b565b8015610b865780601f10610b5b57610100808354040283529160200191610b86565b820191906000526020600020905b815481529060010190602001808311610b6957829003601f168201915b505050505081565b6000818152600660205260408120546001600160a01b0316610c0c5760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b60648201526084015b60405180910390fd5b506000908152600860205260409020546001600160a01b031690565b6000610c3382611f13565b9050806001600160a01b0316836001600160a01b031603610ca05760405162461bcd60e51b815260206004820152602160248201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656044820152603960f91b6064820152608401610c03565b336001600160a01b0382161480610cbc5750610cbc8133611cb7565b610d2e5760405162461bcd60e51b815260206004820152603860248201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f7760448201527f6e6572206e6f7220617070726f76656420666f7220616c6c00000000000000006064820152608401610c03565b610d388383611f8a565b505050565b600f8054610b0d9061448b565b6002546001600160a01b03163314610d745760405162461bcd60e51b8152600401610c03906144bf565b600380546001600160a01b0319169055565b610d903382611ff8565b610dac5760405162461bcd60e51b8152600401610c03906144f6565b610d388383836120cf565b610dbf611d87565b610dcb8a8a8a8a61227a565b896001600160a01b0316816001600160a01b031614610e0057601680546001600160a01b0319166001600160a01b0383161790555b610e0e8a8787878787611de0565b610e186001600555565b50505050505050505050565b6002546001600160a01b03163314610e4e5760405162461bcd60e51b8152600401610c03906144bf565b60125460405163f0091f4f60e01b81526001600160a01b03918216600482015282821660248201527f00000000000000000000000000000000000000000000000000000000000000009091169063f0091f4f90604401600060405180830381600087803b158015610ebe57600080fd5b505af1158015610ed2573d6000803e3d6000fd5b5050601280546001600160a01b0319166001600160a01b0394909416939093179092555050565b6002546001600160a01b03163314610f235760405162461bcd60e51b8152600401610c03906144bf565b600d548110158015610f4057506011541580610f40575060115481105b610f975760405162461bcd60e51b815260206004820152602260248201527f6c696d6974206d757374206265203c207468616e2063757272656e74206c696d6044820152611a5d60f21b6064820152608401610c03565b60115460405163271c9e1f60e01b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063271c9e1f90604401600060405180830381600087803b15801561100457600080fd5b505af1158015611018573d6000803e3d6000fd5b50505060119190915550565b6013546014546001600160a01b039091169060009061271090611047908561455d565b611051919061458a565b90509250929050565b6060611064612349565b905090565b610d3883838360405180602001604052806000815250612475565b600354600090600160a01b900460ff16156110a1576110a16124a7565b6110a9611d87565b6110b48484846124d8565b90506110c06001600555565b9392505050565b606081516001600160401b038111156110e2576110e2613c20565b60405190808252806020026020018201604052801561110b578160200160208202803683370190505b50905060005b825181101561119657600660008483815181106111305761113061459e565b6020026020010151815260200190815260200160002060009054906101000a90046001600160a01b031682828151811061116c5761116c61459e565b6001600160a01b03909216602092830291909101909101528061118e816145b4565b915050611111565b50919050565b600e8054610b0d9061448b565b6002546001600160a01b031633146111d35760405162461bcd60e51b8152600401610c03906144bf565b6015546001600160a01b0316156112235760405162461bcd60e51b81526020600482015260146024820152731c995b99195c995c88185b1c9958591e481cd95d60621b6044820152606401610c03565b601580546001600160a01b0319166001600160a01b03838116918217909255604051631445778560e21b815260048101919091527f000000000000000000000000000000000000000000000000000000000000000090911690635115de1490602401600060405180830381600087803b15801561129f57600080fd5b505af11580156112b3573d6000803e3d6000fd5b5050505050565b6000610afa82611f13565b6002546000906001600160a01b031633146112f25760405162461bcd60e51b8152600401610c03906144bf565b610afa8261267f565b60006001600160a01b0382166113665760405162461bcd60e51b815260206004820152602a60248201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604482015269726f206164647265737360b01b6064820152608401610c03565b506001600160a01b031660009081526007602052604090205490565b6002546001600160a01b031633146113ac5760405162461bcd60e51b8152600401610c03906144bf565b6113b4612702565b565b60606113c0612349565b6113c94661274c565b6113d2306127de565b6040516020016113e4939291906145e9565b604051602081830303815290604052905090565b6003546001600160a01b031633146114675760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b6064820152608401610c03565b600380546001600160a01b0319908116909155600280543392168217905560405181907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3565b6000606080828080836114e57f000000000000000000000000000000000000000000000000000000000000000083612947565b6115107f00000000000000000000000000000000000000000000000000000000000000006001612947565b60408051600080825260208201909252600f60f81b9b939a50919850469750309650945092509050565b6002546001600160a01b031633146115645760405162461bcd60e51b8152600401610c03906144bf565b60105460405163509d812560e11b81526004810191909152602481018290527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063a13b024a90604401600060405180830381600087803b1580156115d157600080fd5b505af11580156115e5573d6000803e3d6000fd5b50505060109190915550565b600b8054610b0d9061448b565b6002546001600160a01b031633146116285760405162461bcd60e51b8152600401610c03906144bf565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663fc0ed66a61165f612349565b836040518363ffffffff1660e01b815260040161167d929190614639565b600060405180830381600087803b15801561169757600080fd5b505af11580156116ab573d6000803e3d6000fd5b5050505080600c90816116be91906146ad565b5050565b336001600160a01b0383160361171a5760405162461bcd60e51b815260206004820152601960248201527f4552433732313a20617070726f766520746f2063616c6c6572000000000000006044820152606401610c03565b3360008181526009602090815260408083206001600160a01b03871680855290835292819020805460ff191686151590811790915590519081529192917f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31910160405180910390a35050565b6002546001600160a01b031633146117b05760405162461bcd60e51b8152600401610c03906144bf565b6113b46129f2565b6117c484848484612475565b50505050565b6002546001600160a01b031633146117f45760405162461bcd60e51b8152600401610c03906144bf565b601154600d5460405163271c9e1f60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169263271c9e1f9261184d92600401918252602082015260400190565b600060405180830381600087803b15801561186757600080fd5b505af115801561187b573d6000803e3d6000fd5b5050600d546011555050565b6000818152600660205260409020546060906001600160a01b03166118fa5760405162461bcd60e51b815260206004820152602360248201527f4552433732313a20717565727920666f72206e6f6e6578697374656e7420746f60448201526235b2b760e91b6064820152608401610c03565b6015546001600160a01b03161561197c5760155460405163c87b56dd60e01b8152600481018490526001600160a01b039091169063c87b56dd90602401600060405180830381865afa158015611954573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610afa919081019061476c565b60606011546000146119b55761199360115461274c565b6040516020016119a391906147d9565b60405160208183030381529060405290505b6000611a9e611a4d600a80546119ca9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546119f69061448b565b8015611a435780601f10611a1857610100808354040283529160200191611a43565b820191906000526020600020905b815481529060010190602001808311611a2657829003601f168201915b5050505050612a09565b611a568661274c565b84611a67611a626113b6565b612a09565b600e600f611a748b61274c565b604051602001611a8a9796959493929190614875565b604051602081830303815290604052612bbf565b905080604051602001611ab191906149d1565b60405160208183030381529060405292505050919050565b6002546001600160a01b03163314611af35760405162461bcd60e51b8152600401610c03906144bf565b612710811115611b565760405162461bcd60e51b815260206004820152602860248201527f627073206d757374206265206c657373207468616e206f7220657175616c207460448201526706f2031302c3030360c41b6064820152608401610c03565b601354601454604051630ba1ba8360e01b81526001600160a01b03928316600482015260248101919091528382166044820152606481018390527f000000000000000000000000000000000000000000000000000000000000000090911690630ba1ba8390608401600060405180830381600087803b158015611bd857600080fd5b505af1158015611bec573d6000803e3d6000fd5b5050601380546001600160a01b0319166001600160a01b0395909516949094179093555060145550565b6015546060906001600160a01b031615611caf57601560009054906101000a90046001600160a01b03166001600160a01b031663e8a3d4856040518163ffffffff1660e01b8152600401600060405180830381865afa925050508015611c9e57506040513d6000823e601f3d908101601f19168201604052611c9b919081019061476c565b60015b611caa57611064612d11565b919050565b611064612d11565b6001600160a01b03918216600090815260096020908152604080832093909416825291909152205460ff1690565b6002546001600160a01b03163314611d0f5760405162461bcd60e51b8152600401610c03906144bf565b6001600160a01b038116611d655760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e006044820152606401610c03565b600380546001600160a01b0319166001600160a01b0392909216919091179055565b600260055403611dd95760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610c03565b6002600555565b336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614611e4e5760405162461bcd60e51b81526020600482015260136024820152723ab730baba3437b934bd32b21031b0b63632b960691b6044820152606401610c03565b8451600a90611e5d90826146ad565b506020850151600b90611e7090826146ad565b506060850151600f90611e8390826146ad565b506080850151600e90611e9690826146ad565b5060a085015160105560c085015160115560e0850151601280546001600160a01b039283166001600160a01b03199182161790915561010087015160158054919093169116179055611ee786612d9d565b611ef081612de9565b6001600160a01b03841615610a8b57611f0a8484846124d8565b50505050505050565b6000818152600660205260408120546001600160a01b031680610afa5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a206f776e657220717565727920666f72206e6f6e657869737460448201526832b73a103a37b5b2b760b91b6064820152608401610c03565b600081815260086020526040902080546001600160a01b0319166001600160a01b0384169081179091558190611fbf82611f13565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b6000818152600660205260408120546001600160a01b03166120715760405162461bcd60e51b815260206004820152602c60248201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860448201526b34b9ba32b73a103a37b5b2b760a11b6064820152608401610c03565b600061207c83611f13565b9050806001600160a01b0316846001600160a01b031614806120b75750836001600160a01b03166120ac84610b8e565b6001600160a01b0316145b806120c757506120c78185611cb7565b949350505050565b826001600160a01b03166120e282611f13565b6001600160a01b03161461214a5760405162461bcd60e51b815260206004820152602960248201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960448201526839903737ba1037bbb760b91b6064820152608401610c03565b6001600160a01b0382166121ac5760405162461bcd60e51b8152602060048201526024808201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646044820152637265737360e01b6064820152608401610c03565b6121b7838383612e07565b6121c2600082611f8a565b6001600160a01b03831660009081526007602052604081208054600192906121eb908490614a16565b90915550506001600160a01b0382166000908152600760205260408120805460019290612219908490614a29565b909155505060008181526006602052604080822080546001600160a01b0319166001600160a01b0386811691821790925591518493918716917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91a4505050565b61228683858484612e8d565b6122a35760405163120d188f60e31b815260040160405180910390fd5b6040805160808101825260008082529181018281527f0000000000000000000000000000000000000000000000000000000000000000601f830152815182016020018051605f84015283905280518201606001839052829190915091507f06c5a80e592816bd4f60093568e69affa68b5e378a189b2f59a1121703de47de85838389888860405161233996959493929190614a65565b60405180910390a1505050505050565b6060600c80546123589061448b565b1590506123ef57600c805461236c9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546123989061448b565b80156123e55780601f106123ba576101008083540402835291602001916123e5565b820191906000526020600020905b8154815290600101906020018083116123c857829003601f168201915b5050505050905090565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316633cb70c2d6040518163ffffffff1660e01b8152600401600060405180830381865afa15801561244d573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611064919081019061476c565b61247f3383611ff8565b61249b5760405162461bcd60e51b8152600401610c03906144f6565b6117c48484848461300c565b60005a60045490915060005a6004549091506124c38285614a16565b5a6124ce9084614a16565b106117c457600080fd5b60006124e38461267f565b90506000806124f061303f565b90506001600160a01b038116158015906125695750806001600160a01b031663da8b5a566040518163ffffffff1660e01b81526004016020604051808303816000875af1158015612545573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125699190614ac1565b156125d557806001600160a01b031663ff0c44da6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156125ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906125d29190614ade565b91505b60105460405163ca9d19eb60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163ca9d19eb9161262b9187918b91908b908990600401614af7565b600060405180830381600087803b15801561264557600080fd5b505af1158015612659573d6000803e3d6000fd5b505060125461267692506001600160a01b031690503484876130c5565b50509392505050565b6000600d60008154612690906145b4565b918290555060115490915015806126aa5750600019601154145b806126c157506011546126be906001614a29565b81105b6126f85760405162461bcd60e51b81526020600482015260086024820152671cdbdb19081bdd5d60c21b6044820152606401610c03565b611caa828261338d565b6002546040516000916001600160a01b0316907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600280546001600160a01b0319169055565b60606000612759836134db565b60010190506000816001600160401b0381111561277857612778613c20565b6040519080825280601f01601f1916602001820160405280156127a2576020820181803683370190505b5090508181016020015b600019016f181899199a1a9b1b9c1cb0b131b232b360811b600a86061a8153600a85049450846127ac57509392505050565b60408051602880825260608281019093526000919060208201818036833701905050905060005b601481101561291e57600061281b826013614a16565b61282690600861455d565b612831906002614c1a565b612844906001600160a01b03871661458a565b60f81b9050600060108260f81c61285b9190614c26565b60f81b905060008160f81c60106128729190614c48565b8360f81c6128809190614c6b565b60f81b905061288e826135b3565b8561289a86600261455d565b815181106128aa576128aa61459e565b60200101906001600160f81b031916908160001a9053506128ca816135b3565b856128d686600261455d565b6128e1906001614a29565b815181106128f1576128f161459e565b60200101906001600160f81b031916908160001a9053505050508080612916906145b4565b915050612805565b50806040516020016129309190614c84565b604051602081830303815290604052915050919050565b606060ff83146129615761295a836135e9565b9050610afa565b81805461296d9061448b565b80601f01602080910402602001604051908101604052809291908181526020018280546129999061448b565b80156129e65780601f106129bb576101008083540402835291602001916129e6565b820191906000526020600020905b8154815290600101906020018083116129c957829003601f168201915b50505050509050610afa565b6003546113b490600160a01b900460ff1615612de9565b6060816000805b82518160ff161015612a6a57828160ff1681518110612a3157612a3161459e565b01602001516001600160f81b031916601160f91b03612a585781612a5481614cae565b9250505b80612a6281614cae565b915050612a10565b5060ff811615612bb75760008160ff168351612a869190614a29565b6001600160401b03811115612a9d57612a9d613c20565b6040519080825280601f01601f191660200182016040528015612ac7576020820181803683370190505b5090506000805b84518160ff161015612bac57848160ff1681518110612aef57612aef61459e565b01602001516001600160f81b031916601160f91b03612b4257601760fa1b8383612b18816145b4565b945081518110612b2a57612b2a61459e565b60200101906001600160f81b031916908160001a9053505b848160ff1681518110612b5757612b5761459e565b01602001516001600160f81b0319168383612b71816145b4565b945081518110612b8357612b8361459e565b60200101906001600160f81b031916908160001a90535080612ba481614cae565b915050612ace565b509095945050505050565b509192915050565b60608151600003612bde57505060408051602081019091526000815290565b6000604051806060016040528060408152602001614f666040913990506000600384516002612c0d9190614a29565b612c17919061458a565b612c2290600461455d565b6001600160401b03811115612c3957612c39613c20565b6040519080825280601f01601f191660200182016040528015612c63576020820181803683370190505b509050600182016020820185865187015b80821015612ccf576003820191508151603f8160121c168501518453600184019350603f81600c1c168501518453600184019350603f8160061c168501518453600184019350603f8116850151845350600183019250612c74565b5050600386510660018114612ceb5760028114612cfe57612d06565b603d6001830353603d6002830353612d06565b603d60018303535b509195945050505050565b60606000612d75612d28600a80546119ca9061448b565b612d33611a626113b6565b600e600f612d4260145461274c565b601354612d57906001600160a01b03166127de565b612d5f612349565b604051602001611a8a9796959493929190614ccd565b905080604051602001612d8891906149d1565b60405160208183030381529060405291505090565b600280546001600160a01b0319166001600160a01b0383169081179091556040516000907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908290a350565b60038054911515600160a01b0260ff60a01b19909216919091179055565b60405163536a4ee560e11b81526001600160a01b0384811660048301528381166024830152604482018390527f0000000000000000000000000000000000000000000000000000000000000000169063a6d49dca90606401600060405180830381600087803b158015612e7957600080fd5b505af1158015611f0a573d6000803e3d6000fd5b60006001600160a01b038416612eb6576040516332db94d160e21b815260040160405180910390fd5b604080517f2ef51e411d530420ce5b1e9bccba7925a7d50a82a8ec0b09d8d096e59ebb9f7b6020820152908101869052600090612f0b9060600160405160208183030381529060405280519060200120613628565b90506001600160a01b0385163b15612fb057604051630b135d3f60e11b81526001600160a01b03861690631626ba7e90612f4d90849088908890600401614e1d565b602060405180830381865afa925050508015612f86575060408051601f3d908101601f19168201909252612f8391810190614e37565b60015b612f945760009150506120c7565b6001600160e01b031916630b135d3f60e11b1491506120c79050565b6000612ff28286868080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525061365592505050565b6001600160a01b0387811691161492505050949350505050565b6130178484846120cf565b61302384848484613679565b6117c45760405162461bcd60e51b8152600401610c0390614e54565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166398c47e8c6040518163ffffffff1660e01b81526004016020604051808303816000875af11580156130a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110649190614ea6565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166361d027b36040518163ffffffff1660e01b81526004016020604051808303816000875af1158015613127573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061314b9190614ea6565b9050821580159061316457506001600160a01b03811615155b1561334157826010546131779190614a29565b84146131b65760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c03565b60105460405163136171d360e21b81526001600160a01b038416600482015290156024820152604481018490526000908190819081907377bcbbb7783ea8c43ae1893e35c76daf7ec153e390634d85c74c90606401608060405180830381865af4158015613228573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061324c9190614ec3565b601654939750919550935091506001600160a01b031661328d576132888982866010546132799190614a29565b6132839190614a29565b613777565b6132b5565b6016546132a3906001600160a01b031682613777565b6132b589856010546132839190614a29565b6132bf8584613777565b6016546040805186815260208101869052808201859052606081018490526001600160a01b038c8116608083015289811660a083015290921660c0830152517f8b181338417e2725cdc77ffb698b4e26f936882171329a7de03b0e3d3f8266849181900360e00190a18115613338576133388683613777565b505050506112b3565b60105484146133835760405162461bcd60e51b815260206004820152600e60248201526d1a5b9d985b1a5908185b5bdd5b9d60921b6044820152606401610c03565b6112b38585613777565b6001600160a01b0382166133e35760405162461bcd60e51b815260206004820181905260248201527f4552433732313a206d696e7420746f20746865207a65726f20616464726573736044820152606401610c03565b6000818152600660205260409020546001600160a01b0316156134485760405162461bcd60e51b815260206004820152601c60248201527f4552433732313a20746f6b656e20616c7265616479206d696e746564000000006044820152606401610c03565b61345460008383612e07565b6001600160a01b038216600090815260076020526040812080546001929061347d908490614a29565b909155505060008181526006602052604080822080546001600160a01b0319166001600160a01b03861690811790915590518392907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef908290a45050565b60008072184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b831061351a5772184f03e93ff9f4daa797ed6e38ed64bf6a1f0160401b830492506040015b6d04ee2d6d415b85acef81000000008310613546576d04ee2d6d415b85acef8100000000830492506020015b662386f26fc10000831061356457662386f26fc10000830492506010015b6305f5e100831061357c576305f5e100830492506008015b612710831061359057612710830492506004015b606483106135a2576064830492506002015b600a8310610afa5760010192915050565b6000600a60f883901c10156135da576135d160f883901c6030614ef9565b60f81b92915050565b6135d160f883901c6057614ef9565b606060006135f683613857565b604080516020808252818301909252919250600091906020820181803683375050509182525060208101929092525090565b6000610afa61363561387f565b8360405161190160f01b8152600281019290925260228201526042902090565b600080600061366485856139aa565b91509150613671816139ef565b509392505050565b60006001600160a01b0384163b1561376f57604051630a85bd0160e11b81526001600160a01b0385169063150b7a02906136bd903390899088908890600401614f12565b6020604051808303816000875af19250505080156136f8575060408051601f3d908101601f191682019092526136f591810190614e37565b60015b613755573d808015613726576040519150601f19603f3d011682016040523d82523d6000602084013e61372b565b606091505b50805160000361374d5760405162461bcd60e51b8152600401610c0390614e54565b805181602001fd5b6001600160e01b031916630a85bd0160e11b1490506120c7565b5060016120c7565b804710156137be5760405162461bcd60e51b8152602060048201526014602482015273696e73756666696369656e742062616c616e636560601b6044820152606401610c03565b6000826001600160a01b0316825a6040519091906000818181858888f193505050503d806000811461380c576040519150601f19603f3d011682016040523d82523d6000602084013e613811565b606091505b5050905080610d385760405162461bcd60e51b81526020600482015260126024820152711c9958da5c1a595b9d081c995d995c9d195960721b6044820152606401610c03565b600060ff8216601f811115610afa57604051632cd44ac360e21b815260040160405180910390fd5b6000306001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161480156138d857507f000000000000000000000000000000000000000000000000000000000000000046145b1561390257507f000000000000000000000000000000000000000000000000000000000000000090565b611064604080517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60208201527f0000000000000000000000000000000000000000000000000000000000000000918101919091527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260009060c00160405160208183030381529060405280519060200120905090565b60008082516041036139e05760208301516040840151606085015160001a6139d487828585613b3c565b945094505050506139e8565b506000905060025b9250929050565b6000816004811115613a0357613a03614f4f565b03613a0b5750565b6001816004811115613a1f57613a1f614f4f565b03613a6c5760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610c03565b6002816004811115613a8057613a80614f4f565b03613acd5760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610c03565b6003816004811115613ae157613ae1614f4f565b03613b395760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610c03565b50565b6000807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0831115613b735750600090506003613bf7565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613bc7573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b038116613bf057600060019250925050613bf7565b9150600090505b94509492505050565b6001600160a01b0381168114613b3957600080fd5b8035611caa81613c00565b634e487b7160e01b600052604160045260246000fd5b60405161014081016001600160401b0381118282101715613c5957613c59613c20565b60405290565b604051601f8201601f191681016001600160401b0381118282101715613c8757613c87613c20565b604052919050565b60006001600160401b03821115613ca857613ca8613c20565b50601f01601f191660200190565b6000613cc9613cc484613c8f565b613c5f565b9050828152838383011115613cdd57600080fd5b828260208301376000602084830101529392505050565b600082601f830112613d0557600080fd5b6110c083833560208501613cb6565b60006101408284031215613d2757600080fd5b613d2f613c36565b905081356001600160401b0380821115613d4857600080fd5b613d5485838601613cf4565b83526020840135915080821115613d6a57600080fd5b613d7685838601613cf4565b60208401526040840135915080821115613d8f57600080fd5b613d9b85838601613cf4565b60408401526060840135915080821115613db457600080fd5b613dc085838601613cf4565b60608401526080840135915080821115613dd957600080fd5b50613de684828501613cf4565b60808301525060a082013560a082015260c082013560c0820152613e0c60e08301613c15565b60e0820152610100613e1f818401613c15565b818301525061012080830135818301525092915050565b8015158114613b3957600080fd5b8035611caa81613e36565b60008060008060008060c08789031215613e6857600080fd5b8635613e7381613c00565b955060208701356001600160401b0380821115613e8f57600080fd5b613e9b8a838b01613d14565b965060408901359150613ead82613c00565b90945060608801359080821115613ec357600080fd5b50613ed089828a01613cf4565b9350506080870135613ee181613c00565b915060a0870135613ef181613e36565b809150509295509295509295565b6001600160e01b031981168114613b3957600080fd5b600060208284031215613f2757600080fd5b81356110c081613eff565b60005b83811015613f4d578181015183820152602001613f35565b50506000910152565b60008151808452613f6e816020860160208601613f32565b601f01601f19169290920160200192915050565b6020815260006110c06020830184613f56565b600060208284031215613fa757600080fd5b5035919050565b60008060408385031215613fc157600080fd5b8235613fcc81613c00565b946020939093013593505050565b600080600060608486031215613fef57600080fd5b8335613ffa81613c00565b9250602084013561400a81613c00565b929592945050506040919091013590565b60008083601f84011261402d57600080fd5b5081356001600160401b0381111561404457600080fd5b6020830191508360208285010111156139e857600080fd5b6000806000806000806000806000806101208b8d03121561407c57600080fd5b6140858b613c15565b995060208b0135985060408b01356001600160401b03808211156140a857600080fd5b6140b48e838f0161401b565b909a50985060608d01359150808211156140cd57600080fd5b6140d98e838f01613d14565b97506140e760808e01613c15565b965060a08d01359150808211156140fd57600080fd5b5061410a8d828e01613cf4565b94505061411960c08c01613c15565b925061412760e08c01613e44565b91506141366101008c01613c15565b90509295989b9194979a5092959850565b60006020828403121561415957600080fd5b81356110c081613c00565b6000806040838503121561417757600080fd5b50508035926020909101359150565b60008060006060848603121561419b57600080fd5b83356141a681613c00565b925060208401356001600160401b038111156141c157600080fd5b6141cd86828701613cf4565b92505060408401356141de81613c00565b809150509250925092565b600060208083850312156141fc57600080fd5b82356001600160401b038082111561421357600080fd5b818501915085601f83011261422757600080fd5b81358181111561423957614239613c20565b8060051b915061424a848301613c5f565b818152918301840191848101908884111561426457600080fd5b938501935b8385101561428257843582529385019390850190614269565b98975050505050505050565b6020808252825182820181905260009190848201906040850190845b818110156142cf5783516001600160a01b0316835292840192918401916001016142aa565b50909695505050505050565b60ff60f81b881681526000602060e0818401526142fb60e084018a613f56565b838103604085015261430d818a613f56565b606085018990526001600160a01b038816608086015260a0850187905284810360c0860152855180825283870192509083019060005b8181101561435f57835183529284019291840191600101614343565b50909c9b505050505050505050505050565b60006020828403121561438357600080fd5b81356001600160401b0381111561439957600080fd5b6120c784828501613cf4565b600080604083850312156143b857600080fd5b82356143c381613c00565b915060208301356143d381613e36565b809150509250929050565b600080600080608085870312156143f457600080fd5b84356143ff81613c00565b9350602085013561440f81613c00565b92506040850135915060608501356001600160401b0381111561443157600080fd5b8501601f8101871361444257600080fd5b61445187823560208401613cb6565b91505092959194509250565b6000806040838503121561447057600080fd5b823561447b81613c00565b915060208301356143d381613c00565b600181811c9082168061449f57607f821691505b60208210810361119657634e487b7160e01b600052602260045260246000fd5b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610afa57610afa614547565b634e487b7160e01b600052601260045260246000fd5b60008261459957614599614574565b500490565b634e487b7160e01b600052603260045260246000fd5b6000600182016145c6576145c6614547565b5060010190565b600081516145df818560208601613f32565b9290920192915050565b600084516145fb818460208901613f32565b84519083019061460f818360208901613f32565b602f60f81b9101908152835161462c816001840160208801613f32565b0160010195945050505050565b60408152600061464c6040830185613f56565b828103602084015261465e8185613f56565b95945050505050565b601f821115610d3857600081815260208120601f850160051c8101602086101561468e5750805b601f850160051c820191505b81811015610a8b5782815560010161469a565b81516001600160401b038111156146c6576146c6613c20565b6146da816146d4845461448b565b84614667565b602080601f83116001811461470f57600084156146f75750858301515b600019600386901b1c1916600185901b178555610a8b565b600085815260208120601f198616915b8281101561473e5788860151825594840194600190910190840161471f565b508582101561475c5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121561477e57600080fd5b81516001600160401b0381111561479457600080fd5b8201601f810184136147a557600080fd5b80516147b3613cc482613c8f565b8181528560208385010111156147c857600080fd5b61465e826020830160208601613f32565b602f60f81b8152600082516147f5816001850160208701613f32565b9190910160010192915050565b6000815461480f8161448b565b60018281168015614827576001811461483c5761486b565b60ff198416875282151583028701945061486b565b8560005260208060002060005b858110156148625781548a820152908401908201614849565b50505082870194505b5050505092915050565b693d913730b6b2911d101160b11b8152875160009061489b81600a850160208d01613f32565b600160fd1b600a9184019182015288516148bc81600b840160208d01613f32565b88519101906148d281600b840160208c01613f32565b72111610113232b9b1b934b83a34b7b7111d101160691b600b9290910191820152865161490681601e840160208b01613f32565b73222c2022636f6e74656e74223a202261723a2f2f60601b601e92909101918201526149356032820187614802565b73222c2022696d616765223a2022697066733a2f2f60601b815290506149c36149b36149ad6149676014850189614802565b7f222c202261747472696275746573223a5b7b202274726169745f74797065223a8152730101129b2b934b0b6111610113b30b63ab2911d160651b602082015260340190565b866145cd565b637d5d207d60e01b815260040190565b9a9950505050505050505050565b7f646174613a6170706c69636174696f6e2f6a736f6e3b6261736536342c000000815260008251614a0981601d850160208701613f32565b91909101601d0192915050565b81810381811115610afa57610afa614547565b80820180821115610afa57610afa614547565b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b86815260a060208201526000614a7e60a0830188613f56565b8281036040840152614a908188613f56565b6001600160a01b038716606085015283810360808501529050614ab4818587614a3c565b9998505050505050505050565b600060208284031215614ad357600080fd5b81516110c081613e36565b600060208284031215614af057600080fd5b5051919050565b85815260018060a01b038516602082015283604082015260a060608201526000614b2460a0830185613f56565b90508260808301529695505050505050565b600181815b80851115614b71578160001904821115614b5757614b57614547565b80851615614b6457918102915b93841c9390800290614b3b565b509250929050565b600082614b8857506001610afa565b81614b9557506000610afa565b8160018114614bab5760028114614bb557614bd1565b6001915050610afa565b60ff841115614bc657614bc6614547565b50506001821b610afa565b5060208310610133831016604e8410600b8410161715614bf4575081810a610afa565b614bfe8383614b36565b8060001904821115614c1257614c12614547565b029392505050565b60006110c08383614b79565b600060ff831680614c3957614c39614574565b8060ff84160491505092915050565b60ff8181168382160290811690818114614c6457614c64614547565b5092915050565b60ff8281168282160390811115610afa57610afa614547565b61060f60f31b815260008251614ca1816002850160208701613f32565b9190910160020192915050565b600060ff821660ff8103614cc457614cc4614547565b60010192915050565b693d913730b6b2911d101160b11b81528751600090614cf381600a850160208d01613f32565b72111610113232b9b1b934b83a34b7b7111d101160691b600a918401918201528851614d2681601d840160208d01613f32565b73222c2022636f6e74656e74223a202261723a2f2f60601b601d9290910191820152614d556031820189614802565b73222c2022696d616765223a2022697066733a2f2f60601b81529050614d7e6014820188614802565b90507f222c202273656c6c65725f6665655f62617369735f706f696e7473223a20000081528551614db681601e840160208a01613f32565b731610113332b2afb932b1b4b834b2b73a111d101160611b601e92909101918201526149c3614e0f6149ad614dee60328501896145cd565b741116101132bc3a32b93730b62fb634b735911d101160591b815260150190565b61227d60f01b815260020190565b83815260406020820152600061465e604083018486614a3c565b600060208284031215614e4957600080fd5b81516110c081613eff565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b600060208284031215614eb857600080fd5b81516110c081613c00565b60008060008060808587031215614ed957600080fd5b505082516020840151604085015160609095015191969095509092509050565b60ff8181168382160190811115610afa57610afa614547565b6001600160a01b0385811682528416602082015260408101839052608060608201819052600090614f4590830184613f56565b9695505050505050565b634e487b7160e01b600052602160045260246000fdfe4142434445464748494a4b4c4d4e4f505152535455565758595a6162636465666768696a6b6c6d6e6f707172737475767778797a303132333435363738392b2fa2646970667358221220cb7e837f88fa7481fe3fa18d8b035389e972d63b2a6e6bd8fa67f823ce6c622164736f6c63430008130033000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
Deployed Bytecode
0x6080604052600436106101355760003560e01c806379ba5097116100ab578063bee0ec0b1161006f578063bee0ec0b1461037e578063d784d426146103b2578063e6d283a6146103d2578063ed459df214610402578063f2fde38b14610422578063ffa1ad741461044257600080fd5b806379ba5097146102f65780638da5cb5b1461030b5780638f32d59b1461032b578063a56600631461034b578063ae3605ed1461036b57600080fd5b80633cb70c2d116100fd5780633cb70c2d146101f25780635c60da1b1461023e57806364d44b1e146102635780636f19d0b614610283578063705bf368146102ad578063715018a6146102e157600080fd5b8063089dc13b1461013a5780631a861d261461017757806323452b9c1461019b57806327ea6f2b146101b2578063360d0fad146101d2575b600080fd5b34801561014657600080fd5b5061015a61015536600461151c565b610488565b6040516001600160a01b0390911681526020015b60405180910390f35b34801561018357600080fd5b5061018d60055481565b60405190815260200161016e565b3480156101a757600080fd5b506101b06104ae565b005b3480156101be57600080fd5b506101b06101cd366004611559565b6104f3565b3480156101de57600080fd5b5061015a6101ed366004611572565b6105aa565b3480156101fe57600080fd5b506102316040518060400160405280601381526020017268747470733a2f2f6d6972726f722e78797a2f60681b81525081565b60405161016e91906115e4565b34801561024a57600080fd5b5060035461015a9061010090046001600160a01b031681565b34801561026f57600080fd5b506101b061027e3660046115f7565b6105be565b34801561028f57600080fd5b5060035461029d9060ff1681565b604051901515815260200161016e565b3480156102b957600080fd5b5061015a7f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07281565b3480156102ed57600080fd5b506101b0610677565b34801561030257600080fd5b506101b06106ab565b34801561031757600080fd5b5060005461015a906001600160a01b031681565b34801561033757600080fd5b506000546001600160a01b0316331461029d565b34801561035757600080fd5b506101b0610366366004611619565b610762565b61015a610379366004611652565b610987565b34801561038a57600080fd5b5061015a7f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde681565b3480156103be57600080fd5b506101b06103cd366004611719565b610b15565b3480156103de57600080fd5b5061029d6103ed366004611559565b60046020526000908152604090205460ff1681565b34801561040e57600080fd5b506001546001600160a01b0316331461029d565b34801561042e57600080fd5b506101b061043d366004611719565b610bf4565b34801561044e57600080fd5b506104767f000000000000000000000000000000000000000000000000000000000000000381565b60405160ff909116815260200161016e565b60006104a833836000604051806020016040528060008152506000610c96565b92915050565b6000546001600160a01b031633146104e15760405162461bcd60e51b81526004016104d890611736565b60405180910390fd5b600180546001600160a01b0319169055565b6000546001600160a01b0316331461051d5760405162461bcd60e51b81526004016104d890611736565b600554604051639ea75f2760e01b81526004810191909152602481018290527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b031690639ea75f2790604401600060405180830381600087803b15801561058a57600080fd5b505af115801561059e573d6000803e3d6000fd5b50505060059190915550565b60006105b7838330610d1e565b9392505050565b6000546001600160a01b031633146105e85760405162461bcd60e51b81526004016104d890611736565b6040516302f848e760e11b815281151560048201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0726001600160a01b0316906305f091ce90602401600060405180830381600087803b15801561064b57600080fd5b505af115801561065f573d6000803e3d6000fd5b50506003805460ff1916931515939093179092555050565b6000546001600160a01b031633146106a15760405162461bcd60e51b81526004016104d890611736565b6106a9610d7a565b565b6001546001600160a01b0316331461071a5760405162461bcd60e51b815260206004820152602c60248201527f63757272656e74206f776e6572206d757374207365742063616c6c657220617360448201526b103732bc3a1037bbb732b91760a11b60648201526084016104d8565b600180546001600160a01b0319908116909155600080543392168217815560405182917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a3565b816001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156107a0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107c4919061176d565b6001600160a01b0316336001600160a01b0316146108135760405162461bcd60e51b815260206004820152600c60248201526b1d5b985d5d1a1bdc9a5e995960a21b60448201526064016104d8565b600061081d610dc4565b6040516352ba8c2b60e11b81526001600160a01b0385811660048301529192507f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0728216916332c7791691869185169063a575185690602401602060405180830381865afa158015610892573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108b6919061176d565b6040516001600160e01b031960e085901b1681526001600160a01b03928316600482015290821660248201529085166044820152606401600060405180830381600087803b15801561090757600080fd5b505af115801561091b573d6000803e3d6000fd5b505060405163a566006360e01b81526001600160a01b03868116600483015285811660248301528416925063a56600639150604401600060405180830381600087803b15801561096a57600080fd5b505af115801561097e573d6000803e3d6000fd5b50505050505050565b6000610991610e4f565b60008989600001518a602001518b61012001516040516020016109b7949392919061178a565b60408051601f1981840301815291815281516020928301206000818152600490935291205490915060ff1615610ad257600354610a039061010090046001600160a01b03168230610d1e565b91506000826001600160a01b03163b11610a575760405162461bcd60e51b8152602060048201526015602482015274696e76616c696420636c6f6e65206164647265737360581b60448201526064016104d8565b6040516321a6e7dd60e11b81526001600160a01b0383169063434dcfba903490610a89908990899089906004016117d2565b60206040518083038185885af1158015610aa7573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610acc9190611807565b50610afe565b6000818152600460205260409020805460ff19166001179055610afb8a8a8a8a8a8a8a8a610ea6565b91505b50610b096001600255565b98975050505050505050565b6000546001600160a01b03163314610b3f5760405162461bcd60e51b81526004016104d890611736565b6003546040516322fd9ed760e01b81526101009091046001600160a01b03908116600483015282811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07216906322fd9ed790604401600060405180830381600087803b158015610bb357600080fd5b505af1158015610bc7573d6000803e3d6000fd5b5050600380546001600160a01b0390941661010002610100600160a81b0319909416939093179092555050565b6000546001600160a01b03163314610c1e5760405162461bcd60e51b81526004016104d890611736565b6001600160a01b038116610c745760405162461bcd60e51b815260206004820152601f60248201527f4e657874206f776e657220697320746865207a65726f20616464726573732e0060448201526064016104d8565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6000610ca28686610fbd565b60035460405162c76e0b60e11b81529192506001600160a01b0383169163018edc16913491610ce3918b918b918b918b918b9160ff909116906004016118f7565b6000604051808303818588803b158015610cfc57600080fd5b505af1158015610d10573d6000803e3d6000fd5b505050505095945050505050565b60405160388101919091526f5af43d82803e903d91602b57fd5bf3ff60248201526014810192909252733d602d80600a3d3981f3363d3d373d3d3d363d73825260588201526037600c8201206078820152605560439091012090565b600080546040516001600160a01b03909116907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0908390a3600080546001600160a01b0319169055565b60007f000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde66001600160a01b031663d6811b6f6040518163ffffffff1660e01b81526004016020604051808303816000875af1158015610e26573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e4a919061176d565b905090565b6002805403610ea05760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016104d8565b60028055565b6000808989600001518a602001518b6101200151604051602001610ecd949392919061178a565b604051602081830303815290604052805190602001209050610eef8a8a610fbd565b9150816001600160a01b031663240e6f89348c848b8b8e604051602001610f3693929190928352602083019190915260f81b6001600160f81b031916604082015260410190565b6040516020818303038152906040528e8b8b8b600360009054906101000a900460ff16336040518b63ffffffff1660e01b8152600401610f7e9998979695949392919061194f565b6000604051808303818588803b158015610f9757600080fd5b505af1158015610fab573d6000803e3d6000fd5b50505050505098975050505050505050565b60e08101516000906001600160a01b03166110135760405162461bcd60e51b81526020600482015260166024820152751b5d5cdd081cdc1958da599e481c9958da5c1a595b9d60521b60448201526064016104d8565b6005541580611037575060008260c0015111801561103757506005548260c0015111155b6110735760405162461bcd60e51b815260206004820152600d60248201526c1a5b9d985b1a59081b1a5b5a5d609a1b60448201526064016104d8565b60035482516020808501516101208601516040516110c59561010090046001600160a01b0316946110aa948a94919391920161178a565b60405160208183030381529060405280519060200120611267565b905060006110d1610dc4565b90506001600160a01b038116156111dd5760e0830151604051631963bc8b60e11b81526001600160a01b0384811660048301526000602483015291821660448201527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072909116906332c7791690606401600060405180830381600087803b15801561115b57600080fd5b505af115801561116f573d6000803e3d6000fd5b5050505060e083015160405163a566006360e01b81526001600160a01b03848116600483015291821660248201529082169063a566006390604401600060405180830381600087803b1580156111c457600080fd5b505af11580156111d8573d6000803e3d6000fd5b505050505b6040516306e9fe9960e41b81526001600160a01b03858116600483015283811660248301527f0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec0721690636e9fe99090604401600060405180830381600087803b15801561124857600080fd5b505af115801561125c573d6000803e3d6000fd5b505050505092915050565b6000763d602d80600a3d3981f3363d3d373d3d3d363d730000008360601b60e81c176000526e5af43d82803e903d91602b57fd5bf38360781b1760205281603760096000f590506001600160a01b0381166104a85760405162461bcd60e51b815260206004820152601760248201527f455243313136373a2063726561746532206661696c656400000000000000000060448201526064016104d8565b634e487b7160e01b600052604160045260246000fd5b604051610140810167ffffffffffffffff8111828210171561133e5761133e611304565b60405290565b600082601f83011261135557600080fd5b813567ffffffffffffffff8082111561137057611370611304565b604051601f8301601f19908116603f0116810190828211818310171561139857611398611304565b816040528381528660208588010111156113b157600080fd5b836020870160208301376000602085830101528094505050505092915050565b6001600160a01b03811681146113e657600080fd5b50565b80356113f4816113d1565b919050565b6000610140828403121561140c57600080fd5b61141461131a565b9050813567ffffffffffffffff8082111561142e57600080fd5b61143a85838601611344565b8352602084013591508082111561145057600080fd5b61145c85838601611344565b6020840152604084013591508082111561147557600080fd5b61148185838601611344565b6040840152606084013591508082111561149a57600080fd5b6114a685838601611344565b606084015260808401359150808211156114bf57600080fd5b506114cc84828501611344565b60808301525060a082013560a082015260c082013560c08201526114f260e083016113e9565b60e08201526101006115058184016113e9565b818301525061012080830135818301525092915050565b60006020828403121561152e57600080fd5b813567ffffffffffffffff81111561154557600080fd5b611551848285016113f9565b949350505050565b60006020828403121561156b57600080fd5b5035919050565b6000806040838503121561158557600080fd5b8235611590816113d1565b946020939093013593505050565b6000815180845260005b818110156115c4576020818501810151868301820152016115a8565b506000602082860101526020601f19601f83011685010191505092915050565b6020815260006105b7602083018461159e565b60006020828403121561160957600080fd5b813580151581146105b757600080fd5b6000806040838503121561162c57600080fd5b8235611637816113d1565b91506020830135611647816113d1565b809150509250929050565b600080600080600080600080610100898b03121561166f57600080fd5b883561167a816113d1565b9750602089013567ffffffffffffffff8082111561169757600080fd5b6116a38c838d016113f9565b985060408b0135915060ff821682146116bb57600080fd5b81975060608b0135965060808b013595506116d860a08c016113e9565b945060c08b01359150808211156116ee57600080fd5b506116fb8b828c01611344565b92505061170a60e08a016113e9565b90509295985092959890939650565b60006020828403121561172b57600080fd5b81356105b7816113d1565b60208082526018908201527f63616c6c6572206973206e6f7420746865206f776e65722e0000000000000000604082015260600190565b60006020828403121561177f57600080fd5b81516105b7816113d1565b6001600160a01b03851681526080602082018190526000906117ae9083018661159e565b82810360408401526117c0818661159e565b91505082606083015295945050505050565b600060018060a01b038086168352606060208401526117f4606084018661159e565b9150808416604084015250949350505050565b60006020828403121561181957600080fd5b5051919050565b600061014082518185526118368286018261159e565b91505060208301518482036020860152611850828261159e565b9150506040830151848203604086015261186a828261159e565b91505060608301518482036060860152611884828261159e565b9150506080830151848203608086015261189e828261159e565b91505060a083015160a085015260c083015160c085015260e08301516118cf60e08601826001600160a01b03169052565b50610100838101516001600160a01b0316908501526101209283015192909301919091525090565b600060018060a01b03808916835260c0602084015261191960c0840189611820565b81881660408501528381036060850152611933818861159e565b959091166080840152505090151560a090910152949350505050565b6001600160a01b038a81168252602082018a90526101206040830181905260009161197c8483018c61159e565b91508382036060850152611990828b611820565b9150808916608085015283820360a08501526119ac828961159e565b96811660c085015294151560e08401525050911661010090910152969550505050505056fea2646970667358221220994653b8c585513601d17ed8f59a59c1d00c08ad547d70fe216ece47cf44764e64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde60000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec07200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
-----Decoded View---------------
Arg [0] : _owner (address): 0xae6FdDA940155D090E3A3DE9b090aD6A392F761a
Arg [1] : _treasuryConfiguration (address): 0x004D8438f4016A96F2D6dDc17808F4e40b47cdE6
Arg [2] : _o11y (address): 0x3F2408693cc2E0C8E0bb68f039cEB6DEac0EC072
Arg [3] : _maxLimit (uint256): 0
Arg [4] : _guardOn (bool): True
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000ae6fdda940155d090e3a3de9b090ad6a392f761a
Arg [1] : 000000000000000000000000004d8438f4016a96f2d6ddc17808f4e40b47cde6
Arg [2] : 0000000000000000000000003f2408693cc2e0c8e0bb68f039ceb6deac0ec072
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000001
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
[ 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.