Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 25 from a total of 98 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Grant Role | 21114926 | 500 days ago | IN | 0 ETH | 0.00054791 | ||||
| Grant Role | 21114906 | 500 days ago | IN | 0 ETH | 0.00050586 | ||||
| Grant Role | 21112587 | 501 days ago | IN | 0 ETH | 0.00023506 | ||||
| Grant Role | 21110865 | 501 days ago | IN | 0 ETH | 0.00015097 | ||||
| Create Multiple ... | 21098175 | 503 days ago | IN | 0 ETH | 0.00158811 | ||||
| Grant Role | 21096519 | 503 days ago | IN | 0 ETH | 0.00022138 | ||||
| Grant Role | 21093134 | 503 days ago | IN | 0 ETH | 0.00073933 | ||||
| Create Multiple ... | 21083368 | 505 days ago | IN | 0 ETH | 0.00256813 | ||||
| Create Multiple ... | 21083364 | 505 days ago | IN | 0 ETH | 0.00268418 | ||||
| Create Multiple ... | 21083360 | 505 days ago | IN | 0 ETH | 0.00503619 | ||||
| Create Multiple ... | 21083334 | 505 days ago | IN | 0 ETH | 0.00271254 | ||||
| Grant Role | 21082686 | 505 days ago | IN | 0 ETH | 0.00054541 | ||||
| Create Multiple ... | 21080681 | 505 days ago | IN | 0 ETH | 0.0091218 | ||||
| Grant Role | 21073328 | 506 days ago | IN | 0 ETH | 0.00090651 | ||||
| Create Multiple ... | 21072172 | 506 days ago | IN | 0 ETH | 0.00511034 | ||||
| Create Multiple ... | 21072168 | 506 days ago | IN | 0 ETH | 0.00484633 | ||||
| Grant Role | 21068051 | 507 days ago | IN | 0 ETH | 0.00060998 | ||||
| Grant Role | 21065599 | 507 days ago | IN | 0 ETH | 0.00109242 | ||||
| Grant Role | 21065585 | 507 days ago | IN | 0 ETH | 0.00098213 | ||||
| Grant Role | 21064840 | 507 days ago | IN | 0 ETH | 0.00079551 | ||||
| Create Multiple ... | 21051500 | 509 days ago | IN | 0 ETH | 0.00208966 | ||||
| Grant Role | 21050001 | 509 days ago | IN | 0 ETH | 0.00032657 | ||||
| Grant Role | 21044079 | 510 days ago | IN | 0 ETH | 0.0005342 | ||||
| Grant Role | 21036181 | 511 days ago | IN | 0 ETH | 0.00089758 | ||||
| Set Product Id C... | 21035494 | 511 days ago | IN | 0 ETH | 0.00027817 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
SykySelfService
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 300 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Gustavo A. Rodrigues
/*
@@@ @@@@@ @@@@@@@ @@@@ @@@@@@@ @@@@@ @@@@@@@ @@@@
@@@@ @@@ @@@@@@ @@ @@@@@@ @@ @@@@@@ @@
@@@@@ @@ @@@@@ @@ @@@@@ @ @@@@@ @@
@@@@@@ @@ @@@@@ @@ @@@@@ @ @@@@ @@
@@@@@@ @ @@@@@ @@ @@@@@ @ @@@@@ @
@@@@@@@ @@@@@ @@ @@@@@ @@@ @@@@@ @
@@@@@@ @@@@@@ @@@@@@@@@@@ @@@@@@
@@@@@@ @@@@@ @@@@@ @@@@@ @@@@@
@@@@@@@ @@@@@ @@@@@ @@@@@ @@@@@
@ @@@@@@ @@@@@ @@@@@ @@@@@ @@@@@
@@ @@@@@ @@@@@ @@@@@ @@@@@ @@@@@
@@@@ @@@@@ @@@@@ @@@@@@ @@@@@@ @@@@@@
@@@@@@ @@@@ @@@@@@@ @@@@@@@ @@@@@@@ @@@@@@@
*/
import "../base/ProductSelfService.sol";
contract SykySelfService is ProductSelfService {
/*//////////////////////////////////////////////////////////////
Version Info
//////////////////////////////////////////////////////////////*/
string public constant ENV = "MAINNET";
string public constant VER = "1.0.1";
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor(
address defaultAdmin_,
address defaultToken_,
address defaultMarket_
) ProductSelfService(defaultAdmin_, defaultToken_, defaultMarket_) {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./interface/IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* [EIP](https://eips.ethereum.org/EIPS/eip-165).
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Interface for the NFT Royalty Standard.
*
* A standardized way to retrieve royalty payment information for non-fungible tokens (NFTs) to enable universal
* support for royalty payments across all NFT marketplaces and ecosystem participants.
*
* _Available since v4.5._
*/
interface IERC2981 is IERC165 {
/**
* @dev Returns how much royalty is owed and to whom, based on a sale price that may be denominated in any unit of
* exchange. The royalty amount is denominated and should be payed in that same unit of exchange.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
returns (address receiver, uint256 royaltyAmount);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address);
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external;
}// SPDX-License-Identifier: MIT
// ERC721A Contracts v3.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import "./IERC721.sol";
import "./IERC721Metadata.sol";
/**
* @dev Interface of an ERC721A compliant contract.
*/
interface IERC721A is IERC721, IERC721Metadata {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* The caller cannot approve to their own address.
*/
error ApproveToCaller();
/**
* The caller cannot approve to the current owner.
*/
error ApprovalToCurrentOwner();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
// Compiler will pack this into a single 256bit word.
struct TokenOwnership {
// The address of the owner.
address addr;
// Keeps track of the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
}
// Compiler will pack this into a single 256bit word.
struct AddressData {
// Realistically, 2**64-1 is more than enough.
uint64 balance;
// Keeps track of mint count with minimal overhead for tokenomics.
uint64 numberMinted;
// Keeps track of burn count with minimal overhead for tokenomics.
uint64 numberBurned;
// For miscellaneous variable(s) pertaining to the address
// (e.g. number of whitelist mint slots used).
// If there are multiple variables, please pack them into a uint64.
uint64 aux;
}
/**
* @dev Returns the total amount of tokens stored by the contract.
*
* Burned tokens are calculated here, use `_totalMinted()` if you want to count just minted tokens.
*/
function totalSupply() external view returns (uint256);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
/* is ERC721 */
interface IERC721Metadata {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string memory);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(uint256 _tokenId) external view returns (string memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.0;
/**
* @title ERC721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
*
* The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "./interface/IContractMetadata.sol";
/**
* @title Contract Metadata
* @notice Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/
abstract contract ContractMetadata is IContractMetadata {
/// @notice Returns the contract metadata URI.
string public override contractURI;
/**
* @notice Lets a contract admin set the URI for contract-level metadata.
* @dev Caller should be authorized to setup contractURI, e.g. contract admin.
* See {_canSetContractURI}.
* Emits {ContractURIUpdated Event}.
*
* @param _uri keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*/
function setContractURI(string memory _uri) external override {
if (!_canSetContractURI()) {
revert("Not authorized");
}
_setupContractURI(_uri);
}
/// @dev Lets a contract admin set the URI for contract-level metadata.
function _setupContractURI(string memory _uri) internal {
string memory prevURI = contractURI;
contractURI = _uri;
emit ContractURIUpdated(prevURI, _uri);
}
/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view virtual returns (bool);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* Thirdweb's `ContractMetadata` is a contract extension for any base contracts. It lets you set a metadata URI
* for you contract.
*
* Additionally, `ContractMetadata` is necessary for NFT contracts that want royalties to get distributed on OpenSea.
*/
interface IContractMetadata {
/// @dev Returns the metadata URI of the contract.
function contractURI() external view returns (string memory);
/**
* @dev Sets contract URI for the storefront-level metadata of the contract.
* Only module admin can call this function.
*/
function setContractURI(string calldata _uri) external;
/// @dev Emitted when the contract URI is updated.
event ContractURIUpdated(string prevURI, string newURI);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
interface IMulticall {
/**
* @dev Receives and executes a batch of function calls on this contract.
*/
function multicall(bytes[] calldata data) external returns (bytes[] memory results);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
* information about who the contract's owner is.
*/
interface IOwnable {
/// @dev Returns the owner of the contract.
function owner() external view returns (address);
/// @dev Lets a module admin set a new owner for the contract. The new owner must be a module admin.
function setOwner(address _newOwner) external;
/// @dev Emitted when a new Owner is set.
event OwnerUpdated(address indexed prevOwner, address indexed newOwner);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IPermissions {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "../../eip/interface/IERC2981.sol";
/**
* Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about royalty fees, if desired.
*
* The `Royalty` contract is ERC2981 compliant.
*/
interface IRoyalty is IERC2981 {
struct RoyaltyInfo {
address recipient;
uint256 bps;
}
/// @dev Returns the royalty recipient and fee bps.
function getDefaultRoyaltyInfo() external view returns (address, uint16);
/// @dev Lets a module admin update the royalty bps and recipient.
function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external;
/// @dev Lets a module admin set the royalty recipient for a particular token Id.
function setRoyaltyInfoForToken(
uint256 tokenId,
address recipient,
uint256 bps
) external;
/// @dev Returns the royalty recipient for a particular token Id.
function getRoyaltyInfoForToken(uint256 tokenId) external view returns (address, uint16);
/// @dev Emitted when royalty info is updated.
event DefaultRoyalty(address indexed newRoyaltyRecipient, uint256 newRoyaltyBps);
/// @dev Emitted when royalty recipient for tokenId is set
event RoyaltyForToken(uint256 indexed tokenId, address indexed royaltyRecipient, uint256 royaltyBps);
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "../lib/TWAddress.sol";
import "./interface/IMulticall.sol";
/**
* @dev Provides a function to batch together multiple calls in a single external call.
*
* _Available since v4.1._
*/
contract Multicall is IMulticall {
/**
* @notice Receives and executes a batch of function calls on this contract.
* @dev Receives and executes a batch of function calls on this contract.
*
* @param data The bytes data that makes up the batch of function calls to execute.
* @return results The bytes data that makes up the result of the batch of function calls executed.
*/
function multicall(bytes[] calldata data) external virtual override returns (bytes[] memory results) {
results = new bytes[](data.length);
for (uint256 i = 0; i < data.length; i++) {
results[i] = TWAddress.functionDelegateCall(address(this), data[i]);
}
return results;
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "./interface/IOwnable.sol";
/**
* @title Ownable
* @notice Thirdweb's `Ownable` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* who the 'owner' of the inheriting smart contract is, and lets the inheriting contract perform conditional logic that uses
* information about who the contract's owner is.
*/
abstract contract Ownable is IOwnable {
/// @dev Owner of the contract (purpose: OpenSea compatibility)
address private _owner;
/// @dev Reverts if caller is not the owner.
modifier onlyOwner() {
if (msg.sender != _owner) {
revert("Not authorized");
}
_;
}
/**
* @notice Returns the owner of the contract.
*/
function owner() public view override returns (address) {
return _owner;
}
/**
* @notice Lets an authorized wallet set a new owner for the contract.
* @param _newOwner The address to set as the new owner of the contract.
*/
function setOwner(address _newOwner) external override {
if (!_canSetOwner()) {
revert("Not authorized");
}
_setupOwner(_newOwner);
}
/// @dev Lets a contract admin set a new owner for the contract. The new owner must be a contract admin.
function _setupOwner(address _newOwner) internal {
address _prevOwner = _owner;
_owner = _newOwner;
emit OwnerUpdated(_prevOwner, _newOwner);
}
/// @dev Returns whether owner can be set in the given execution context.
function _canSetOwner() internal view virtual returns (bool);
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "./interface/IPermissions.sol";
import "../lib/TWStrings.sol";
/**
* @title Permissions
* @dev This contracts provides extending-contracts with role-based access control mechanisms
*/
contract Permissions is IPermissions {
/// @dev Map from keccak256 hash of a role => a map from address => whether address has role.
mapping(bytes32 => mapping(address => bool)) private _hasRole;
/// @dev Map from keccak256 hash of a role to role admin. See {getRoleAdmin}.
mapping(bytes32 => bytes32) private _getRoleAdmin;
/// @dev Default admin role for all roles. Only accounts with this role can grant/revoke other roles.
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @dev Modifier that checks if an account has the specified role; reverts otherwise.
modifier onlyRole(bytes32 role) {
_checkRole(role, msg.sender);
_;
}
/**
* @notice Checks whether an account has a particular role.
* @dev Returns `true` if `account` has been granted `role`.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account for which the role is being checked.
*/
function hasRole(bytes32 role, address account) public view override returns (bool) {
return _hasRole[role][account];
}
/**
* @notice Checks whether an account has a particular role;
* role restrictions can be swtiched on and off.
*
* @dev Returns `true` if `account` has been granted `role`.
* Role restrictions can be swtiched on and off:
* - If address(0) has ROLE, then the ROLE restrictions
* don't apply.
* - If address(0) does not have ROLE, then the ROLE
* restrictions will apply.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account for which the role is being checked.
*/
function hasRoleWithSwitch(bytes32 role, address account) public view returns (bool) {
if (!_hasRole[role][address(0)]) {
return _hasRole[role][account];
}
return true;
}
/**
* @notice Returns the admin role that controls the specified role.
* @dev See {grantRole} and {revokeRole}.
* To change a role's admin, use {_setRoleAdmin}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
*/
function getRoleAdmin(bytes32 role) external view override returns (bytes32) {
return _getRoleAdmin[role];
}
/**
* @notice Grants a role to an account, if not previously granted.
* @dev Caller must have admin role for the `role`.
* Emits {RoleGranted Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account to which the role is being granted.
*/
function grantRole(bytes32 role, address account) public virtual override {
_checkRole(_getRoleAdmin[role], msg.sender);
if (_hasRole[role][account]) {
revert("Can only grant to non holders");
}
_setupRole(role, account);
}
/**
* @notice Revokes role from an account.
* @dev Caller must have admin role for the `role`.
* Emits {RoleRevoked Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account from which the role is being revoked.
*/
function revokeRole(bytes32 role, address account) public virtual override {
_checkRole(_getRoleAdmin[role], msg.sender);
_revokeRole(role, account);
}
/**
* @notice Revokes role from the account.
* @dev Caller must have the `role`, with caller being the same as `account`.
* Emits {RoleRevoked Event}.
*
* @param role keccak256 hash of the role. e.g. keccak256("TRANSFER_ROLE")
* @param account Address of the account from which the role is being revoked.
*/
function renounceRole(bytes32 role, address account) public virtual override {
if (msg.sender != account) {
revert("Can only renounce for self");
}
_revokeRole(role, account);
}
/// @dev Sets `adminRole` as `role`'s admin role.
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = _getRoleAdmin[role];
_getRoleAdmin[role] = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/// @dev Sets up `role` for `account`
function _setupRole(bytes32 role, address account) internal virtual {
_hasRole[role][account] = true;
emit RoleGranted(role, account, msg.sender);
}
/// @dev Revokes `role` from `account`
function _revokeRole(bytes32 role, address account) internal virtual {
_checkRole(role, account);
delete _hasRole[role][account];
emit RoleRevoked(role, account, msg.sender);
}
/// @dev Checks `role` for `account`. Reverts with a message including the required role.
function _checkRole(bytes32 role, address account) internal view virtual {
if (!_hasRole[role][account]) {
revert(
string(
abi.encodePacked(
"Permissions: account ",
TWStrings.toHexString(uint160(account), 20),
" is missing role ",
TWStrings.toHexString(uint256(role), 32)
)
)
);
}
}
/// @dev Checks `role` for `account`. Reverts with a message including the required role.
function _checkRoleWithSwitch(bytes32 role, address account) internal view virtual {
if (!hasRoleWithSwitch(role, account)) {
revert(
string(
abi.encodePacked(
"Permissions: account ",
TWStrings.toHexString(uint160(account), 20),
" is missing role ",
TWStrings.toHexString(uint256(role), 32)
)
)
);
}
}
}// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/// @author thirdweb
import "./interface/IRoyalty.sol";
/**
* @title Royalty
* @notice Thirdweb's `Royalty` is a contract extension to be used with any base contract. It exposes functions for setting and reading
* the recipient of royalty fee and the royalty fee basis points, and lets the inheriting contract perform conditional logic
* that uses information about royalty fees, if desired.
*
* @dev The `Royalty` contract is ERC2981 compliant.
*/
abstract contract Royalty is IRoyalty {
/// @dev The (default) address that receives all royalty value.
address private royaltyRecipient;
/// @dev The (default) % of a sale to take as royalty (in basis points).
uint16 private royaltyBps;
/// @dev Token ID => royalty recipient and bps for token
mapping(uint256 => RoyaltyInfo) private royaltyInfoForToken;
/**
* @notice View royalty info for a given token and sale price.
* @dev Returns royalty amount and recipient for `tokenId` and `salePrice`.
* @param tokenId The tokenID of the NFT for which to query royalty info.
* @param salePrice Sale price of the token.
*
* @return receiver Address of royalty recipient account.
* @return royaltyAmount Royalty amount calculated at current royaltyBps value.
*/
function royaltyInfo(uint256 tokenId, uint256 salePrice)
external
view
virtual
override
returns (address receiver, uint256 royaltyAmount)
{
(address recipient, uint256 bps) = getRoyaltyInfoForToken(tokenId);
receiver = recipient;
royaltyAmount = (salePrice * bps) / 10_000;
}
/**
* @notice View royalty info for a given token.
* @dev Returns royalty recipient and bps for `_tokenId`.
* @param _tokenId The tokenID of the NFT for which to query royalty info.
*/
function getRoyaltyInfoForToken(uint256 _tokenId) public view override returns (address, uint16) {
RoyaltyInfo memory royaltyForToken = royaltyInfoForToken[_tokenId];
return
royaltyForToken.recipient == address(0)
? (royaltyRecipient, uint16(royaltyBps))
: (royaltyForToken.recipient, uint16(royaltyForToken.bps));
}
/**
* @notice Returns the defualt royalty recipient and BPS for this contract's NFTs.
*/
function getDefaultRoyaltyInfo() external view override returns (address, uint16) {
return (royaltyRecipient, uint16(royaltyBps));
}
/**
* @notice Updates default royalty recipient and bps.
* @dev Caller should be authorized to set royalty info.
* See {_canSetRoyaltyInfo}.
* Emits {DefaultRoyalty Event}; See {_setupDefaultRoyaltyInfo}.
*
* @param _royaltyRecipient Address to be set as default royalty recipient.
* @param _royaltyBps Updated royalty bps.
*/
function setDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) external override {
if (!_canSetRoyaltyInfo()) {
revert("Not authorized");
}
_setupDefaultRoyaltyInfo(_royaltyRecipient, _royaltyBps);
}
/// @dev Lets a contract admin update the default royalty recipient and bps.
function _setupDefaultRoyaltyInfo(address _royaltyRecipient, uint256 _royaltyBps) internal {
if (_royaltyBps > 10_000) {
revert("Exceeds max bps");
}
royaltyRecipient = _royaltyRecipient;
royaltyBps = uint16(_royaltyBps);
emit DefaultRoyalty(_royaltyRecipient, _royaltyBps);
}
/**
* @notice Updates default royalty recipient and bps for a particular token.
* @dev Sets royalty info for `_tokenId`. Caller should be authorized to set royalty info.
* See {_canSetRoyaltyInfo}.
* Emits {RoyaltyForToken Event}; See {_setupRoyaltyInfoForToken}.
*
* @param _recipient Address to be set as royalty recipient for given token Id.
* @param _bps Updated royalty bps for the token Id.
*/
function setRoyaltyInfoForToken(
uint256 _tokenId,
address _recipient,
uint256 _bps
) external override {
if (!_canSetRoyaltyInfo()) {
revert("Not authorized");
}
_setupRoyaltyInfoForToken(_tokenId, _recipient, _bps);
}
/// @dev Lets a contract admin set the royalty recipient and bps for a particular token Id.
function _setupRoyaltyInfoForToken(
uint256 _tokenId,
address _recipient,
uint256 _bps
) internal {
if (_bps > 10_000) {
revert("Exceeds max bps");
}
royaltyInfoForToken[_tokenId] = RoyaltyInfo({ recipient: _recipient, bps: _bps });
emit RoyaltyForToken(_tokenId, _recipient, _bps);
}
/// @dev Returns whether royalty info can be set in the given execution context.
function _canSetRoyaltyInfo() internal view virtual returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev Collection of functions related to the address type
*/
library TWAddress {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* [EIP1884](https://eips.ethereum.org/EIPS/eip-1884) increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
(bool success, bytes memory returndata) = target.call{ value: value }(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
require(isContract(target), "Address: static call to non-contract");
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
require(isContract(target), "Address: delegate call to non-contract");
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
/// @author thirdweb
/**
* @dev String operations.
*/
library TWStrings {
bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef";
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
// Inspired by OraclizeAPI's implementation - MIT licence
// https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol
if (value == 0) {
return "0";
}
uint256 temp = value;
uint256 digits;
while (temp != 0) {
digits++;
temp /= 10;
}
bytes memory buffer = new bytes(digits);
while (value != 0) {
digits -= 1;
buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
value /= 10;
}
return string(buffer);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
if (value == 0) {
return "0x00";
}
uint256 temp = value;
uint256 length = 0;
while (temp != 0) {
length++;
temp >>= 8;
}
return toHexString(value, length);
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _HEX_SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
interface IProductArtistToken {
/*//////////////////////////////////////////////////////////////
Methods
//////////////////////////////////////////////////////////////*/
/// @notice Lets an authorized address mint single NFTs to a recipient.
function mintTo(address _to, uint256 _productId) external;
function mintTo(address _to, uint256 _productId, bytes memory _data) external;
function mintTo(
address _to,
uint256 _productId,
string calldata _uri,
bytes memory _data
) external;
/// @notice Lets an authorized address mint multiple NFTs at once to a recipient.
function batchMintTo(address _to, uint256 _productId, uint256 _quantity) external;
function batchMintTo(
address _to,
uint256 _productId,
uint256 _quantity,
bytes memory _data
) external;
function batchMintTo(
address _to,
uint256 _productId,
uint256 _quantity,
string[] calldata _uris,
bytes memory _data
) external;
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @dev Emitted when all minting is enabled or disabled
event GlobalMintingRestricted(bool restricted);
/// @dev Emitted when all transfers are enabled or disabled
event GlobalTransfersRestricted(bool restricted);
/// @dev Emitted when all burning is enabled or disabled
event GlobalBurningRestricted(bool restricted);
/*//////////////////////////////////////////////////////////////
Errors
//////////////////////////////////////////////////////////////*/
/// @dev URIs array length for batch mint must match _quantity
error BatchMintURICountMismatch();
/// @dev Action requires the manager or admin role
error ManagerRoleRequired();
/// @dev Action requires the minting or admin role
error MintingRoleRequired();
/// @dev Minting has been disabled via address(0) role
error GlobalMintingDisabled();
/// @dev Transfers have been disabled via address(0) role
error GlobalTransfersDisabled();
/// @dev Burning has been disabled via address(0) role
error GlobalBurningDisabled();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
import "../../extension/interface/IProductCreators.sol";
interface IProductDataContract {
function tieredURI(
uint256 _tokenId,
uint256 _productId,
bool _isUnique
) external view returns (string memory);
function getProductCreators(
uint256 _productId
) external view returns (IProductCreators.CreatorData[] memory);
function baseURI() external view returns (string memory);
function setBaseURI(string memory _uri) external returns (string memory);
function productURI(uint256 _productId) external view returns (string memory);
function setProductURI(
uint256 _productId,
string calldata _uri
) external returns (string memory);
function tokenURI(uint256 _tokenId) external view returns (string memory);
function setTokenURI(
uint256 _tokenId,
string calldata _uri
) external returns (string memory);
function setTokenURIs(
uint256[] calldata _tokenIds,
string[] calldata _uris
) external returns (string[] memory);
function setTokenURIs(
uint256 _startTokenId,
uint256 _quantity,
string[] calldata _uri
) external returns (string[] memory);
function runBeforeExtensions(
uint256 _productId,
address _from,
address _to,
uint256 _startTokenId,
uint256 _quantity
) external;
function runAfterExtensions(
uint256 _productId,
address _from,
address _to,
uint256 _startTokenId,
uint256 _quantity
) external;
error ManagerRoleRequired();
error CalledFromTokenOnly();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Gustavo A. Rodrigues
interface IProductSelfServiceMarket {
struct Listing {
uint24 productId;
uint8 enabled;
uint32 quantity;
uint16 reserved;
uint16 vipLimit;
uint16 pubLimit;
uint80 vipPrice;
uint80 pubPrice;
//compresses to one 256
uint32 endTimestamp;
uint32 vipStartTimestamp;
uint32 pubStartTimestamp;
uint16 resPurchased;
uint16 vipPurchased;
uint16 pubPurchased;
//compresses to one 144
}
struct ListingQuery {
uint256 listingId;
Listing listingData;
}
function createAuction(
IProductSelfService.AuctioMarketParams calldata _params
) external returns (uint256 auctionId);
function createListing(
IProductSelfService.ListingMarketParams calldata _params
) external returns (uint256 listingId);
function updateListing(
uint256 listingId,
IProductSelfService.ListingMarketParams calldata _params
) external;
function createMultipleListings(
IProductSelfService.ListingMarketParams[] calldata _params
) external returns (uint256[] memory listingIds);
function getListing(
uint256 _listingId
) external view returns (ListingQuery memory _listing);
}
interface IProductSelfService {
/*//////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
struct AuctionParams {
string uri;
uint32 startTimestamp;
uint32 endTimestamp;
uint64 bidBufferBps;
uint64 timeBufferSeconds;
uint256 minimumBidAmount;
uint256 reserveBidAmount;
}
struct AuctioMarketParams {
uint56 productId;
uint32 startTimestamp;
uint32 endTimestamp;
uint64 bidBufferBps;
uint64 timeBufferSeconds;
uint256 minimumBidAmount;
uint256 reserveBidAmount;
}
struct ListingParams {
uint16 quantity;
string uri;
uint80 vipPrice;
uint80 pubPrice;
uint32 endTimestamp;
uint32 vipStartTimestamp;
uint32 pubStartTimestamp;
}
struct ListingMarketParams {
uint24 productId;
uint16 quantity;
uint16 reserved;
uint16 vipLimit;
uint16 pubLimit;
uint64 vipPrice;
uint64 pubPrice;
uint32 endTimestamp;
uint32 vipStartTimestamp;
uint32 pubStartTimestamp;
bool enabled;
}
/*//////////////////////////////////////////////////////////////
Errors
//////////////////////////////////////////////////////////////*/
/// @dev Action requires the minting or admin role
error CreatorRoleRequired();
/// @dev URI already being used
error URIAlreadyUsed();
/// @dev Sender must be the product owner
error ProductOwnershipRequired();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
import "../eip/ERC721ArtistProduct.sol";
import "./interface/IProductArtistToken.sol";
import "../extension/ProductDataConnector.sol";
import "@thirdweb-dev/contracts/extension/ContractMetadata.sol";
import "@thirdweb-dev/contracts/extension/Multicall.sol";
import "@thirdweb-dev/contracts/extension/Ownable.sol";
import "@thirdweb-dev/contracts/extension/Royalty.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
import "@thirdweb-dev/contracts/lib/TWStrings.sol";
// ERC-5192 (lock/unlock a token);
// ERC-5192 (recommended for gas efficiency)
// event Locked(uint256 tokenId);
// event Unlocked(uint256 tokenId);
// ERC-4906 metadata update
// event MetadataUpdate(uint256 _tokenId);
// event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
//means the metadata is permanently frozen
//event PermanentURI(string _value, uint256 indexed _id);
// To refresh a whole collection, emit _toTokenId with type(uint256).max
//event PermanentURI(string _value, uint256 indexed _id);
//ERC721Tradable - allow opensea proxy
contract ProductArtistToken is
IProductArtistToken,
ERC721ArtistProduct,
ContractMetadata,
ProductDataConnector,
Multicall,
Ownable,
Royalty,
Permissions
{
using TWStrings for uint256;
/*//////////////////////////////////////////////////////////////
Roles
//////////////////////////////////////////////////////////////*/
bytes32 public constant BURNING_ROLE = keccak256("BURNING_ROLE");
bytes32 public constant MINTING_ROLE = keccak256("MINTING_ROLE");
bytes32 public constant MANAGER_ROLE = keccak256("MANAGER_ROLE");
bytes32 public constant TRANSFER_ROLE = keccak256("TRANSFER_ROLE");
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor(
address defaultAdmin_,
string memory name_,
string memory symbol_,
uint256 productOffset_,
address royaltyRecipient_,
uint128 royaltyBps_
) ERC721ArtistProduct(name_, symbol_, productOffset_) {
//sets the owner of the contract (important for OpenSea)
_setupOwner(defaultAdmin_);
//sets the admin of the contract to the owner
_setupRole(DEFAULT_ADMIN_ROLE, defaultAdmin_);
//enables transfers for all tokens
_setupRole(TRANSFER_ROLE, address(0));
_setupRole(MINTING_ROLE, address(0));
//sets the admin for various roles
_setRoleAdmin(BURNING_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(MINTING_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(MANAGER_ROLE, DEFAULT_ADMIN_ROLE);
_setRoleAdmin(TRANSFER_ROLE, DEFAULT_ADMIN_ROLE);
//sets the default royalty setup
_setupDefaultRoyaltyInfo(royaltyRecipient_, royaltyBps_);
}
/*//////////////////////////////////////////////////////////////
ERC165 Logic
//////////////////////////////////////////////////////////////*/
/// @dev See ERC165: https://eips.ethereum.org/EIPS/eip-165
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC721ArtistProduct, IERC165) returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f || // ERC165 Interface ID for ERC721Metadata
interfaceId == 0x49064906 || // ERC165 Interface ID for ERC4906 - MetadataUpdated
interfaceId == type(IERC2981).interfaceId; // ERC165 ID for ERC2981 - Royalties
}
/*//////////////////////////////////////////////////////////////
Overriden ERC721 logic
//////////////////////////////////////////////////////////////*/
/**
* @notice Returns the metadata URI for an NFT.
*
* @param _tokenId The tokenId of an NFT.
*/
function tokenURI(
uint256 _tokenId
) public view override(ERC721ArtistProduct) returns (string memory) {
if (!_exists(_tokenId)) revert URIQueryForNonexistentToken();
uint256 productId = _getProductId(_tokenId);
bool isUnique = _getProductUniqueMetadata(productId);
return _tieredURI(_tokenId, productId, isUnique);
}
/*//////////////////////////////////////////////////////////////
Minting logic
//////////////////////////////////////////////////////////////*/
/**
* @notice Lets an authorized address mint an NFT to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
*/
function mintTo(address _to, uint256 _productId) external onlyMinter {
_safeMint(_to, _productId, 1, "");
}
/**
* @notice Lets an authorized address mint an NFT to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
* @param _data Data to be passed along during minting.
*/
function mintTo(
address _to,
uint256 _productId,
bytes memory _data
) external onlyMinter {
_safeMint(_to, _productId, 1, _data);
}
/**
* @notice Lets an authorized address mint an NFT to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
* @param _uri The full metadata uri for the token being minted.
* @param _data Data to be passed along during minting.
*/
function mintTo(
address _to,
uint256 _productId,
string calldata _uri,
bytes memory _data
) external onlyMinter {
(uint256 startTokenId, ) = _safeMint(_to, _productId, 1, _data);
_setTokenUri(startTokenId, _uri);
}
/**
* @notice Lets an authorized address mint multiple NFTs at once to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
* @param _quantity The number of NFTs to mint.
*/
function batchMintTo(
address _to,
uint256 _productId,
uint256 _quantity
) external onlyMinter {
_safeMint(_to, _productId, _quantity);
}
/**
* @notice Lets an authorized address mint multiple NFTs at once to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
* @param _quantity The number of NFTs to mint.
* @param _data Data to be passed along during minting.
*/
function batchMintTo(
address _to,
uint256 _productId,
uint256 _quantity,
bytes memory _data
) external onlyMinter {
_safeMint(_to, _productId, _quantity, _data);
}
/**
* @notice Lets an authorized address mint multiple NFTs at once to a recipient.
*
* @param _to The recipient of the NFT to mint.
* @param _productId The productId of the NFT to mint.
* @param _quantity The number of NFTs to mint.
* @param _uris The custom token URIs for each NFT in the batch
* @param _data Data to be passed along during minting.
*/
function batchMintTo(
address _to,
uint256 _productId,
uint256 _quantity,
string[] calldata _uris,
bytes memory _data
) external onlyMinter {
(uint256 startTokenId, ) = _safeMint(_to, _productId, _quantity, _data);
_setTokenUris(startTokenId, _quantity, _uris);
}
/**
* @notice Lets an owner or approved operator burn the NFT of the given tokenId.
* @dev ERC721A"s `_burn(uint256,bool)` internally checks for token approvals.
*
* @param _tokenId The tokenId of the NFT to burn.
*/
function burn(uint256 _tokenId) external {
_burn(_tokenId, true);
}
/*//////////////////////////////////////////////////////////////
Public getters
//////////////////////////////////////////////////////////////*/
/// @notice The total minted tokens among all products
function totalMinted() external view returns (uint256) {
return _mintCounter;
}
/// @notice The total burned tokens among all products
function totalBurned() external view returns (uint256) {
return _burnCounter;
}
/// @notice The owner data for a given address
function getOwnerData(address _owner) external view returns (AddressData memory) {
return _getAddressData(_owner);
}
/// @notice The token data for a given token id
function getTokenData(
uint256 _tokenId
) external view returns (TokenOwnership memory) {
return _ownershipOf(_tokenId);
}
/// @notice The product offset for token id formation
function getProductOffset() external view returns (uint256) {
return _getProductOffset();
}
/// @notice The product details for a given product id
function getProduct(uint256 _productId) external view returns (ProductData memory) {
return _getProduct(_productId);
}
/// @notice The product details for a given product id
function getProductArtist(uint256 _productId) external view returns (address) {
return _getArtist(_productId);
}
/// @notice The product details for a given token id
function getProductForToken(
uint256 _tokenId
) external view returns (ProductData memory) {
return _getProduct(_getProductId(_tokenId));
}
/// @notice The product details for a given token id
function getArtistForToken(uint256 _tokenId) external view returns (address) {
return _getArtist(_getProductId(_tokenId));
}
/// @notice Returns whether a given address is the owner, or approved to transfer an NFT.
function isApprovedOrOwner(
address _operator,
uint256 _tokenId
) external view returns (bool isApprovedOrOwnerOf) {
address owner = ownerOf(_tokenId);
isApprovedOrOwnerOf = (_operator == owner ||
isApprovedForAll(owner, _operator) ||
getApproved(_tokenId) == _operator);
}
/*//////////////////////////////////////////////////////////////
Admin setters
//////////////////////////////////////////////////////////////*/
function setProductURI(
uint256 _productId,
string calldata _uri
) external override onlyManager {
_setProductUri(_productId, _uri);
if (_getProductQuantity(_productId) != 0) {
emit BatchMetadataUpdate(
_getMinTokenId(_productId),
_getMaxTokenId(_productId)
);
}
}
function setOwnerAux(address owner, uint64 aux) external onlyManager {
_setAux(owner, aux);
}
function setProductIdOffset(uint256 _offset) external onlyManager {
_setProductOffset(_offset);
}
function setProduct(uint256 _productId, uint64 _quantity) external onlyManager {
_setProductQuantity(_productId, _quantity);
}
function setProductArtist(uint256 _productId, address _artist) external onlyManager {
_setArtist(_productId, _artist);
}
function setProductUniqueMetadata(
uint256 _productId,
bool _isUnique
) external onlyManager {
_setProductUniqueMetadata(_productId, _isUnique);
}
function restrictProductMinting(
uint256 _productId,
bool _toRestrict
) external onlyManager {
_setProductLocked(_productId, _toRestrict);
}
function restrictProductTransfers(
uint256 _productId,
bool _toRestrict
) external onlyManager {
_setProductFrozen(_productId, _toRestrict);
}
function restrictProductBurning(
uint256 _productId,
bool _toRestrict
) external onlyManager {
_setProductBurnable(_productId, !_toRestrict);
}
function restrictMinting(bool _toRestrict) external onlyManager {
if (_toRestrict) {
_revokeRole(MINTING_ROLE, address(0));
} else {
_setupRole(MINTING_ROLE, address(0));
}
emit GlobalMintingRestricted(_toRestrict);
}
function restrictTransfers(bool _toRestrict) external onlyManager {
if (_toRestrict) {
_revokeRole(TRANSFER_ROLE, address(0));
} else {
_setupRole(TRANSFER_ROLE, address(0));
}
emit GlobalTransfersRestricted(_toRestrict);
}
function restrictBurning(bool _toRestrict) external onlyManager {
if (_toRestrict) {
_revokeRole(BURNING_ROLE, address(0));
} else {
_setupRole(BURNING_ROLE, address(0));
}
emit GlobalBurningRestricted(_toRestrict);
}
/*//////////////////////////////////////////////////////////////
ERC-721 overrides
//////////////////////////////////////////////////////////////*/
/**
* @dev See {ERC721A-_beforeTokenTransfers}.
* @notice Product levels are restricted prior to hook
* This means even if specific users has a transfer or burn role
* The product level setting will override that functionality
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal override(ERC721ArtistProduct) {
// Check transfer restrictions
if (from != address(0) && to != address(0)) {
//transfers restricted globally, unless from and to both have transfer role
if (
!hasRole(TRANSFER_ROLE, address(0)) &&
!(hasRole(TRANSFER_ROLE, from) && hasRole(TRANSFER_ROLE, to))
) revert GlobalTransfersDisabled();
}
// Check burn restrictions
if (from != address(0) && to == address(0)) {
//burning restricted globally, unless from has burn permission
if (!hasRoleWithSwitch(BURNING_ROLE, from)) revert GlobalBurningDisabled();
}
// Check mint restrictions
if (from == address(0) && to != address(0)) {
//minting restricted globally, regardless of operator roles
if (!hasRole(MINTING_ROLE, address(0))) revert GlobalMintingDisabled();
}
_runBeforeExtensions(
_getProductId(startTokenId),
from,
to,
startTokenId,
quantity
);
}
/// @dev See {ERC721A-_afterTokenTransfers}.
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal override(ERC721ArtistProduct) {
_runAfterExtensions(
_getProductId(startTokenId),
from,
to,
startTokenId,
quantity
);
}
/*//////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/
/// @dev Modifier that checks if an account has admin or product role; reverts otherwise.
modifier onlyManager() {
_checkManagerAdmin();
_;
}
/// @dev Modifier that checks if an account has admin or minter role; reverts otherwise.
modifier onlyMinter() {
_checkMintingAdmin();
_;
}
/*//////////////////////////////////////////////////////////////
Module admin checks
//////////////////////////////////////////////////////////////*/
/// @dev Function that checks if an account has admin or product role; reverts otherwise.
function _checkManagerAdmin() internal view {
if (
!hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(MANAGER_ROLE, msg.sender)
) {
revert ManagerRoleRequired();
}
}
/// @dev Function that checks if an account has admin or minter role; reverts otherwise.
function _checkMintingAdmin() internal view {
if (
!hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(MINTING_ROLE, msg.sender)
) {
revert MintingRoleRequired();
}
}
/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetContractURI() internal view override returns (bool) {
return
hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(MANAGER_ROLE, msg.sender);
}
/// @dev Returns whether contract metadata can be set in the given execution context.
function _canSetData() internal view override returns (bool) {
return
hasRole(DEFAULT_ADMIN_ROLE, msg.sender) || hasRole(MANAGER_ROLE, msg.sender);
}
/// @dev Returns whether owner can be set in the given execution context.
function _canSetOwner() internal view override returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/// @dev Returns whether royalty info can be set in the given execution context.
function _canSetRoyaltyInfo() internal view override returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "./interface/IProductSelfService.sol";
import "./ProductArtistToken.sol";
import "@thirdweb-dev/contracts/extension/Ownable.sol";
import "@thirdweb-dev/contracts/extension/Permissions.sol";
contract ProductSelfService is IProductSelfService, Ownable, Permissions {
using TWAddress for address;
/*//////////////////////////////////////////////////////////////
Constants
//////////////////////////////////////////////////////////////*/
bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE");
ProductArtistToken private _productArtistTokenContract;
IProductSelfServiceMarket internal _productMarketContract;
uint64 private _productIdCounter = 1000000;
mapping(string => uint64) private _uriToProductId;
bool private _enforceUniqueURI = true;
/*//////////////////////////////////////////////////////////////
Constructor
//////////////////////////////////////////////////////////////*/
constructor(
address defaultAdmin_,
address productArtistTokenAddress_,
address productMarketAddress_
) {
_setupOwner(defaultAdmin_);
_productArtistTokenContract = ProductArtistToken(productArtistTokenAddress_);
_productMarketContract = IProductSelfServiceMarket(productMarketAddress_);
_setupRole(DEFAULT_ADMIN_ROLE, defaultAdmin_);
_setRoleAdmin(CREATOR_ROLE, DEFAULT_ADMIN_ROLE);
}
function createAuction(
AuctionParams calldata _params
) external onlyCreator returns (uint256 auctionId) {
uint64 _productId = _createProduct(1, _params.uri);
AuctioMarketParams memory params = AuctioMarketParams({
productId: uint56(_productId),
startTimestamp: _params.startTimestamp,
endTimestamp: _params.endTimestamp,
bidBufferBps: _params.bidBufferBps,
timeBufferSeconds: _params.timeBufferSeconds,
minimumBidAmount: _params.minimumBidAmount,
reserveBidAmount: _params.reserveBidAmount
});
auctionId = _productMarketContract.createAuction(params);
}
function updateListing(
uint256 listingId,
ListingParams calldata _params
) external virtual onlyCreator {
uint24 productId = _productMarketContract
.getListing(listingId)
.listingData
.productId;
if (msg.sender != _productArtistTokenContract.getProductArtist(productId)) {
revert ProductOwnershipRequired();
}
_productMarketContract.updateListing(
listingId,
ListingMarketParams({
productId: productId,
quantity: _params.quantity,
reserved: _params.quantity,
vipLimit: _params.quantity,
pubLimit: _params.quantity,
vipPrice: uint64(_params.vipPrice),
pubPrice: uint64(_params.pubPrice),
endTimestamp: _params.endTimestamp,
vipStartTimestamp: _params.vipStartTimestamp,
pubStartTimestamp: _params.pubStartTimestamp,
enabled: true
})
);
}
function createListing(
ListingParams calldata _params
) external virtual onlyCreator returns (uint256 listingId) {
listingId = _createListing(_params);
}
function createMultipleListings(
ListingParams[] calldata _params
) external virtual onlyCreator returns (uint256[] memory listingIds) {
uint256 numListings = _params.length;
listingIds = new uint256[](numListings);
for (uint256 i = 0; i < numListings; i++) {
listingIds[i] = _createListing(_params[i]);
}
return listingIds;
}
function _createListing(
ListingParams calldata _params
) internal returns (uint256 listingId) {
uint64 _productId = _createProduct(_params.quantity, _params.uri);
ListingMarketParams memory params = ListingMarketParams({
productId: uint24(_productId),
quantity: _params.quantity,
reserved: _params.quantity,
vipLimit: _params.quantity,
pubLimit: _params.quantity,
vipPrice: uint64(_params.vipPrice),
pubPrice: uint64(_params.pubPrice),
endTimestamp: _params.endTimestamp,
vipStartTimestamp: _params.vipStartTimestamp,
pubStartTimestamp: _params.pubStartTimestamp,
enabled: true
});
listingId = _productMarketContract.createListing(params);
}
function _createProduct(
uint64 quantity,
string calldata uri
) internal returns (uint64 productId) {
if (_enforceUniqueURI && _uriToProductId[uri] != 0) {
revert URIAlreadyUsed();
}
unchecked {
productId = ++_productIdCounter;
}
_productArtistTokenContract.setProduct(productId, quantity);
_productArtistTokenContract.setProductArtist(productId, msg.sender);
_productArtistTokenContract.setProductURI(productId, uri);
_uriToProductId[uri] = productId;
}
/// @dev Modifier that checks if an account has admin or creator role; reverts otherwise.
modifier onlyCreator() {
_checkCreatorAdmin();
_;
}
function _checkCreatorAdmin() internal view {
if (
!hasRole(DEFAULT_ADMIN_ROLE, msg.sender) && !hasRole(CREATOR_ROLE, msg.sender)
) {
revert CreatorRoleRequired();
}
}
function _getProductMarketContract()
internal
view
returns (IProductSelfServiceMarket)
{
return _productMarketContract;
}
/// @dev Returns whether owner can be set in the given execution context.
function _canSetOwner() internal view override returns (bool) {
return hasRole(DEFAULT_ADMIN_ROLE, msg.sender);
}
/*//////////////////////////////////////////////////////////////
External setters
//////////////////////////////////////////////////////////////*/
function setProductIdCounter(uint64 newCounter) external onlyOwner {
_productIdCounter = newCounter;
}
function setEnforceUniqueURI(bool isUniqueURI) external onlyOwner {
_enforceUniqueURI = isUniqueURI;
}
/*//////////////////////////////////////////////////////////////
Public getters
//////////////////////////////////////////////////////////////*/
function getProductArtistToken() external view returns (address) {
return address(_productArtistTokenContract);
}
function getProductMarketContract() external view returns (address) {
return address(_productMarketContract);
}
function getProductIdCounter() external view returns (uint256) {
return _productIdCounter;
}
function getProductOwner(uint64 productId) external view returns (address) {
return _productArtistTokenContract.getProductArtist(productId);
}
function getProductIdFromURI(string calldata uri) public view returns (uint64) {
return _uriToProductId[uri];
}
function getEnforceUniqueURI() external view returns (bool) {
return _enforceUniqueURI;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
/// @author Syky - Nathan Rempel
import "./interface/IERC721ArtistProduct.sol";
import "@thirdweb-dev/contracts/eip/ERC165.sol";
import "@thirdweb-dev/contracts/eip/interface/IERC721Receiver.sol";
import "@thirdweb-dev/contracts/external-deps/openzeppelin/utils/Context.sol";
import "@thirdweb-dev/contracts/lib/TWStrings.sol";
import "@thirdweb-dev/contracts/lib/TWAddress.sol";
/**
* @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including
* the Metadata extension, along with ERC-1155 like functionality of "Product" with mint numbers.
* Built to optimize for lower gas during batch mints.
*
* Assumes that an owner cannot have more than 2**64 - 1 (max value of uint64) of supply.
*
* Assumes that the maximum token id cannot exceed 2**256 - 1 (max value of uint256).
*/
contract ERC721ArtistProduct is Context, ERC165, IERC721ArtistProduct {
using TWAddress for address;
using TWStrings for uint256;
// The product offset factor N to represent product
// Example: offset of 6 means that token ids will be:
// product 1 will have the token ids 1000001, 1000002, etc
// product 69 will have the token ids 69000001, 69000002, etc
// The offset provides 10 ** N - 1 possible supply
//enables 9 assets to be minted per product
uint256 constant PRODUCT_OFFSET_MIN = 1;
//enables 1e19-1 assets to be minted, fitting uint64 max
uint256 constant PRODUCT_OFFSET_MAX = 19;
//stores the 10**N value to save math operations
uint256 private _productOffset;
// The number of tokens minted.
uint256 internal _mintCounter;
// The number of tokens burned.
uint256 internal _burnCounter;
// Token name
string private _name;
// Token symbol
string private _symbol;
// Mapping from token ID to ownership details
// An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
mapping(uint256 => TokenOwnership) internal _ownerships;
//Mapping from product ID to product details
mapping(uint256 => ProductData) private _productData;
//Mapping from product ID to artist address
mapping(uint256 => address) private _productArtist;
//Mapping owner address to product balance
mapping(address => mapping(uint256 => uint256)) private _productBalance;
// Mapping owner address to address data
mapping(address => AddressData) private _addressData;
// Mapping from token ID to approved address
mapping(uint256 => address) private _tokenApprovals;
// Mapping from owner to operator approvals
mapping(address => mapping(address => bool)) private _operatorApprovals;
constructor(string memory name_, string memory symbol_, uint256 offset_) {
_name = name_;
_symbol = symbol_;
_setProductOffset(offset_);
}
/**
* @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
*/
function totalSupply() public view override returns (uint256) {
// Counter underflow is impossible as _burnCounter cannot be incremented
// more than _mintCounter times
unchecked {
return _mintCounter - _burnCounter;
}
}
/**
* @dev Burned tokens are calculated here, use _getProductBurned() if you want to count just minted tokens.
*/
function productSupply(uint256 productId) public view returns (uint256) {
// Counter underflow is impossible as numberBurned cannot be incremented
// more than numberMinted times
ProductData memory product = _productData[productId];
unchecked {
return product.numberMinted - product.numberBurned;
}
}
/**
* Returns the total amount of tokens minted in the contract.
*/
function _totalMinted() internal view returns (uint256) {
return _mintCounter;
}
/**
* Returns the total amount of tokens burned in the contract.
*/
function _totalBurned() internal view returns (uint256) {
return _burnCounter;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override(ERC165) returns (bool) {
return
interfaceId == type(IERC721).interfaceId ||
interfaceId == type(IERC721Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
/**
* @dev See {IERC721-balanceOf}.
*/
function productBalanceOf(
address owner,
uint256 productId
) public view returns (uint256) {
if (owner == address(0)) revert BalanceQueryForZeroAddress();
return _productBalance[owner][productId];
}
/**
* @dev See {IERC721-balanceOf}.
*/
function balanceOf(address owner) public view override returns (uint256) {
if (owner == address(0)) revert BalanceQueryForZeroAddress();
return uint256(_addressData[owner].balance);
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _getAddressData(address owner) internal view returns (AddressData memory) {
return _addressData[owner];
}
/**
* Returns the number of tokens minted by `owner`.
*/
function _numberMinted(address owner) internal view returns (uint256) {
return uint256(_addressData[owner].numberMinted);
}
/**
* Returns the number of tokens burned by or on behalf of `owner`.
*/
function _numberBurned(address owner) internal view returns (uint256) {
return uint256(_addressData[owner].numberBurned);
}
/**
* Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getAux(address owner) internal view returns (uint64) {
return _addressData[owner].aux;
}
/**
* Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setAux(address owner, uint64 aux) internal {
_addressData[owner].aux = aux;
}
/**
* Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
*/
function _getArtist(uint256 _productId) internal view returns (address) {
return _productArtist[_productId];
}
/**
* Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
* If there are multiple variables, please pack them into a uint64.
*/
function _setArtist(uint256 _productId, address _artist) internal {
_productArtist[_productId] = _artist;
}
/**
* Gets the product configuration
*/
function _getProduct(uint256 _productId) internal view returns (ProductData memory) {
return _productData[_productId];
}
/**
* Gets the product offset
*/
function _getProductOffset() internal view returns (uint256 _offset) {
uint256 offset = _productOffset;
while (offset % 10 == 0) {
offset /= 10;
++_offset;
}
}
/**
* Sets the product offset if prior to minting
*/
function _setProductOffset(uint256 _offset) internal {
if (_mintCounter > 0) revert ProductOffsetAfterMinting();
if (_offset < PRODUCT_OFFSET_MIN || _offset > PRODUCT_OFFSET_MAX) {
revert ProductOffsetOutsideRange(
_offset,
PRODUCT_OFFSET_MIN,
PRODUCT_OFFSET_MAX
);
}
_productOffset = 10 ** _offset;
}
/**
* Gets the maximum productId based on the product offset
*/
function _getProductMaxId() internal view returns (uint256) {
return type(uint256).max / _productOffset - _productOffset;
}
/**
* Gets the maximum product supply based on the product offset
*/
function _getProductMaxQuantity() internal view returns (uint64) {
return uint64(_productOffset - 1);
}
/**
* Gets the product token supply
*/
function _getProductQuantity(uint256 _productId) internal view returns (uint64) {
return _productData[_productId].quantity;
}
/**
* Creates a product or modifies the supply
*/
function _setProductQuantity(uint256 _productId, uint64 _quantity) internal {
if (_getProductMaxId() < _productId)
revert ProductIdExceedsOffsetMaximum(_productId, _getProductMaxId());
if (_getProductMaxQuantity() < _quantity)
revert ProductQuantityExceedsOffsetMaximum(
_quantity,
_getProductMaxQuantity()
);
ProductData storage product = _productData[_productId];
uint64 oldQuantity = product.quantity;
if (_quantity < product.numberMinted) revert ProductMintsExceedsQuantity();
product.quantity = _quantity;
emit ProductQuantityUpdated(_productId, oldQuantity, _quantity);
}
/**
* Gets the product tokens minted
*/
function _getProductMinted(uint256 _productId) internal view returns (uint64) {
return _productData[_productId].numberMinted;
}
/**
* Gets the product tokens burn
*/
function _getProductBurned(uint256 _productId) internal view returns (uint64) {
return _productData[_productId].numberBurned;
}
/**
* Gets the product locked status (minting)
*/
function _getProductLocked(uint256 _productId) internal view returns (bool) {
return _productData[_productId].locked;
}
/**
* Sets the locked condition for a product (minting)
*/
function _setProductLocked(uint256 _productId, bool _locked) internal {
ProductData storage product = _productData[_productId];
if (product.quantity == 0) revert ProductNotConfigured();
product.locked = _locked;
emit ProductMintingRestricted(_productId, _locked);
}
/**
* Gets the product frozen status (transfers)
*/
function _getProductFrozen(uint256 _productId) internal view returns (bool) {
return _productData[_productId].frozen;
}
/**
* Sets the frozen condition for a product (transfers)
*/
function _setProductFrozen(uint256 _productId, bool _frozen) internal {
ProductData storage product = _productData[_productId];
if (product.quantity == 0) revert ProductNotConfigured();
product.frozen = _frozen;
emit ProductTransfersRestricted(_productId, _frozen);
}
/**
* Gets the product burnable status (burning)
*/
function _getProductBurnable(uint256 _productId) internal view returns (bool) {
return _productData[_productId].burnable;
}
/**
* Sets the burnable condition for a product (burning)
*/
function _setProductBurnable(uint256 _productId, bool _burnable) internal {
ProductData storage product = _productData[_productId];
if (product.quantity == 0) revert ProductNotConfigured();
product.burnable = _burnable;
emit ProductBurningRestricted(_productId, !_burnable);
}
/**
* Gets the product burnable status (burning)
*/
function _getProductUniqueMetadata(uint256 _productId) internal view returns (bool) {
return _productData[_productId].uniqueMetadata;
}
/**
* Sets the burnable condition for a product (burning)
*/
function _setProductUniqueMetadata(uint256 _productId, bool _isUnique) internal {
ProductData storage product = _productData[_productId];
if (product.quantity == 0) revert ProductNotConfigured();
product.uniqueMetadata = _isUnique;
emit ProductUniqueTokenMetadata(_productId, _isUnique);
}
/**
* Gets the product id for a token
*/
function _getProductId(uint256 _tokenId) internal view returns (uint256 _productId) {
if (_tokenId < _productOffset) revert ProductQueryForImpossibleToken();
_productId = _tokenId / _productOffset;
if (_productData[_productId].quantity == 0)
revert ProductQueryForNonexistantToken();
}
/**
* Gets the next token id for product given a quantity
* Checks that range of tokens wont exceed supply
*/
function _getNextTokenIds(
uint256 _productId,
uint256 _quantity
) internal view returns (uint256 startTokenId, uint256 finalTokenId) {
ProductData memory product = _productData[_productId];
if (product.quantity == 0) revert ProductNotConfigured();
if (product.quantity < product.numberMinted + _quantity)
revert ProductMintsExceedsQuantity();
startTokenId = _productId * _productOffset + product.numberMinted + 1;
finalTokenId = startTokenId + _quantity - 1;
}
/**
* Gets the previously minted token id for a product
*/
function _getPrevTokenId(uint256 _productId) internal view returns (uint256) {
return _productId * _productOffset + _getProductMinted(_productId);
}
/**
* Gets the first possible token id for a product
*/
function _getMinTokenId(uint256 _productId) internal view returns (uint256) {
return _productId * _productOffset + 1;
}
/**
* Gets the last possible token id for a product
*/
function _getMaxTokenId(uint256 _productId) internal view returns (uint256) {
return _productId * _productOffset + _getProductQuantity(_productId) + 1;
}
/**
* Gas spent here starts off proportional to the maximum mint batch size.
* It gradually moves to O(1) as tokens get transferred around in the collection over time.
*/
function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
uint256 curr = tokenId;
//this will revert if the product is unconfigured
uint256 productId = _getProductId(tokenId);
uint256 minTokenId = _getMinTokenId(productId);
unchecked {
if (minTokenId <= curr)
if (curr <= _getPrevTokenId(productId)) {
TokenOwnership memory ownership = _ownerships[curr];
if (!ownership.burned) {
if (ownership.addr != address(0)) {
return ownership;
}
// Invariant:
// There will always be an ownership that has an address and is not burned
// before an ownership that does not have an address and is not burned.
// Hence, curr will not underflow.
while (minTokenId < curr) {
curr--;
ownership = _ownerships[curr];
if (ownership.addr != address(0)) {
return ownership;
}
}
}
}
}
revert OwnerQueryForNonexistentToken();
}
/**
* @dev See {IERC721-ownerOf}.
*/
function ownerOf(uint256 tokenId) public view override returns (address) {
return _ownershipOf(tokenId).addr;
}
/**
* @dev See {IERC721Metadata-name}.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev See {IERC721Metadata-symbol}.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev See {IERC721Metadata-tokenURI}.
*/
function tokenURI(
uint256 tokenId
) public view virtual override returns (string memory) {
if (!_exists(tokenId)) revert URIQueryForNonexistentToken();
string memory baseURI = _baseURI();
return
bytes(baseURI).length != 0
? string(abi.encodePacked(baseURI, tokenId.toString()))
: "";
}
/**
* @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
* token will be the concatenation of the `baseURI` and the `tokenId`. Empty
* by default, can be overriden in child contracts.
*/
function _baseURI() internal view virtual returns (string memory) {
return "";
}
/**
* @dev See {IERC721-approve}.
*/
function approve(address to, uint256 tokenId) public virtual override {
address owner = ERC721ArtistProduct.ownerOf(tokenId);
if (to == owner) revert ApprovalToCurrentOwner();
if (_msgSender() != owner)
if (!isApprovedForAll(owner, _msgSender())) {
revert ApprovalCallerNotOwnerNorApproved();
}
_approve(to, tokenId, owner);
}
/**
* @dev See {IERC721-getApproved}.
*/
function getApproved(uint256 tokenId) public view override returns (address) {
if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();
return _tokenApprovals[tokenId];
}
/**
* @dev See {IERC721-setApprovalForAll}.
*/
function setApprovalForAll(address operator, bool approved) public virtual override {
if (operator == _msgSender()) revert ApproveToCaller();
_operatorApprovals[_msgSender()][operator] = approved;
emit ApprovalForAll(_msgSender(), operator, approved);
}
/**
* @dev See {IERC721-isApprovedForAll}.
*/
function isApprovedForAll(
address owner,
address operator
) public view virtual override returns (bool) {
return _operatorApprovals[owner][operator];
}
/**
* @dev See {IERC721-transferFrom}.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
_transfer(from, to, tokenId);
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) public virtual override {
safeTransferFrom(from, to, tokenId, "");
}
/**
* @dev See {IERC721-safeTransferFrom}.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes memory _data
) public virtual override {
_transfer(from, to, tokenId);
if (to.isContract())
if (!_checkContractOnERC721Received(from, to, tokenId, _data)) {
revert TransferToNonERC721ReceiverImplementer();
}
}
/**
* @dev Returns whether `tokenId` exists.
*
* Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}.
*
* Tokens start existing when they are minted (`_mint`),
*/
function _exists(uint256 tokenId) internal view returns (bool) {
uint256 productId = _getProductId(tokenId);
return
_getMinTokenId(productId) <= tokenId &&
tokenId <= _getPrevTokenId(productId) &&
!_ownerships[tokenId].burned;
}
/**
* @dev Equivalent to `_safeMint(to, productId, quantity, '')`.
*/
function _safeMint(
address to,
uint256 productId,
uint256 quantity
) internal returns (uint256 startTokenId, uint256 finalTokenId) {
return _safeMint(to, productId, quantity, "");
}
/**
* @dev Safely mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called for each safe transfer.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event.
*/
function _safeMint(
address to,
uint256 productId,
uint256 quantity,
bytes memory _data
) internal returns (uint256 startTokenId, uint256 finalTokenId) {
uint256 startCount = _mintCounter;
if (to == address(0)) revert MintToZeroAddress();
if (quantity == 0) revert MintZeroQuantity();
if (_getProductLocked(productId)) revert ProductMintingDisabled();
//this validates against that total minted wont exceed supply
(startTokenId, finalTokenId) = _getNextTokenIds(productId, quantity);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
// updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
unchecked {
_addressData[to].balance += uint64(quantity);
_addressData[to].numberMinted += uint64(quantity);
_ownerships[startTokenId].addr = to;
_ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
_productBalance[to][productId] += quantity;
uint256 currTokenId = startTokenId;
//increment final token to save on comparison gas in the loops
finalTokenId++;
if (to.isContract()) {
do {
if (_productArtist[productId] != address(0)) {
emit Transfer(address(0), _productArtist[productId], currTokenId);
emit Transfer(_productArtist[productId], to, currTokenId);
} else {
emit Transfer(address(0), to, currTokenId);
}
if (
!_checkContractOnERC721Received(
address(0),
to,
currTokenId++,
_data
)
) {
revert TransferToNonERC721ReceiverImplementer();
}
} while (currTokenId < finalTokenId);
// Reentrancy protection
if (_mintCounter != startCount) revert();
} else {
do {
if (_productArtist[productId] != address(0)) {
emit Transfer(address(0), _productArtist[productId], currTokenId);
emit Transfer(_productArtist[productId], to, currTokenId);
} else {
emit Transfer(address(0), to, currTokenId);
}
++currTokenId;
} while (currTokenId < finalTokenId);
}
_mintCounter += quantity;
_productData[productId].numberMinted += uint64(quantity);
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Mints `quantity` tokens and transfers them to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `quantity` must be greater than 0.
*
* Emits a {Transfer} event.
*/
function _mint(
address to,
uint256 productId,
uint256 quantity
) internal returns (uint256 startTokenId, uint256 finalTokenId) {
if (to == address(0)) revert MintToZeroAddress();
if (quantity == 0) revert MintZeroQuantity();
if (_getProductLocked(productId)) revert ProductMintingDisabled();
//this validates against that total minted wont exceed supply
(startTokenId, finalTokenId) = _getNextTokenIds(productId, quantity);
_beforeTokenTransfers(address(0), to, startTokenId, quantity);
// Overflows are incredibly unrealistic.
// balance or numberMinted overflow if current value of either + quantity > 1.8e19 (2**64) - 1
// updatedIndex overflows if _currentIndex + quantity > 1.2e77 (2**256) - 1
unchecked {
_addressData[to].balance += uint64(quantity);
_addressData[to].numberMinted += uint64(quantity);
_ownerships[startTokenId].addr = to;
_ownerships[startTokenId].startTimestamp = uint64(block.timestamp);
_productBalance[to][productId] += quantity;
uint256 currTokenId = startTokenId;
//increment final token to save on comparison gas in the loops
finalTokenId++;
do {
if (_productArtist[productId] != address(0)) {
emit Transfer(address(0), _productArtist[productId], currTokenId);
emit Transfer(_productArtist[productId], to, currTokenId);
} else {
emit Transfer(address(0), to, currTokenId);
}
++currTokenId;
} while (currTokenId < finalTokenId);
_mintCounter += quantity;
_productData[productId].numberMinted += uint64(quantity);
}
_afterTokenTransfers(address(0), to, startTokenId, quantity);
}
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
*
* Emits a {Transfer} event.
*/
function _transfer(address from, address to, uint256 tokenId) private {
TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
if (prevOwnership.addr != from) revert TransferFromIncorrectOwner();
bool isApprovedOrOwner = (_msgSender() == from ||
isApprovedForAll(from, _msgSender()) ||
getApproved(tokenId) == _msgSender());
if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
if (to == address(0)) revert TransferToZeroAddress();
uint256 productId = _getProductId(tokenId);
if (_getProductFrozen(productId)) revert ProductTransfersDisabled();
_beforeTokenTransfers(from, to, tokenId, 1);
// Clear approvals from the previous owner
_approve(address(0), tokenId, from);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
unchecked {
_addressData[from].balance -= 1;
_addressData[to].balance += 1;
_productBalance[from][productId] -= 1;
_productBalance[to][productId] += 1;
TokenOwnership storage currSlot = _ownerships[tokenId];
currSlot.addr = to;
currSlot.startTimestamp = uint64(block.timestamp);
// If the ownership slot of tokenId+1 is not explicitly set, that means the transfer initiator owns it.
// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
TokenOwnership storage nextSlot = _ownerships[tokenId + 1];
if (nextSlot.addr == address(0)) {
// This will suffice for checking _exists(nextTokenId),
// as a burned slot cannot contain the zero address.
if (tokenId != _getPrevTokenId(productId)) {
// This confirms the current token is not the last token in the product
nextSlot.addr = from;
nextSlot.startTimestamp = prevOwnership.startTimestamp;
}
}
}
emit Transfer(from, to, tokenId);
_afterTokenTransfers(from, to, tokenId, 1);
}
/**
* @dev Equivalent to `_burn(tokenId, false)`.
*/
function _burn(uint256 tokenId) internal virtual {
_burn(tokenId, false);
}
/**
* @dev Destroys `tokenId`.
* The approval is cleared when the token is burned.
*
* Requirements:
*
* - `tokenId` must exist.
*
* Emits a {Transfer} event.
*/
function _burn(uint256 tokenId, bool approvalCheck) internal virtual {
TokenOwnership memory prevOwnership = _ownershipOf(tokenId);
address from = prevOwnership.addr;
if (approvalCheck) {
bool isApprovedOrOwner = (_msgSender() == from ||
isApprovedForAll(from, _msgSender()) ||
getApproved(tokenId) == _msgSender());
if (!isApprovedOrOwner) revert TransferCallerNotOwnerNorApproved();
}
uint256 productId = _getProductId(tokenId);
if (!_getProductBurnable(productId)) revert ProductBurningDisabled();
_beforeTokenTransfers(from, address(0), tokenId, 1);
// Clear approvals from the previous owner
_approve(address(0), tokenId, from);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
// Counter overflow is incredibly unrealistic as tokenId would have to be 2**256.
unchecked {
AddressData storage addressData = _addressData[from];
addressData.balance -= 1;
addressData.numberBurned += 1;
// Keep track of who burned the token, and the timestamp of burning.
TokenOwnership storage currSlot = _ownerships[tokenId];
currSlot.addr = from;
currSlot.startTimestamp = uint64(block.timestamp);
currSlot.burned = true;
_productBalance[from][productId] -= 1;
// If the ownership slot of tokenId+1 is not explicitly set, that means the burn initiator owns it.
// Set the slot of tokenId+1 explicitly in storage to maintain correctness for ownerOf(tokenId+1) calls.
TokenOwnership storage nextSlot = _ownerships[tokenId + 1];
if (nextSlot.addr == address(0)) {
// This will suffice for checking _exists(nextTokenId),
// as a burned slot cannot contain the zero address.
if (tokenId != _getPrevTokenId(productId)) {
// This confirms the current token is not the last token in the product
nextSlot.addr = from;
nextSlot.startTimestamp = prevOwnership.startTimestamp;
}
}
}
emit Transfer(from, address(0), tokenId);
_afterTokenTransfers(from, address(0), tokenId, 1);
// Overflow not possible, as _burnCounter cannot be exceed _mintCounter times.
unchecked {
_burnCounter++;
_productData[productId].numberBurned++;
}
}
/**
* @dev Approve `to` to operate on `tokenId`
*
* Emits a {Approval} event.
*/
function _approve(address to, uint256 tokenId, address owner) private {
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
/**
* @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target contract.
*
* @param from address representing the previous owner of the given token ID
* @param to target address that will receive the tokens
* @param tokenId uint256 ID of the token to be transferred
* @param _data bytes optional data to send along with the call
* @return bool whether the call correctly returned the expected magic value
*/
function _checkContractOnERC721Received(
address from,
address to,
uint256 tokenId,
bytes memory _data
) private returns (bool) {
try
IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data)
returns (bytes4 retval) {
return retval == IERC721Receiver(to).onERC721Received.selector;
} catch (bytes memory reason) {
if (reason.length == 0) {
revert TransferToNonERC721ReceiverImplementer();
} else {
assembly {
revert(add(32, reason), mload(reason))
}
}
}
}
/**
* @dev Hook that is called before a set of serially-ordered token ids are about to be transferred. This includes minting.
* And also called before burning one token.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` will be
* transferred to `to`.
* - When `from` is zero, `tokenId` will be minted for `to`.
* - When `to` is zero, `tokenId` will be burned by `from`.
* - `from` and `to` are never both zero.
*/
function _beforeTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
/**
* @dev Hook that is called after a set of serially-ordered token ids have been transferred. This includes
* minting.
* And also called after one token has been burned.
*
* startTokenId - the first token id to be transferred
* quantity - the amount to be transferred
*
* Calling conditions:
*
* - When `from` and `to` are both non-zero, `from`'s `tokenId` has been
* transferred to `to`.
* - When `from` is zero, `tokenId` has been minted for `to`.
* - When `to` is zero, `tokenId` has been burned by `from`.
* - `from` and `to` are never both zero.
*/
function _afterTokenTransfers(
address from,
address to,
uint256 startTokenId,
uint256 quantity
) internal virtual {}
}// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.0;
import "@thirdweb-dev/contracts/eip/interface/IERC165.sol";
import "@thirdweb-dev/contracts/eip/interface/IERC721.sol";
/// @title EIP-721 Metadata Update Extension
interface IERC4906 is IERC165, IERC721 {
/// @dev This event emits when the metadata of a token is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFT.
event MetadataUpdate(uint256 _tokenId);
/// @dev This event emits when the metadata of a range of tokens is changed.
/// So that the third-party platforms such as NFT market could
/// timely update the images and related attributes of the NFTs.
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
import "@thirdweb-dev/contracts/eip/interface/IERC721A.sol";
interface IERC721ArtistProduct is IERC721A {
/*//////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
// Compiler will pack this into a single 256bit word.
struct ProductData {
// Keeps track of quantity, Realistically, 2**64-1 is more than enough.
uint64 quantity;
// Keeps track of mint count with minimal overhead for tokenomics.
uint64 numberMinted;
// Keeps track of burn count with minimal overhead for tokenomics.
uint64 numberBurned;
//Toggles if mints are disabled for this product
//Defaults to enabled (locked = false)
bool locked;
//Toggles if transfers are disabled for this product
//Defaults to enabled (frozen = false)
bool frozen;
//Toggles if burns are enabled for this product
//Defaults to disabled (burnable = false)
bool burnable;
//Toggles if tokens within the product have unique metadata
//Defaults to non-unique (uniqueMetadata = false)
bool uniqueMetadata;
}
/*//////////////////////////////////////////////////////////////
Methods
//////////////////////////////////////////////////////////////*/
/**
* @dev Returns the total amount of tokens stored by the contract product.
*
* Burned tokens are calculated here, use `_getProductMinted()` if you want to count just minted tokens.
*/
function productSupply(uint256 productId) external view returns (uint256);
function productBalanceOf(
address owner,
uint256 productId
) external view returns (uint256);
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @dev Emitted when a product is created or has its supply modified
event ProductQuantityUpdated(
uint256 indexed productId,
uint64 indexed oldQuantity,
uint64 indexed newQuantity
);
/// @dev Emitted when specific product mints are enabled or disabled
event ProductMintingRestricted(uint256 indexed productId, bool indexed isRestricted);
/// @dev Emitted when specific product transfers are enabled or disabled
event ProductTransfersRestricted(
uint256 indexed productId,
bool indexed isRestricted
);
/// @dev Emitted when specific product burns are enabled or disabled
event ProductBurningRestricted(uint256 indexed productId, bool indexed isRestricted);
/// @dev Emitted when specific products toggle unique token metadata within product
event ProductUniqueTokenMetadata(
uint256 indexed productId,
bool indexed isUniquePerToken
);
/*//////////////////////////////////////////////////////////////
Errors
//////////////////////////////////////////////////////////////*/
/// @dev The product scale must be set prior to minting.
error ProductOffsetAfterMinting();
/// @dev The product scale must be set prior to minting.
error ProductOffsetOutsideRange(uint256 requested, uint256 minumum, uint256 maximum);
/// @dev The productId must not exceed the maximum for the scale.
error ProductIdExceedsOffsetMaximum(uint256 requested, uint256 maximum);
/// @dev The product quantity must not exceed the maximum for the scale.
error ProductQuantityExceedsOffsetMaximum(uint256 requested, uint256 maximum);
/// @dev The product quantity must equal or exceed the number minted.
error ProductMintsExceedsQuantity();
/// @dev The product must be configured by setting a quantity.
error ProductNotConfigured();
/// @dev The token ID is impossible given the product scale.
error ProductQueryForImpossibleToken();
/// @dev The token ID cannot exist given configured product
error ProductQueryForNonexistantToken();
/// @dev Minting is disabled for this product
error ProductMintingDisabled();
/// @dev Burning are disabled for this product
error ProductBurningDisabled();
/// @dev Transfers are disabled for this product
error ProductTransfersDisabled();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
interface IProductCreators {
/*//////////////////////////////////////////////////////////////
Structs
//////////////////////////////////////////////////////////////*/
struct ProductCreatorData {
uint256 index;
mapping(uint256 => CreatorData) creators;
}
struct CreatorData {
address creatorAddress;
string creatorInfo;
}
/*//////////////////////////////////////////////////////////////
Methods
//////////////////////////////////////////////////////////////*/
/// @dev Returns the base URI of the contract.
function getProductCreators(
uint256 _productId
) external view returns (CreatorData[] memory);
/**
* @dev Sets base URI for the token metadata of the contract.
* Only module admin can call this function.
*/
function setProductCreators(
uint256 _productId,
CreatorData[] calldata _creators
) external;
/*//////////////////////////////////////////////////////////////
Events
//////////////////////////////////////////////////////////////*/
/// @dev Emitted when the base URI is updated.
event ProductCreatorsUpdated(uint256 indexed productId, CreatorData[] creators);
/*//////////////////////////////////////////////////////////////
Errors
//////////////////////////////////////////////////////////////*/
/// @dev Module admin is required
error ProductCreatorNotAuthorized();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
import "./IProductCreators.sol";
import "../../eip/interface/IERC4906.sol";
interface IProductDataConnector is IERC4906 {
/*//////////////////////////////////////////////////////////////
Methods
//////////////////////////////////////////////////////////////*/
function setDataContract(address dataContract_) external;
function getDataContract() external view returns (address);
function getProductCreators(
uint256 _productId
) external view returns (IProductCreators.CreatorData[] memory);
function setBaseURI(string calldata _uri) external;
function setTokenURI(uint256 _tokenId, string calldata _uri) external;
function setTokenURIs(
uint256 _tokenId,
uint256 _quantity,
string[] calldata _uris
) external;
function setTokenURIs(uint256[] calldata _tokenIds, string[] calldata _uris) external;
function setProductURI(uint256 _productId, string calldata _uri) external;
/// @dev Emitted when the base URI is updated.
event BaseURIUpdated(string prevURI, string newURI);
/// @dev Emitted when the token URI is updated.
event TokenURIUpdated(uint256 indexed tokenId, string prevURI, string newURI);
/// @dev Emitted when the product URI is updated.
event ProductURIUpdated(uint256 indexed tokenId, string prevURI, string newURI);
error ProductDataContractNotConfigured();
error ProductDataNotAuthorized();
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/// @author Syky - Nathan Rempel
import "./interface/IProductDataConnector.sol";
import "../base/interface/IProductDataContract.sol";
abstract contract ProductDataConnector is IProductDataConnector {
address private _dataContract;
/*//////////////////////////////////////////////////////////////
Public getters
//////////////////////////////////////////////////////////////*/
function getDataContract() external view virtual returns (address) {
return _dataContract;
}
function getProductCreators(
uint256 _productId
) external view virtual returns (IProductCreators.CreatorData[] memory) {
if (_dataContract == address(0)) return new IProductCreators.CreatorData[](0);
return IProductDataContract(_dataContract).getProductCreators(_productId);
}
/*//////////////////////////////////////////////////////////////
Admin setters
//////////////////////////////////////////////////////////////*/
function setDataContract(address dataContract_) external virtual onlyData {
_dataContract = dataContract_;
}
function setBaseURI(string calldata _uri) external virtual onlyData {
_setBaseUri(_uri);
}
function setTokenURI(
uint256 _tokenId,
string calldata _uri
) external virtual onlyData {
_setTokenUri(_tokenId, _uri);
}
function setTokenURIs(
uint256 _tokenId,
uint256 _quantity,
string[] calldata _uris
) external virtual onlyData {
_setTokenUris(_tokenId, _quantity, _uris);
}
function setTokenURIs(
uint256[] calldata _tokenIds,
string[] calldata _uris
) external virtual onlyData {
_setTokenUris(_tokenIds, _uris);
}
function setProductURI(
uint256 _productId,
string calldata _uri
) external virtual onlyData {
_setProductUri(_productId, _uri);
}
/*//////////////////////////////////////////////////////////////
Modifiers
//////////////////////////////////////////////////////////////*/
/// @dev Modifier that checks if the data contract has been configured
modifier onlyConfigured() {
_notConfigured();
_;
}
/// @dev Modifier that checks module admin permissions
modifier onlyData() {
_checkAuth();
_;
}
/*//////////////////////////////////////////////////////////////
Internal functions
//////////////////////////////////////////////////////////////*/
function _checkAuth() internal virtual {
if (!_canSetData()) revert ProductDataNotAuthorized();
}
/// @dev Returns whether product data can be set in the given execution context.
function _canSetData() internal view virtual returns (bool);
function _notConfigured() internal virtual {
if (_dataContract == address(0)) revert ProductDataContractNotConfigured();
}
function _tieredURI(
uint256 _tokenId,
uint256 _productId,
bool _isUnique
) internal view returns (string memory) {
if (_dataContract == address(0)) return "";
return
IProductDataContract(_dataContract).tieredURI(
_tokenId,
_productId,
_isUnique
);
}
function _setBaseUri(string memory _uri) internal onlyConfigured {
string memory prevURI = IProductDataContract(_dataContract).setBaseURI(_uri);
emit BaseURIUpdated(prevURI, _uri);
}
function _setTokenUri(uint256 _tokenId, string memory _uri) internal onlyConfigured {
string memory prevURI = IProductDataContract(_dataContract).setTokenURI(
_tokenId,
_uri
);
emit TokenURIUpdated(_tokenId, prevURI, _uri);
emit MetadataUpdate(_tokenId);
}
function _setTokenUris(
uint256 _tokenId,
uint256 _quantity,
string[] memory _uris
) internal onlyConfigured {
string[] memory _prevURIs = IProductDataContract(_dataContract).setTokenURIs(
_tokenId,
_quantity,
_uris
);
unchecked {
for (uint i; i < _quantity; ) {
emit TokenURIUpdated(_tokenId + i, _prevURIs[i], _uris[i]);
++i;
}
emit BatchMetadataUpdate(_tokenId, _tokenId + _quantity - 1);
}
}
function _setTokenUris(
uint256[] memory _tokenIds,
string[] memory _uris
) internal onlyConfigured {
string[] memory _prevURIs = IProductDataContract(_dataContract).setTokenURIs(
_tokenIds,
_uris
);
uint256 length = _prevURIs.length;
for (uint i; i < length; ) {
emit TokenURIUpdated(_tokenIds[i], _prevURIs[i], _uris[i]);
emit MetadataUpdate(_tokenIds[i]);
unchecked {
++i;
}
}
}
function _setProductUri(
uint256 _productId,
string memory _uri
) internal onlyConfigured {
string memory prevURI = IProductDataContract(_dataContract).setProductURI(
_productId,
_uri
);
emit ProductURIUpdated(_productId, prevURI, _uri);
}
function _runBeforeExtensions(
uint256 _productId,
address _from,
address _to,
uint256 _startTokenId,
uint256 _quantity
) internal {
if (_dataContract == address(0)) return;
IProductDataContract(_dataContract).runBeforeExtensions(
_productId,
_from,
_to,
_startTokenId,
_quantity
);
}
function _runAfterExtensions(
uint256 _productId,
address _from,
address _to,
uint256 _startTokenId,
uint256 _quantity
) internal {
if (_dataContract == address(0)) return;
IProductDataContract(_dataContract).runAfterExtensions(
_productId,
_from,
_to,
_startTokenId,
_quantity
);
}
}{
"optimizer": {
"enabled": true,
"runs": 300
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"defaultAdmin_","type":"address"},{"internalType":"address","name":"defaultToken_","type":"address"},{"internalType":"address","name":"defaultMarket_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CreatorRoleRequired","type":"error"},{"inputs":[],"name":"ProductOwnershipRequired","type":"error"},{"inputs":[],"name":"URIAlreadyUsed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"prevOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"CREATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ENV","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VER","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint32","name":"startTimestamp","type":"uint32"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint64","name":"bidBufferBps","type":"uint64"},{"internalType":"uint64","name":"timeBufferSeconds","type":"uint64"},{"internalType":"uint256","name":"minimumBidAmount","type":"uint256"},{"internalType":"uint256","name":"reserveBidAmount","type":"uint256"}],"internalType":"struct IProductSelfService.AuctionParams","name":"_params","type":"tuple"}],"name":"createAuction","outputs":[{"internalType":"uint256","name":"auctionId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"quantity","type":"uint16"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint80","name":"vipPrice","type":"uint80"},{"internalType":"uint80","name":"pubPrice","type":"uint80"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"vipStartTimestamp","type":"uint32"},{"internalType":"uint32","name":"pubStartTimestamp","type":"uint32"}],"internalType":"struct IProductSelfService.ListingParams","name":"_params","type":"tuple"}],"name":"createListing","outputs":[{"internalType":"uint256","name":"listingId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint16","name":"quantity","type":"uint16"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint80","name":"vipPrice","type":"uint80"},{"internalType":"uint80","name":"pubPrice","type":"uint80"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"vipStartTimestamp","type":"uint32"},{"internalType":"uint32","name":"pubStartTimestamp","type":"uint32"}],"internalType":"struct IProductSelfService.ListingParams[]","name":"_params","type":"tuple[]"}],"name":"createMultipleListings","outputs":[{"internalType":"uint256[]","name":"listingIds","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getEnforceUniqueURI","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProductArtistToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProductIdCounter","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"uri","type":"string"}],"name":"getProductIdFromURI","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProductMarketContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"productId","type":"uint64"}],"name":"getProductOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRoleWithSwitch","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"isUniqueURI","type":"bool"}],"name":"setEnforceUniqueURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"setOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"newCounter","type":"uint64"}],"name":"setProductIdCounter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"listingId","type":"uint256"},{"components":[{"internalType":"uint16","name":"quantity","type":"uint16"},{"internalType":"string","name":"uri","type":"string"},{"internalType":"uint80","name":"vipPrice","type":"uint80"},{"internalType":"uint80","name":"pubPrice","type":"uint80"},{"internalType":"uint32","name":"endTimestamp","type":"uint32"},{"internalType":"uint32","name":"vipStartTimestamp","type":"uint32"},{"internalType":"uint32","name":"pubStartTimestamp","type":"uint32"}],"internalType":"struct IProductSelfService.ListingParams","name":"_params","type":"tuple"}],"name":"updateListing","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
608060405260048054600160a01b600160e01b031916613d0960a61b1790556006805460ff191660011790553480156200003857600080fd5b5060405162001eb338038062001eb38339810160408190526200005b91620001f1565b8282826200006983620000df565b600380546001600160a01b038085166001600160a01b0319928316179092556004805492841692909116919091179055620000a66000846200012f565b620000d37f828634d95e775031b9ff576b159a8509d3053581a8c9c4d7d86899e0afcd882f60006200018c565b5050505050506200023b565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a35050565b60008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b600082815260026020526040808220805490849055905190918391839186917fbd79b86ffe0ab8e8776151514217cd7cacd52c909f66475c3af44e129f0b00ff9190a4505050565b80516001600160a01b0381168114620001ec57600080fd5b919050565b6000806000606084860312156200020757600080fd5b6200021284620001d4565b92506200022260208501620001d4565b91506200023260408501620001d4565b90509250925092565b611c68806200024b6000396000f3fe608060405234801561001057600080fd5b50600436106101985760003560e01c806391d14854116100e3578063d547741f1161008c578063f65d432311610066578063f65d4323146103d4578063f7a17377146103ff578063ff34250a1461041257600080fd5b8063d547741f1461039b578063e16562f4146103ae578063f08a7be2146103c157600080fd5b8063a217fddf116100bd578063a217fddf14610360578063a32fa5b314610368578063a886bc241461037b57600080fd5b806391d1485414610303578063978e649a1461033c5780639ef974381461034d57600080fd5b806346c13919116101455780638aa9d7961161011f5780638aa9d7961461029f5780638aeda25a146102b75780638da5cb5b146102de57600080fd5b806346c139191461025357806349b0feac146102665780637c6285a11461027957600080fd5b80632f2ff15d116101765780632f2ff15d146101fc57806336568abe1461020f5780633c4de40f1461022257600080fd5b806313af40351461019d5780631543a6f1146101b2578063248a9ca3146101ce575b600080fd5b6101b06101ab3660046113f4565b610423565b005b60065460ff165b60405190151581526020015b60405180910390f35b6101ee6101dc366004611411565b60009081526002602052604090205490565b6040519081526020016101c5565b6101b061020a36600461142a565b6104a3565b6101b061021d36600461142a565b61053d565b61024660405180604001604052806005815260200164312e302e3160d81b81525081565b6040516101c5919061147e565b6101b06102613660046114c9565b61059f565b6101ee61027436600461150f565b610839565b610246604051806040016040528060078152602001661350525393915560ca1b81525081565b600454600160a01b90046001600160401b03166101ee565b6101ee7f828634d95e775031b9ff576b159a8509d3053581a8c9c4d7d86899e0afcd882f81565b6000546001600160a01b03165b6040516001600160a01b0390911681526020016101c5565b6101b961031136600461142a565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6003546001600160a01b03166102eb565b6102eb61035b366004611543565b610852565b6101ee600081565b6101b961037636600461142a565b6108c8565b61038e61038936600461156c565b61091e565b6040516101c591906115e0565b6101b06103a936600461142a565b6109db565b6101b06103bc366004611624565b6109f4565b6101b06103cf366004611543565b610a52565b6103e76103e2366004611646565b610aca565b6040516001600160401b0390911681526020016101c5565b6101ee61040d36600461150f565b610aff565b6004546001600160a01b03166102eb565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff166104975760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b60448201526064015b60405180910390fd5b6104a081610c99565b50565b6000828152600260205260409020546104bc9033610cf6565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff161561052f5760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c64657273000000604482015260640161048e565b6105398282610d76565b5050565b336001600160a01b038216146105955760405162461bcd60e51b815260206004820152601a60248201527f43616e206f6e6c792072656e6f756e636520666f722073656c66000000000000604482015260640161048e565b6105398282610dd3565b6105a7610e35565b6004805460405163083d13a560e11b81529182018490526000916001600160a01b039091169063107a274a906024016101e060405180830381865afa1580156105f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061891906117a6565b6020015151600354604051639fc1b85f60e01b815262ffffff831660048201529192506001600160a01b031690639fc1b85f90602401602060405180830381865afa15801561066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068f91906118e9565b6001600160a01b0316336001600160a01b0316146106c057604051630cdff0e560e01b815260040160405180910390fd5b60045460408051610160810190915262ffffff831681526001600160a01b0390911690630c269b0b9085906020808201906106fd90880188611906565b61ffff16815260209081019061071590880188611906565b61ffff16815260209081019061072d90880188611906565b61ffff16815260209081019061074590880188611906565b61ffff16815260200161075e6060880160408901611923565b6001600160401b0316815260200161077c6080880160608901611923565b6001600160401b0316815260200161079a60a0880160808901611940565b63ffffffff1681526020016107b560c0880160a08901611940565b63ffffffff1681526020016107d060e0880160c08901611940565b63ffffffff16815260016020909101526040516001600160e01b031960e085901b168152610802929190600401611a3e565b600060405180830381600087803b15801561081c57600080fd5b505af1158015610830573d6000803e3d6000fd5b50505050505050565b6000610843610e35565b61084c82610ec3565b92915050565b600354604051639fc1b85f60e01b81526001600160401b03831660048201526000916001600160a01b031690639fc1b85f90602401602060405180830381865afa1580156108a4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084c91906118e9565b600082815260016020908152604080832083805290915281205460ff16610915575060008281526001602090815260408083206001600160a01b038516845290915290205460ff1661084c565b50600192915050565b6060610928610e35565b81806001600160401b03811115610941576109416116a5565b60405190808252806020026020018201604052801561096a578160200160208202803683370190505b50915060005b818110156109d3576109a485858381811061098d5761098d611a53565b905060200281019061099f9190611a69565b610ec3565b8382815181106109b6576109b6611a53565b6020908102919091010152806109cb81611a9f565b915050610970565b505092915050565b6000828152600260205260409020546105959033610cf6565b6000546001600160a01b03163314610a3f5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161048e565b6006805460ff1916911515919091179055565b6000546001600160a01b03163314610a9d5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161048e565b600480546001600160401b03909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b600060058383604051610ade929190611ab8565b908152604051908190036020019020546001600160401b0316905092915050565b6000610b09610e35565b6000610b1f6001610b1a8580611ac8565b611028565b905060006040518060e001604052808366ffffffffffffff168152602001856020016020810190610b509190611940565b63ffffffff168152602001610b6b6060870160408801611940565b63ffffffff168152602001610b866080870160608801611543565b6001600160401b03168152602001610ba460a0870160808801611543565b6001600160401b03908116825260a08781013560208085019190915260c0808a01356040958601526004805486516232087b60ea1b8152885166ffffffffffffff16928101929092529287015163ffffffff9081166024830152958701519095166044860152606086015184166064860152608086015190931660848501529084015160a48401529083015160c48301529192506001600160a01b039091169063c821ec009060e4015b6020604051808303816000875af1158015610c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c919190611b15565b949350505050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a35050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff1661053957610d34816001600160a01b0316601461123d565b610d3f83602061123d565b604051602001610d50929190611b2e565b60408051601f198184030181529082905262461bcd60e51b825261048e9160040161147e565b60008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b610ddd8282610cf6565b60008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16158015610ea357503360009081527f67d0884ddda2f85585a2a6de0aeff7e8d3a80261ab9caf8b46e05d66ef1a6fe6602052604090205460ff16155b15610ec1576040516380510fe160e01b815260040160405180910390fd5b565b600080610ee7610ed66020850185611906565b61ffff16610b1a6020860186611ac8565b905060006040518061016001604052808362ffffff168152602001856000016020810190610f159190611906565b61ffff168152602090810190610f2d90870187611906565b61ffff168152602090810190610f4590870187611906565b61ffff168152602090810190610f5d90870187611906565b61ffff168152602001610f766060870160408801611923565b6001600160401b03168152602001610f946080870160608801611923565b6001600160401b03168152602001610fb260a0870160808801611940565b63ffffffff168152602001610fcd60c0870160a08801611940565b63ffffffff168152602001610fe860e0870160c08801611940565b63ffffffff1681526001602090910152600480546040516324cf0a2d60e11b81529293506001600160a01b03169163499e145a91610c4e91859101611ba3565b60065460009060ff16801561106657506005838360405161104a929190611ab8565b908152604051908190036020019020546001600160401b031615155b1561108457604051633a977e0160e11b815260040160405180910390fd5b506004805467ffffffffffffffff60a01b198116600160a01b918290046001600160401b0390811660010180821693840292909217845560035460405163148977e160e31b81529485019390935286166024840152916001600160a01b039091169063a44bbf0890604401600060405180830381600087803b15801561110957600080fd5b505af115801561111d573d6000803e3d6000fd5b50506003546040516318fb442760e01b81526001600160401b03851660048201523360248201526001600160a01b0390911692506318fb44279150604401600060405180830381600087803b15801561117557600080fd5b505af1158015611189573d6000803e3d6000fd5b5050600354604051636a01dfc360e11b81526001600160a01b03909116925063d403bf8691506111c190849087908790600401611bb2565b600060405180830381600087803b1580156111db57600080fd5b505af11580156111ef573d6000803e3d6000fd5b505050508060058484604051611206929190611ab8565b90815260405190819003602001902080546001600160401b039290921667ffffffffffffffff199092169190911790559392505050565b6060600061124c836002611bf1565b611257906002611c08565b6001600160401b0381111561126e5761126e6116a5565b6040519080825280601f01601f191660200182016040528015611298576020820181803683370190505b509050600360fc1b816000815181106112b3576112b3611a53565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106112e2576112e2611a53565b60200101906001600160f81b031916908160001a9053506000611306846002611bf1565b611311906001611c08565b90505b6001811115611389576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061134557611345611a53565b1a60f81b82828151811061135b5761135b611a53565b60200101906001600160f81b031916908160001a90535060049490941c9361138281611c1b565b9050611314565b5083156113d85760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161048e565b9392505050565b6001600160a01b03811681146104a057600080fd5b60006020828403121561140657600080fd5b81356113d8816113df565b60006020828403121561142357600080fd5b5035919050565b6000806040838503121561143d57600080fd5b82359150602083013561144f816113df565b809150509250929050565b60005b8381101561147557818101518382015260200161145d565b50506000910152565b602081526000825180602084015261149d81604085016020870161145a565b601f01601f19169190910160400192915050565b600060e082840312156114c357600080fd5b50919050565b600080604083850312156114dc57600080fd5b8235915060208301356001600160401b038111156114f957600080fd5b611505858286016114b1565b9150509250929050565b60006020828403121561152157600080fd5b81356001600160401b0381111561153757600080fd5b610c91848285016114b1565b60006020828403121561155557600080fd5b81356001600160401b03811681146113d857600080fd5b6000806020838503121561157f57600080fd5b82356001600160401b038082111561159657600080fd5b818501915085601f8301126115aa57600080fd5b8135818111156115b957600080fd5b8660208260051b85010111156115ce57600080fd5b60209290920196919550909350505050565b6020808252825182820181905260009190848201906040850190845b81811015611618578351835292840192918401916001016115fc565b50909695505050505050565b60006020828403121561163657600080fd5b813580151581146113d857600080fd5b6000806020838503121561165957600080fd5b82356001600160401b038082111561167057600080fd5b818501915085601f83011261168457600080fd5b81358181111561169357600080fd5b8660208285010111156115ce57600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156116eb57634e487b7160e01b600052604160045260246000fd5b60405290565b6040516101c081016001600160401b03811182821017156116eb57634e487b7160e01b600052604160045260246000fd5b805162ffffff8116811461173557600080fd5b919050565b805160ff8116811461173557600080fd5b63ffffffff811681146104a057600080fd5b80516117358161174b565b61ffff811681146104a057600080fd5b805161173581611768565b69ffffffffffffffffffff811681146104a057600080fd5b805161173581611783565b60008183036101e08112156117ba57600080fd5b6117c26116bb565b835181526101c080601f19840112156117da57600080fd5b6117e26116f1565b92506117f060208601611722565b83526117fe6040860161173a565b602084015261180f6060860161175d565b604084015261182060808601611778565b606084015261183160a08601611778565b608084015261184260c08601611778565b60a084015261185360e0860161179b565b60c084015261010061186681870161179b565b60e085015261012061187981880161175d565b82860152610140915061188d82880161175d565b9085015261016061189f87820161175d565b8286015261018091506118b3828801611778565b908501526101a06118c5878201611778565b828601526118d4838801611778565b90850152505060208101919091529392505050565b6000602082840312156118fb57600080fd5b81516113d8816113df565b60006020828403121561191857600080fd5b81356113d881611768565b60006020828403121561193557600080fd5b81356113d881611783565b60006020828403121561195257600080fd5b81356113d88161174b565b805162ffffff168252602081015161197b602084018261ffff169052565b506040810151611991604084018261ffff169052565b5060608101516119a7606084018261ffff169052565b5060808101516119bd608084018261ffff169052565b5060a08101516119d860a08401826001600160401b03169052565b5060c08101516119f360c08401826001600160401b03169052565b5060e0810151611a0b60e084018263ffffffff169052565b506101008181015163ffffffff908116918401919091526101208083015190911690830152610140908101511515910152565b82815261018081016113d8602083018461195d565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611a7f57600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611ab157611ab1611a89565b5060010190565b8183823760009101908152919050565b6000808335601e19843603018112611adf57600080fd5b8301803591506001600160401b03821115611af957600080fd5b602001915036819003821315611b0e57600080fd5b9250929050565b600060208284031215611b2757600080fd5b5051919050565b7f5065726d697373696f6e733a206163636f756e74200000000000000000000000815260008351611b6681601585016020880161145a565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351611b9781602684016020880161145a565b01602601949350505050565b610160810161084c828461195d565b6001600160401b038416815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b808202811582820484141761084c5761084c611a89565b8082018082111561084c5761084c611a89565b600081611c2a57611c2a611a89565b50600019019056fea2646970667358221220256ca23ca09626f7eb63062c83e7d2e11ea4e1069213c6e51081ee55b78303c364736f6c63430008140033000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49000000000000000000000000a1cf615003464ab30c1c6a7c926f52788e64077d0000000000000000000000001e6c04832c3b776094287cfcad93f359f6002f35
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101985760003560e01c806391d14854116100e3578063d547741f1161008c578063f65d432311610066578063f65d4323146103d4578063f7a17377146103ff578063ff34250a1461041257600080fd5b8063d547741f1461039b578063e16562f4146103ae578063f08a7be2146103c157600080fd5b8063a217fddf116100bd578063a217fddf14610360578063a32fa5b314610368578063a886bc241461037b57600080fd5b806391d1485414610303578063978e649a1461033c5780639ef974381461034d57600080fd5b806346c13919116101455780638aa9d7961161011f5780638aa9d7961461029f5780638aeda25a146102b75780638da5cb5b146102de57600080fd5b806346c139191461025357806349b0feac146102665780637c6285a11461027957600080fd5b80632f2ff15d116101765780632f2ff15d146101fc57806336568abe1461020f5780633c4de40f1461022257600080fd5b806313af40351461019d5780631543a6f1146101b2578063248a9ca3146101ce575b600080fd5b6101b06101ab3660046113f4565b610423565b005b60065460ff165b60405190151581526020015b60405180910390f35b6101ee6101dc366004611411565b60009081526002602052604090205490565b6040519081526020016101c5565b6101b061020a36600461142a565b6104a3565b6101b061021d36600461142a565b61053d565b61024660405180604001604052806005815260200164312e302e3160d81b81525081565b6040516101c5919061147e565b6101b06102613660046114c9565b61059f565b6101ee61027436600461150f565b610839565b610246604051806040016040528060078152602001661350525393915560ca1b81525081565b600454600160a01b90046001600160401b03166101ee565b6101ee7f828634d95e775031b9ff576b159a8509d3053581a8c9c4d7d86899e0afcd882f81565b6000546001600160a01b03165b6040516001600160a01b0390911681526020016101c5565b6101b961031136600461142a565b60009182526001602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6003546001600160a01b03166102eb565b6102eb61035b366004611543565b610852565b6101ee600081565b6101b961037636600461142a565b6108c8565b61038e61038936600461156c565b61091e565b6040516101c591906115e0565b6101b06103a936600461142a565b6109db565b6101b06103bc366004611624565b6109f4565b6101b06103cf366004611543565b610a52565b6103e76103e2366004611646565b610aca565b6040516001600160401b0390911681526020016101c5565b6101ee61040d36600461150f565b610aff565b6004546001600160a01b03166102eb565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff166104975760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b60448201526064015b60405180910390fd5b6104a081610c99565b50565b6000828152600260205260409020546104bc9033610cf6565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff161561052f5760405162461bcd60e51b815260206004820152601d60248201527f43616e206f6e6c79206772616e7420746f206e6f6e20686f6c64657273000000604482015260640161048e565b6105398282610d76565b5050565b336001600160a01b038216146105955760405162461bcd60e51b815260206004820152601a60248201527f43616e206f6e6c792072656e6f756e636520666f722073656c66000000000000604482015260640161048e565b6105398282610dd3565b6105a7610e35565b6004805460405163083d13a560e11b81529182018490526000916001600160a01b039091169063107a274a906024016101e060405180830381865afa1580156105f4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061061891906117a6565b6020015151600354604051639fc1b85f60e01b815262ffffff831660048201529192506001600160a01b031690639fc1b85f90602401602060405180830381865afa15801561066b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061068f91906118e9565b6001600160a01b0316336001600160a01b0316146106c057604051630cdff0e560e01b815260040160405180910390fd5b60045460408051610160810190915262ffffff831681526001600160a01b0390911690630c269b0b9085906020808201906106fd90880188611906565b61ffff16815260209081019061071590880188611906565b61ffff16815260209081019061072d90880188611906565b61ffff16815260209081019061074590880188611906565b61ffff16815260200161075e6060880160408901611923565b6001600160401b0316815260200161077c6080880160608901611923565b6001600160401b0316815260200161079a60a0880160808901611940565b63ffffffff1681526020016107b560c0880160a08901611940565b63ffffffff1681526020016107d060e0880160c08901611940565b63ffffffff16815260016020909101526040516001600160e01b031960e085901b168152610802929190600401611a3e565b600060405180830381600087803b15801561081c57600080fd5b505af1158015610830573d6000803e3d6000fd5b50505050505050565b6000610843610e35565b61084c82610ec3565b92915050565b600354604051639fc1b85f60e01b81526001600160401b03831660048201526000916001600160a01b031690639fc1b85f90602401602060405180830381865afa1580156108a4573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061084c91906118e9565b600082815260016020908152604080832083805290915281205460ff16610915575060008281526001602090815260408083206001600160a01b038516845290915290205460ff1661084c565b50600192915050565b6060610928610e35565b81806001600160401b03811115610941576109416116a5565b60405190808252806020026020018201604052801561096a578160200160208202803683370190505b50915060005b818110156109d3576109a485858381811061098d5761098d611a53565b905060200281019061099f9190611a69565b610ec3565b8382815181106109b6576109b6611a53565b6020908102919091010152806109cb81611a9f565b915050610970565b505092915050565b6000828152600260205260409020546105959033610cf6565b6000546001600160a01b03163314610a3f5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161048e565b6006805460ff1916911515919091179055565b6000546001600160a01b03163314610a9d5760405162461bcd60e51b815260206004820152600e60248201526d139bdd08185d5d1a1bdc9a5e995960921b604482015260640161048e565b600480546001600160401b03909216600160a01b0267ffffffffffffffff60a01b19909216919091179055565b600060058383604051610ade929190611ab8565b908152604051908190036020019020546001600160401b0316905092915050565b6000610b09610e35565b6000610b1f6001610b1a8580611ac8565b611028565b905060006040518060e001604052808366ffffffffffffff168152602001856020016020810190610b509190611940565b63ffffffff168152602001610b6b6060870160408801611940565b63ffffffff168152602001610b866080870160608801611543565b6001600160401b03168152602001610ba460a0870160808801611543565b6001600160401b03908116825260a08781013560208085019190915260c0808a01356040958601526004805486516232087b60ea1b8152885166ffffffffffffff16928101929092529287015163ffffffff9081166024830152958701519095166044860152606086015184166064860152608086015190931660848501529084015160a48401529083015160c48301529192506001600160a01b039091169063c821ec009060e4015b6020604051808303816000875af1158015610c6d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c919190611b15565b949350505050565b600080546001600160a01b0383811673ffffffffffffffffffffffffffffffffffffffff19831681178455604051919092169283917f8292fce18fa69edf4db7b94ea2e58241df0ae57f97e0a6c9b29067028bf92d769190a35050565b60008281526001602090815260408083206001600160a01b038516845290915290205460ff1661053957610d34816001600160a01b0316601461123d565b610d3f83602061123d565b604051602001610d50929190611b2e565b60408051601f198184030181529082905262461bcd60e51b825261048e9160040161147e565b60008281526001602081815260408084206001600160a01b0386168086529252808420805460ff19169093179092559051339285917f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d9190a45050565b610ddd8282610cf6565b60008281526001602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b3360009081527fa6eef7e35abe7026729641147f7915573c7e97b47efa546f5f6e3230263bcb49602052604090205460ff16158015610ea357503360009081527f67d0884ddda2f85585a2a6de0aeff7e8d3a80261ab9caf8b46e05d66ef1a6fe6602052604090205460ff16155b15610ec1576040516380510fe160e01b815260040160405180910390fd5b565b600080610ee7610ed66020850185611906565b61ffff16610b1a6020860186611ac8565b905060006040518061016001604052808362ffffff168152602001856000016020810190610f159190611906565b61ffff168152602090810190610f2d90870187611906565b61ffff168152602090810190610f4590870187611906565b61ffff168152602090810190610f5d90870187611906565b61ffff168152602001610f766060870160408801611923565b6001600160401b03168152602001610f946080870160608801611923565b6001600160401b03168152602001610fb260a0870160808801611940565b63ffffffff168152602001610fcd60c0870160a08801611940565b63ffffffff168152602001610fe860e0870160c08801611940565b63ffffffff1681526001602090910152600480546040516324cf0a2d60e11b81529293506001600160a01b03169163499e145a91610c4e91859101611ba3565b60065460009060ff16801561106657506005838360405161104a929190611ab8565b908152604051908190036020019020546001600160401b031615155b1561108457604051633a977e0160e11b815260040160405180910390fd5b506004805467ffffffffffffffff60a01b198116600160a01b918290046001600160401b0390811660010180821693840292909217845560035460405163148977e160e31b81529485019390935286166024840152916001600160a01b039091169063a44bbf0890604401600060405180830381600087803b15801561110957600080fd5b505af115801561111d573d6000803e3d6000fd5b50506003546040516318fb442760e01b81526001600160401b03851660048201523360248201526001600160a01b0390911692506318fb44279150604401600060405180830381600087803b15801561117557600080fd5b505af1158015611189573d6000803e3d6000fd5b5050600354604051636a01dfc360e11b81526001600160a01b03909116925063d403bf8691506111c190849087908790600401611bb2565b600060405180830381600087803b1580156111db57600080fd5b505af11580156111ef573d6000803e3d6000fd5b505050508060058484604051611206929190611ab8565b90815260405190819003602001902080546001600160401b039290921667ffffffffffffffff199092169190911790559392505050565b6060600061124c836002611bf1565b611257906002611c08565b6001600160401b0381111561126e5761126e6116a5565b6040519080825280601f01601f191660200182016040528015611298576020820181803683370190505b509050600360fc1b816000815181106112b3576112b3611a53565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106112e2576112e2611a53565b60200101906001600160f81b031916908160001a9053506000611306846002611bf1565b611311906001611c08565b90505b6001811115611389576f181899199a1a9b1b9c1cb0b131b232b360811b85600f166010811061134557611345611a53565b1a60f81b82828151811061135b5761135b611a53565b60200101906001600160f81b031916908160001a90535060049490941c9361138281611c1b565b9050611314565b5083156113d85760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e74604482015260640161048e565b9392505050565b6001600160a01b03811681146104a057600080fd5b60006020828403121561140657600080fd5b81356113d8816113df565b60006020828403121561142357600080fd5b5035919050565b6000806040838503121561143d57600080fd5b82359150602083013561144f816113df565b809150509250929050565b60005b8381101561147557818101518382015260200161145d565b50506000910152565b602081526000825180602084015261149d81604085016020870161145a565b601f01601f19169190910160400192915050565b600060e082840312156114c357600080fd5b50919050565b600080604083850312156114dc57600080fd5b8235915060208301356001600160401b038111156114f957600080fd5b611505858286016114b1565b9150509250929050565b60006020828403121561152157600080fd5b81356001600160401b0381111561153757600080fd5b610c91848285016114b1565b60006020828403121561155557600080fd5b81356001600160401b03811681146113d857600080fd5b6000806020838503121561157f57600080fd5b82356001600160401b038082111561159657600080fd5b818501915085601f8301126115aa57600080fd5b8135818111156115b957600080fd5b8660208260051b85010111156115ce57600080fd5b60209290920196919550909350505050565b6020808252825182820181905260009190848201906040850190845b81811015611618578351835292840192918401916001016115fc565b50909695505050505050565b60006020828403121561163657600080fd5b813580151581146113d857600080fd5b6000806020838503121561165957600080fd5b82356001600160401b038082111561167057600080fd5b818501915085601f83011261168457600080fd5b81358181111561169357600080fd5b8660208285010111156115ce57600080fd5b634e487b7160e01b600052604160045260246000fd5b604080519081016001600160401b03811182821017156116eb57634e487b7160e01b600052604160045260246000fd5b60405290565b6040516101c081016001600160401b03811182821017156116eb57634e487b7160e01b600052604160045260246000fd5b805162ffffff8116811461173557600080fd5b919050565b805160ff8116811461173557600080fd5b63ffffffff811681146104a057600080fd5b80516117358161174b565b61ffff811681146104a057600080fd5b805161173581611768565b69ffffffffffffffffffff811681146104a057600080fd5b805161173581611783565b60008183036101e08112156117ba57600080fd5b6117c26116bb565b835181526101c080601f19840112156117da57600080fd5b6117e26116f1565b92506117f060208601611722565b83526117fe6040860161173a565b602084015261180f6060860161175d565b604084015261182060808601611778565b606084015261183160a08601611778565b608084015261184260c08601611778565b60a084015261185360e0860161179b565b60c084015261010061186681870161179b565b60e085015261012061187981880161175d565b82860152610140915061188d82880161175d565b9085015261016061189f87820161175d565b8286015261018091506118b3828801611778565b908501526101a06118c5878201611778565b828601526118d4838801611778565b90850152505060208101919091529392505050565b6000602082840312156118fb57600080fd5b81516113d8816113df565b60006020828403121561191857600080fd5b81356113d881611768565b60006020828403121561193557600080fd5b81356113d881611783565b60006020828403121561195257600080fd5b81356113d88161174b565b805162ffffff168252602081015161197b602084018261ffff169052565b506040810151611991604084018261ffff169052565b5060608101516119a7606084018261ffff169052565b5060808101516119bd608084018261ffff169052565b5060a08101516119d860a08401826001600160401b03169052565b5060c08101516119f360c08401826001600160401b03169052565b5060e0810151611a0b60e084018263ffffffff169052565b506101008181015163ffffffff908116918401919091526101208083015190911690830152610140908101511515910152565b82815261018081016113d8602083018461195d565b634e487b7160e01b600052603260045260246000fd5b6000823560de19833603018112611a7f57600080fd5b9190910192915050565b634e487b7160e01b600052601160045260246000fd5b600060018201611ab157611ab1611a89565b5060010190565b8183823760009101908152919050565b6000808335601e19843603018112611adf57600080fd5b8301803591506001600160401b03821115611af957600080fd5b602001915036819003821315611b0e57600080fd5b9250929050565b600060208284031215611b2757600080fd5b5051919050565b7f5065726d697373696f6e733a206163636f756e74200000000000000000000000815260008351611b6681601585016020880161145a565b7001034b99036b4b9b9b4b733903937b6329607d1b6015918401918201528351611b9781602684016020880161145a565b01602601949350505050565b610160810161084c828461195d565b6001600160401b038416815260406020820152816040820152818360608301376000818301606090810191909152601f909201601f1916010192915050565b808202811582820484141761084c5761084c611a89565b8082018082111561084c5761084c611a89565b600081611c2a57611c2a611a89565b50600019019056fea2646970667358221220256ca23ca09626f7eb63062c83e7d2e11ea4e1069213c6e51081ee55b78303c364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49000000000000000000000000a1cf615003464ab30c1c6a7c926f52788e64077d0000000000000000000000001e6c04832c3b776094287cfcad93f359f6002f35
-----Decoded View---------------
Arg [0] : defaultAdmin_ (address): 0xf9E30Ba8Df802EeF5A0fb239d59dee05f18B2e49
Arg [1] : defaultToken_ (address): 0xa1cF615003464Ab30c1c6A7c926f52788E64077d
Arg [2] : defaultMarket_ (address): 0x1e6C04832C3b776094287cfcaD93F359f6002F35
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000f9e30ba8df802eef5a0fb239d59dee05f18b2e49
Arg [1] : 000000000000000000000000a1cf615003464ab30c1c6a7c926f52788e64077d
Arg [2] : 0000000000000000000000001e6c04832c3b776094287cfcad93f359f6002f35
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.