Contract Name:
MinterFilterV1
Contract Source Code:
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableMap.sol)
pragma solidity ^0.8.0;
import "./EnumerableSet.sol";
/**
* @dev Library for managing an enumerable variant of Solidity's
* https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
* type.
*
* Maps have the following properties:
*
* - Entries are added, removed, and checked for existence in constant time
* (O(1)).
* - Entries are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableMap for EnumerableMap.UintToAddressMap;
*
* // Declare a set state variable
* EnumerableMap.UintToAddressMap private myMap;
* }
* ```
*
* As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are
* supported.
*/
library EnumerableMap {
using EnumerableSet for EnumerableSet.Bytes32Set;
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Map type with
// bytes32 keys and values.
// The Map implementation uses private functions, and user-facing
// implementations (such as Uint256ToAddressMap) are just wrappers around
// the underlying Map.
// This means that we can only create new EnumerableMaps for types that fit
// in bytes32.
struct Map {
// Storage of keys
EnumerableSet.Bytes32Set _keys;
mapping(bytes32 => bytes32) _values;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function _set(
Map storage map,
bytes32 key,
bytes32 value
) private returns (bool) {
map._values[key] = value;
return map._keys.add(key);
}
/**
* @dev Removes a key-value pair from a map. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function _remove(Map storage map, bytes32 key) private returns (bool) {
delete map._values[key];
return map._keys.remove(key);
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function _contains(Map storage map, bytes32 key) private view returns (bool) {
return map._keys.contains(key);
}
/**
* @dev Returns the number of key-value pairs in the map. O(1).
*/
function _length(Map storage map) private view returns (uint256) {
return map._keys.length();
}
/**
* @dev Returns the key-value pair stored at position `index` in the map. O(1).
*
* Note that there are no guarantees on the ordering of entries inside the
* array, and it may change when more entries are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) {
bytes32 key = map._keys.at(index);
return (key, map._values[key]);
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*/
function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) {
bytes32 value = map._values[key];
if (value == bytes32(0)) {
return (_contains(map, key), bytes32(0));
} else {
return (true, value);
}
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function _get(Map storage map, bytes32 key) private view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), "EnumerableMap: nonexistent key");
return value;
}
/**
* @dev Same as {_get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {_tryGet}.
*/
function _get(
Map storage map,
bytes32 key,
string memory errorMessage
) private view returns (bytes32) {
bytes32 value = map._values[key];
require(value != 0 || _contains(map, key), errorMessage);
return value;
}
// UintToAddressMap
struct UintToAddressMap {
Map _inner;
}
/**
* @dev Adds a key-value pair to a map, or updates the value for an existing
* key. O(1).
*
* Returns true if the key was added to the map, that is if it was not
* already present.
*/
function set(
UintToAddressMap storage map,
uint256 key,
address value
) internal returns (bool) {
return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the key was removed from the map, that is if it was present.
*/
function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
return _remove(map._inner, bytes32(key));
}
/**
* @dev Returns true if the key is in the map. O(1).
*/
function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
return _contains(map._inner, bytes32(key));
}
/**
* @dev Returns the number of elements in the map. O(1).
*/
function length(UintToAddressMap storage map) internal view returns (uint256) {
return _length(map._inner);
}
/**
* @dev Returns the element stored at position `index` in the set. O(1).
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) {
(bytes32 key, bytes32 value) = _at(map._inner, index);
return (uint256(key), address(uint160(uint256(value))));
}
/**
* @dev Tries to returns the value associated with `key`. O(1).
* Does not revert if `key` is not in the map.
*
* _Available since v3.4._
*/
function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) {
(bool success, bytes32 value) = _tryGet(map._inner, bytes32(key));
return (success, address(uint160(uint256(value))));
}
/**
* @dev Returns the value associated with `key`. O(1).
*
* Requirements:
*
* - `key` must be in the map.
*/
function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key)))));
}
/**
* @dev Same as {get}, with a custom error message when `key` is not in the map.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryGet}.
*/
function get(
UintToAddressMap storage map,
uint256 key,
string memory errorMessage
) internal view returns (address) {
return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage))));
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/structs/EnumerableSet.sol)
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = valueIndex; // Replace lastvalue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
return _values(set._inner);
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IAdminACLV0 {
/**
* @notice Token ID `_tokenId` minted to `_to`.
* @param previousSuperAdmin The previous superAdmin address.
* @param newSuperAdmin The new superAdmin address.
* @param genArt721CoreAddressesToUpdate Array of genArt721Core
* addresses to update to the new superAdmin, for indexing purposes only.
*/
event SuperAdminTransferred(
address indexed previousSuperAdmin,
address indexed newSuperAdmin,
address[] genArt721CoreAddressesToUpdate
);
/// Type of the Admin ACL contract, e.g. "AdminACLV0"
function AdminACLType() external view returns (string memory);
/// super admin address
function superAdmin() external view returns (address);
/**
* @notice Calls transferOwnership on other contract from this contract.
* This is useful for updating to a new AdminACL contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function transferOwnershipOn(
address _contract,
address _newAdminACL
) external;
/**
* @notice Calls renounceOwnership on other contract from this contract.
* @dev this function should be gated to only superAdmin-like addresses.
*/
function renounceOwnershipOn(address _contract) external;
/**
* @notice Checks if sender `_sender` is allowed to call function with selector
* `_selector` on contract `_contract`.
*/
function allowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IFilteredMinterV0 {
/**
* @notice Price per token in wei updated for project `_projectId` to
* `_pricePerTokenInWei`.
*/
event PricePerTokenInWeiUpdated(
uint256 indexed _projectId,
uint256 indexed _pricePerTokenInWei
);
/**
* @notice Currency updated for project `_projectId` to symbol
* `_currencySymbol` and address `_currencyAddress`.
*/
event ProjectCurrencyInfoUpdated(
uint256 indexed _projectId,
address indexed _currencyAddress,
string _currencySymbol
);
/// togglePurchaseToDisabled updated
event PurchaseToDisabledUpdated(
uint256 indexed _projectId,
bool _purchaseToDisabled
);
// getter function of public variable
function minterType() external view returns (string memory);
function genArt721CoreAddress() external returns (address);
function minterFilterAddress() external returns (address);
// Triggers a purchase of a token from the desired project, to the
// TX-sending address.
function purchase(
uint256 _projectId
) external payable returns (uint256 tokenId);
// Triggers a purchase of a token from the desired project, to the specified
// receiving address.
function purchaseTo(
address _to,
uint256 _projectId
) external payable returns (uint256 tokenId);
// Toggles the ability for `purchaseTo` to be called directly with a
// specified receiving address that differs from the TX-sending address.
function togglePurchaseToDisabled(uint256 _projectId) external;
// Called to make the minter contract aware of the max invocations for a
// given project.
function setProjectMaxInvocations(uint256 _projectId) external;
// Gets if token price is configured, token price in wei, currency symbol,
// and currency address, assuming this is project's minter.
// Supersedes any defined core price.
function getPriceInfo(
uint256 _projectId
)
external
view
returns (
bool isConfigured,
uint256 tokenPriceInWei,
string memory currencySymbol,
address currencyAddress
);
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
/// use the Royalty Registry's IManifold interface for token royalties
import "./IManifold.sol";
/**
* @title This interface is intended to house interface items that are common
* across all GenArt721CoreContractV3 flagship and derivative implementations.
* This interface extends the IManifold royalty interface in order to
* add support the Royalty Registry by default.
* @author Art Blocks Inc.
*/
interface IGenArt721CoreContractV3_Base is IManifold {
/**
* @notice Token ID `_tokenId` minted to `_to`.
*/
event Mint(address indexed _to, uint256 indexed _tokenId);
/**
* @notice currentMinter updated to `_currentMinter`.
* @dev Implemented starting with V3 core
*/
event MinterUpdated(address indexed _currentMinter);
/**
* @notice Platform updated on bytes32-encoded field `_field`.
*/
event PlatformUpdated(bytes32 indexed _field);
/**
* @notice Project ID `_projectId` updated on bytes32-encoded field
* `_update`.
*/
event ProjectUpdated(uint256 indexed _projectId, bytes32 indexed _update);
event ProposedArtistAddressesAndSplits(
uint256 indexed _projectId,
address _artistAddress,
address _additionalPayeePrimarySales,
uint256 _additionalPayeePrimarySalesPercentage,
address _additionalPayeeSecondarySales,
uint256 _additionalPayeeSecondarySalesPercentage
);
event AcceptedArtistAddressesAndSplits(uint256 indexed _projectId);
// version and type of the core contract
// coreVersion is a string of the form "0.x.y"
function coreVersion() external view returns (string memory);
// coreType is a string of the form "GenArt721CoreV3"
function coreType() external view returns (string memory);
// owner (pre-V3 was named admin) of contract
// this is expected to be an Admin ACL contract for V3
function owner() external view returns (address);
// Admin ACL contract for V3, will be at the address owner()
function adminACLContract() external returns (IAdminACLV0);
// backwards-compatible (pre-V3) admin - equal to owner()
function admin() external view returns (address);
/**
* Function determining if _sender is allowed to call function with
* selector _selector on contract `_contract`. Intended to be used with
* peripheral contracts such as minters, as well as internally by the
* core contract itself.
*/
function adminACLAllowed(
address _sender,
address _contract,
bytes4 _selector
) external returns (bool);
/// getter function of public variable
function startingProjectId() external view returns (uint256);
// getter function of public variable
function nextProjectId() external view returns (uint256);
// getter function of public mapping
function tokenIdToProjectId(
uint256 tokenId
) external view returns (uint256 projectId);
// @dev this is not available in V0
function isMintWhitelisted(address minter) external view returns (bool);
function projectIdToArtistAddress(
uint256 _projectId
) external view returns (address payable);
function projectIdToAdditionalPayeePrimarySales(
uint256 _projectId
) external view returns (address payable);
function projectIdToAdditionalPayeePrimarySalesPercentage(
uint256 _projectId
) external view returns (uint256);
function projectIdToSecondaryMarketRoyaltyPercentage(
uint256 _projectId
) external view returns (uint256);
function projectURIInfo(
uint256 _projectId
) external view returns (string memory projectBaseURI);
// @dev new function in V3
function projectStateData(
uint256 _projectId
)
external
view
returns (
uint256 invocations,
uint256 maxInvocations,
bool active,
bool paused,
uint256 completedTimestamp,
bool locked
);
function projectDetails(
uint256 _projectId
)
external
view
returns (
string memory projectName,
string memory artist,
string memory description,
string memory website,
string memory license
);
function projectScriptDetails(
uint256 _projectId
)
external
view
returns (
string memory scriptTypeAndVersion,
string memory aspectRatio,
uint256 scriptCount
);
function projectScriptByIndex(
uint256 _projectId,
uint256 _index
) external view returns (string memory);
function tokenIdToHash(uint256 _tokenId) external view returns (bytes32);
// function to set a token's hash (must be guarded)
function setTokenHash_8PT(uint256 _tokenId, bytes32 _hash) external;
// @dev gas-optimized signature in V3 for `mint`
function mint_Ecf(
address _to,
uint256 _projectId,
address _by
) external returns (uint256 tokenId);
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
import "./IAdminACLV0.sol";
import "./IGenArt721CoreContractV3_Base.sol";
/**
* @title This interface extends IGenArt721CoreContractV3_Base with functions
* that are part of the Art Blocks Flagship core contract.
* @author Art Blocks Inc.
*/
// This interface extends IGenArt721CoreContractV3_Base with functions that are
// in part of the Art Blocks Flagship core contract.
interface IGenArt721CoreContractV3 is IGenArt721CoreContractV3_Base {
// @dev new function in V3
function getPrimaryRevenueSplits(
uint256 _projectId,
uint256 _price
)
external
view
returns (
uint256 artblocksRevenue_,
address payable artblocksAddress_,
uint256 artistRevenue_,
address payable artistAddress_,
uint256 additionalPayeePrimaryRevenue_,
address payable additionalPayeePrimaryAddress_
);
// @dev Art Blocks primary sales payment address
function artblocksPrimarySalesAddress()
external
view
returns (address payable);
/**
* @notice Backwards-compatible (pre-V3) function returning Art Blocks
* primary sales payment address (now called artblocksPrimarySalesAddress).
*/
function artblocksAddress() external view returns (address payable);
// @dev Percentage of primary sales allocated to Art Blocks
function artblocksPrimarySalesPercentage() external view returns (uint256);
/**
* @notice Backwards-compatible (pre-V3) function returning Art Blocks
* primary sales percentage (now called artblocksPrimarySalesPercentage).
*/
function artblocksPercentage() external view returns (uint256);
// @dev Art Blocks secondary sales royalties payment address
function artblocksSecondarySalesAddress()
external
view
returns (address payable);
// @dev Basis points of secondary sales allocated to Art Blocks
function artblocksSecondarySalesBPS() external view returns (uint256);
/**
* @notice Backwards-compatible (pre-V3) function that gets artist +
* artist's additional payee royalty data for token ID `_tokenId`.
* WARNING: Does not include Art Blocks portion of royalties.
*/
function getRoyaltyData(
uint256 _tokenId
)
external
view
returns (
address artistAddress,
address additionalPayee,
uint256 additionalPayeePercentage,
uint256 royaltyFeeByID
);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @dev Royalty Registry interface, used to support the Royalty Registry.
/// @dev Source: https://github.com/manifoldxyz/royalty-registry-solidity/blob/main/contracts/specs/IManifold.sol
/// @author: manifold.xyz
/**
* @dev Royalty interface for creator core classes
*/
interface IManifold {
/**
* @dev Get royalites of a token. Returns list of receivers and basisPoints
*
* bytes4(keccak256('getRoyalties(uint256)')) == 0xbb3bafd6
*
* => 0xbb3bafd6 = 0xbb3bafd6
*/
function getRoyalties(
uint256 tokenId
) external view returns (address payable[] memory, uint256[] memory);
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
pragma solidity ^0.8.0;
interface IMinterFilterV0 {
/**
* @notice Emitted when contract is deployed to notify indexing services
* of the new contract deployment.
*/
event Deployed();
/**
* @notice Approved minter `_minterAddress`.
*/
event MinterApproved(address indexed _minterAddress, string _minterType);
/**
* @notice Revoked approval for minter `_minterAddress`
*/
event MinterRevoked(address indexed _minterAddress);
/**
* @notice Minter `_minterAddress` of type `_minterType`
* registered for project `_projectId`.
*/
event ProjectMinterRegistered(
uint256 indexed _projectId,
address indexed _minterAddress,
string _minterType
);
/**
* @notice Any active minter removed for project `_projectId`.
*/
event ProjectMinterRemoved(uint256 indexed _projectId);
function genArt721CoreAddress() external returns (address);
function setMinterForProject(uint256, address) external;
function removeMinterForProject(uint256) external;
function mint(
address _to,
uint256 _projectId,
address sender
) external returns (uint256);
function getMinterForProject(uint256) external view returns (address);
function projectHasMinter(uint256) external view returns (bool);
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
// Inspired by: https://ethereum.stackexchange.com/a/123950/103422
pragma solidity ^0.8.0;
/**
* @dev Operations on bytes32 data type, dealing with conversion to string.
*/
library Bytes32Strings {
/**
* @dev Intended to convert a `bytes32`-encoded string literal to `string`.
* Trims zero padding to arrive at original string literal.
*/
function toString(
bytes32 source
) internal pure returns (string memory result) {
uint8 length = 0;
while (source[length] != 0 && length < 32) {
length++;
}
assembly {
// free memory pointer
result := mload(0x40)
// update free memory pointer to new "memory end"
// (offset is 64-bytes: 32 for length, 32 for data)
mstore(0x40, add(result, 0x40))
// store length in first 32-byte memory slot
mstore(result, length)
// write actual data in second 32-byte memory slot
mstore(add(result, 0x20), source)
}
}
/**
* @dev Intended to check if a `bytes32`-encoded string contains a given
* character with UTF-8 character code `utf8CharCode exactly `targetQty`
* times. Does not support searching for multi-byte characters, only
* characters with UTF-8 character codes < 0x80.
*/
function containsExactCharacterQty(
bytes32 source,
uint8 utf8CharCode,
uint8 targetQty
) internal pure returns (bool) {
uint8 _occurrences = 0;
uint8 i;
for (i = 0; i < 32; ) {
uint8 _charCode = uint8(source[i]);
// if not a null byte, or a multi-byte UTF-8 character, check match
if (_charCode != 0 && _charCode < 0x80) {
if (_charCode == utf8CharCode) {
unchecked {
// no risk of overflow since max 32 iterations < max uin8=255
++_occurrences;
}
}
}
unchecked {
// no risk of overflow since max 32 iterations < max uin8=255
++i;
}
}
return _occurrences == targetQty;
}
}
// SPDX-License-Identifier: LGPL-3.0-only
// Created By: Art Blocks Inc.
import "../../interfaces/v0.8.x/IMinterFilterV0.sol";
import "../../interfaces/v0.8.x/IFilteredMinterV0.sol";
import "../../interfaces/v0.8.x/IAdminACLV0.sol";
import "../../interfaces/v0.8.x/IGenArt721CoreContractV3.sol";
import "../../libs/v0.8.x/Bytes32Strings.sol";
import "@openzeppelin-4.5/contracts/utils/structs/EnumerableMap.sol";
pragma solidity 0.8.19;
/**
* @title Minter filter contract that allows filtered minters to be set
* on a per-project basis.
* This is designed to be used with IGenArt721CoreContractV3 contracts.
* @author Art Blocks Inc.
* @notice Privileged Roles and Ownership:
* This contract is designed to be managed, with limited powers.
* Privileged roles and abilities are controlled by the core contract's Admin
* ACL contract and a project's artist. Both of these roles hold extensive
* power and can modify a project's current minter.
* Care must be taken to ensure that the admin ACL contract and artist
* addresses are secure behind a multi-sig or other access control mechanism.
* ----------------------------------------------------------------------------
* The following functions are restricted to the core contract's Admin ACL
* contract:
* - addApprovedMinter
* - removeApprovedMinter
* - removeMintersForProjects
* ----------------------------------------------------------------------------
* The following functions are restricted to the core contract's Admin ACL
* contract or a project's artist:
* - setMinterForProject
* - removeMinterForProject
* ----------------------------------------------------------------------------
* Additional admin and artist privileged roles may be described on minters
*/
contract MinterFilterV1 is IMinterFilterV0 {
// add Enumerable Map methods
using EnumerableMap for EnumerableMap.UintToAddressMap;
// add Bytes32Strings methods
using Bytes32Strings for bytes32;
/// version & type of this core contract
bytes32 constant MINTER_FILTER_VERSION = "v1.0.1";
function minterFilterVersion() external pure returns (string memory) {
return MINTER_FILTER_VERSION.toString();
}
bytes32 constant MINTER_FILTER_TYPE = "MinterFilterV1";
function minterFilterType() external pure returns (string memory) {
return MINTER_FILTER_TYPE.toString();
}
/// Core contract address this minter interacts with
address public immutable genArt721CoreAddress;
/// This contract integrates with the IV3 core contract
IGenArt721CoreContractV3 private immutable genArtCoreContract;
/// projectId => minter address
EnumerableMap.UintToAddressMap private minterForProject;
/// minter address => qty projects currently using minter
mapping(address => uint256) public numProjectsUsingMinter;
/// minter address => is an approved minter?
mapping(address => bool) public isApprovedMinter;
function _onlyNonZeroAddress(address _address) internal pure {
require(_address != address(0), "Must input non-zero address");
}
// function to restrict access to only AdminACL allowed calls
// @dev defers which ACL contract is used to the core contract
function _onlyCoreAdminACL(bytes4 _selector) internal {
require(_coreAdminACLAllowed(_selector), "Only Core AdminACL allowed");
}
// function to restrict access to only the artist of `_projectId`, or
// AdminACL allowed calls
// @dev defers which ACL contract is used to the core contract
function _onlyCoreAdminACLOrArtist(
uint256 _projectId,
bytes4 _selector
) internal {
require(
msg.sender ==
genArtCoreContract.projectIdToArtistAddress(_projectId) ||
_coreAdminACLAllowed(_selector),
"Only Core AdminACL or Artist"
);
}
function _onlyValidProjectId(uint256 _projectId) internal view {
require(
_projectId < genArtCoreContract.nextProjectId(),
"Only existing projects"
);
}
function _usingApprovedMinter(address _minterAddress) internal view {
require(
isApprovedMinter[_minterAddress],
"Only approved minters are allowed"
);
}
/**
* @notice Initializes contract to be a Minter for `_genArt721Address`.
* @param _genArt721Address Art Blocks core contract address
* this contract will be a minter for. Can never be updated.
*/
constructor(address _genArt721Address) {
_onlyNonZeroAddress(_genArt721Address);
genArt721CoreAddress = _genArt721Address;
genArtCoreContract = IGenArt721CoreContractV3(_genArt721Address);
emit Deployed();
}
/**
* @notice Internal function that determines if msg.sender is allowed to
* call a function on this contract. Defers to core contract's
* adminACLAllowed function.
*/
function _coreAdminACLAllowed(bytes4 _selector) internal returns (bool) {
return
genArtCoreContract.adminACLAllowed(
msg.sender,
address(this),
_selector
);
}
/**
* @notice Approves minter `_minterAddress`.
* @param _minterAddress Minter to be added as an approved minter.
*/
function addApprovedMinter(address _minterAddress) external {
_onlyCoreAdminACL(this.addApprovedMinter.selector);
_onlyNonZeroAddress(_minterAddress);
isApprovedMinter[_minterAddress] = true;
emit MinterApproved(
_minterAddress,
IFilteredMinterV0(_minterAddress).minterType()
);
}
/**
* @notice Removes previously approved minter `_minterAddress`.
* @param _minterAddress Minter to remove.
*/
function removeApprovedMinter(address _minterAddress) external {
_onlyCoreAdminACL(this.removeApprovedMinter.selector);
require(isApprovedMinter[_minterAddress], "Only approved minters");
require(
numProjectsUsingMinter[_minterAddress] == 0,
"Only unused minters"
);
isApprovedMinter[_minterAddress] = false;
emit MinterRevoked(_minterAddress);
}
/**
* @notice Sets minter for project `_projectId` to minter
* `_minterAddress`.
* @param _projectId Project ID to set minter for.
* @param _minterAddress Minter to be the project's minter.
*/
function setMinterForProject(
uint256 _projectId,
address _minterAddress
) external {
_onlyCoreAdminACLOrArtist(
_projectId,
this.setMinterForProject.selector
);
_usingApprovedMinter(_minterAddress);
_onlyValidProjectId(_projectId);
// decrement number of projects using a previous minter
(bool hasPreviousMinter, address previousMinter) = minterForProject
.tryGet(_projectId);
if (hasPreviousMinter) {
numProjectsUsingMinter[previousMinter]--;
}
// add new minter
numProjectsUsingMinter[_minterAddress]++;
minterForProject.set(_projectId, _minterAddress);
emit ProjectMinterRegistered(
_projectId,
_minterAddress,
IFilteredMinterV0(_minterAddress).minterType()
);
}
/**
* @notice Updates project `_projectId` to have no configured minter.
* @param _projectId Project ID to remove minter.
* @dev requires project to have an assigned minter
*/
function removeMinterForProject(uint256 _projectId) external {
_onlyCoreAdminACLOrArtist(
_projectId,
this.removeMinterForProject.selector
);
_removeMinterForProject(_projectId);
}
/**
* @notice Updates an array of project IDs to have no configured minter.
* @param _projectIds Array of project IDs to remove minters for.
* @dev requires all project IDs to have an assigned minter
* @dev caution with respect to single tx gas limits
*/
function removeMintersForProjects(uint256[] calldata _projectIds) external {
_onlyCoreAdminACL(this.removeMintersForProjects.selector);
uint256 numProjects = _projectIds.length;
for (uint256 i; i < numProjects; i++) {
_removeMinterForProject(_projectIds[i]);
}
}
/**
* @notice Updates project `_projectId` to have no configured minter
* (reverts tx if project does not have an assigned minter).
* @param _projectId Project ID to remove minter.
*/
function _removeMinterForProject(uint256 _projectId) private {
// remove minter for project and emit
// `minterForProject.get()` reverts tx if no minter set for project
numProjectsUsingMinter[minterForProject.get(_projectId)]--;
minterForProject.remove(_projectId);
emit ProjectMinterRemoved(_projectId);
}
/**
* @notice Mint a token from project `_projectId` to `_to`, originally
* purchased by `sender`.
* @param _to The new token's owner.
* @param _projectId Project ID to mint a new token on.
* @param sender Address purchasing a new token.
* @return _tokenId Token ID of minted token
* @dev reverts w/nonexistent key error when project has no assigned minter
*/
function mint(
address _to,
uint256 _projectId,
address sender
) external returns (uint256 _tokenId) {
// CHECKS
// minter is the project's minter
require(
msg.sender == minterForProject.get(_projectId),
"Only assigned minter"
);
// EFFECTS
uint256 tokenId = genArtCoreContract.mint_Ecf(_to, _projectId, sender);
return tokenId;
}
/**
* @notice Gets the assigned minter for project `_projectId`.
* @param _projectId Project ID to query.
* @return address Minter address assigned to project `_projectId`
* @dev requires project to have an assigned minter
*/
function getMinterForProject(
uint256 _projectId
) external view returns (address) {
_onlyValidProjectId(_projectId);
(bool _hasMinter, address _currentMinter) = minterForProject.tryGet(
_projectId
);
require(_hasMinter, "No minter assigned");
return _currentMinter;
}
/**
* @notice Queries if project `_projectId` has an assigned minter.
* @param _projectId Project ID to query.
* @return bool true if project has an assigned minter, else false
*/
function projectHasMinter(uint256 _projectId) external view returns (bool) {
_onlyValidProjectId(_projectId);
(bool _hasMinter, ) = minterForProject.tryGet(_projectId);
return _hasMinter;
}
/**
* @notice Gets quantity of projects that have assigned minters.
* @return uint256 quantity of projects that have assigned minters
*/
function getNumProjectsWithMinters() external view returns (uint256) {
return minterForProject.length();
}
/**
* @notice Get project ID and minter address at index `_index` of
* enumerable map.
* @param _index enumerable map index to query.
* @return projectId project ID at index `_index`
* @return minterAddress minter address for project at index `_index`
* @return minterType minter type of minter at minterAddress
* @dev index must be < quantity of projects that have assigned minters
*/
function getProjectAndMinterInfoAt(
uint256 _index
)
external
view
returns (
uint256 projectId,
address minterAddress,
string memory minterType
)
{
(projectId, minterAddress) = minterForProject.at(_index);
minterType = IFilteredMinterV0(minterAddress).minterType();
return (projectId, minterAddress, minterType);
}
}