// SPDX-License-Identifier: LGPL-3.0-only// Created By: Art Blocks Inc.pragmasolidity ^0.8.0;interfaceIFilteredMinterV0{
/**
* @notice Price per token in wei updated for project `_projectId` to
* `_pricePerTokenInWei`.
*/eventPricePerTokenInWeiUpdated(uint256indexed _projectId,
uint256indexed _pricePerTokenInWei
);
/**
* @notice Currency updated for project `_projectId` to symbol
* `_currencySymbol` and address `_currencyAddress`.
*/eventProjectCurrencyInfoUpdated(uint256indexed _projectId,
addressindexed _currencyAddress,
string _currencySymbol
);
/// togglePurchaseToDisabled updatedeventPurchaseToDisabledUpdated(uint256indexed _projectId,
bool _purchaseToDisabled
);
// getter function of public variablefunctionminterType() externalviewreturns (stringmemory);
functiongenArt721CoreAddress() externalreturns (address);
functionminterFilterAddress() externalreturns (address);
// Triggers a purchase of a token from the desired project, to the// TX-sending address.functionpurchase(uint256 _projectId)
externalpayablereturns (uint256 tokenId);
// Triggers a purchase of a token from the desired project, to the specified// receiving address.functionpurchaseTo(address _to, uint256 _projectId)
externalpayablereturns (uint256 tokenId);
// Toggles the ability for `purchaseTo` to be called directly with a// specified receiving address that differs from the TX-sending address.functiontogglePurchaseToDisabled(uint256 _projectId) external;
// Called to make the minter contract aware of the max invocations for a// given project.functionsetProjectMaxInvocations(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.functiongetPriceInfo(uint256 _projectId)
externalviewreturns (bool isConfigured,
uint256 tokenPriceInWei,
stringmemory currencySymbol,
address currencyAddress
);
}
Contract Source Code
File 2 of 5: IGenArt721CoreContractV1.sol
// SPDX-License-Identifier: LGPL-3.0-only// Created By: Art Blocks Inc.pragmasolidity ^0.8.0;interfaceIGenArt721CoreContractV1{
eventMint(addressindexed _to,
uint256indexed _tokenId,
uint256indexed _projectId
);
// getter function of public variablefunctionadmin() externalviewreturns (address);
// getter function of public variablefunctionnextProjectId() externalviewreturns (uint256);
// getter function of public mappingfunctiontokenIdToProjectId(uint256 tokenId)
externalviewreturns (uint256 projectId);
functionisWhitelisted(address sender) externalviewreturns (bool);
// @dev this is not available in V0functionisMintWhitelisted(address minter) externalviewreturns (bool);
functionprojectIdToArtistAddress(uint256 _projectId)
externalviewreturns (addresspayable);
functionprojectIdToAdditionalPayee(uint256 _projectId)
externalviewreturns (addresspayable);
functionprojectIdToAdditionalPayeePercentage(uint256 _projectId)
externalviewreturns (uint256);
functionprojectTokenInfo(uint256 _projectId)
externalviewreturns (address,
uint256,
uint256,
uint256,
bool,
address,
uint256,
stringmemory,
address);
functionartblocksAddress() externalviewreturns (addresspayable);
functionartblocksPercentage() externalviewreturns (uint256);
functionmint(address _to,
uint256 _projectId,
address _by
) externalreturns (uint256 tokenId);
functiongetRoyaltyData(uint256 _tokenId)
externalviewreturns (address artistAddress,
address additionalPayee,
uint256 additionalPayeePercentage,
uint256 royaltyFeeByID
);
}
Contract Source Code
File 3 of 5: IMinterFilterV0.sol
// SPDX-License-Identifier: LGPL-3.0-only// Created By: Art Blocks Inc.pragmasolidity ^0.8.0;interfaceIMinterFilterV0{
/**
* @notice Approved minter `_minterAddress`.
*/eventMinterApproved(addressindexed _minterAddress, string _minterType);
/**
* @notice Revoked approval for minter `_minterAddress`
*/eventMinterRevoked(addressindexed _minterAddress);
/**
* @notice Minter `_minterAddress` of type `_minterType`
* registered for project `_projectId`.
*/eventProjectMinterRegistered(uint256indexed _projectId,
addressindexed _minterAddress,
string _minterType
);
/**
* @notice Any active minter removed for project `_projectId`.
*/eventProjectMinterRemoved(uint256indexed _projectId);
functiongenArt721CoreAddress() externalreturns (address);
functionsetMinterForProject(uint256, address) external;
functionremoveMinterForProject(uint256) external;
functionmint(address _to,
uint256 _projectId,
address sender
) externalreturns (uint256);
functiongetMinterForProject(uint256) externalviewreturns (address);
functionprojectHasMinter(uint256) externalviewreturns (bool);
}
Contract Source Code
File 4 of 5: MinterSetPriceV0.sol
// SPDX-License-Identifier: LGPL-3.0-only// Created By: Art Blocks Inc.import"../interfaces/0.8.x/IGenArt721CoreContractV1.sol";
import"../interfaces/0.8.x/IMinterFilterV0.sol";
import"../interfaces/0.8.x/IFilteredMinterV0.sol";
import"@openzeppelin/contracts/security/ReentrancyGuard.sol";
pragmasolidity 0.8.9;/**
* @title Filtered Minter contract that allows tokens to be minted with ETH.
* @author Art Blocks Inc.
*/contractMinterSetPriceV0isReentrancyGuard, IFilteredMinterV0{
/// Core contract address this minter interacts withaddresspublicimmutable genArt721CoreAddress;
/// This contract handles cores with interface IV1
IGenArt721CoreContractV1 privateimmutable genArtCoreContract;
/// Minter filter address this minter interacts withaddresspublicimmutable minterFilterAddress;
/// Minter filter this minter may interact with.
IMinterFilterV0 privateimmutable minterFilter;
/// minterType for this minterstringpublicconstant minterType ="MinterSetPriceV0";
uint256constant ONE_MILLION =1_000_000;
/// projectId => are contracts allowed to mint?mapping(uint256=>bool) public contractMintable;
/// projectId => are tokens allowed to be minted to other addresses?mapping(uint256=>bool) public purchaseToDisabled;
/// purchaser address => projectId => number of mints purchasedmapping(address=>mapping(uint256=>uint256)) public projectMintCounter;
/// projectId => maximum number of mints a given address may invokemapping(uint256=>uint256) public projectMintLimit;
/// projectId => has project reached its maximum number of invocations?mapping(uint256=>bool) public projectMaxHasBeenInvoked;
/// projectId => project's maximum number of invocationsmapping(uint256=>uint256) public projectMaxInvocations;
/// projectId => price per token in wei - supersedes any defined core pricemapping(uint256=>uint256) private projectIdToPricePerTokenInWei;
/// projectId => price per token has been configured on this mintermapping(uint256=>bool) private projectIdToPriceIsConfigured;
modifieronlyCoreWhitelisted() {
require(
genArtCoreContract.isWhitelisted(msg.sender),
"Only Core whitelisted"
);
_;
}
modifieronlyArtist(uint256 _projectId) {
require(
msg.sender==
genArtCoreContract.projectIdToArtistAddress(_projectId),
"Only Artist"
);
_;
}
/**
* @notice Initializes contract to be a Filtered Minter for
* `_minterFilter`, integrated with Art Blocks core contract
* at address `_genArt721Address`.
* @param _genArt721Address Art Blocks core contract address for
* which this contract will be a minter.
* @param _minterFilter Minter filter for whichccthis will a
* filtered minter.
*/constructor(address _genArt721Address, address _minterFilter)
ReentrancyGuard()
{
genArt721CoreAddress = _genArt721Address;
genArtCoreContract = IGenArt721CoreContractV1(_genArt721Address);
minterFilterAddress = _minterFilter;
minterFilter = IMinterFilterV0(_minterFilter);
require(
minterFilter.genArt721CoreAddress() == _genArt721Address,
"Illegal contract pairing"
);
}
/**
* @notice Sets the mint limit of a single purchaser for project
* `_projectId` to `_limit`.
* @param _projectId Project ID to set the mint limit for.
* @param _limit Number of times a given address may mint the
* project's tokens.
*/functionsetProjectMintLimit(uint256 _projectId, uint8 _limit)
externalonlyCoreWhitelisted{
projectMintLimit[_projectId] = _limit;
}
/**
* @notice Sets the maximum invocations of project `_projectId` based
* on the value currently defined in the core contract.
* @param _projectId Project ID to set the maximum invocations for.
* @dev also checks and may refresh projectMaxHasBeenInvoked for project
* @dev this enables gas reduction after maxInvocations have been reached -
* core contracts shall still enforce a maxInvocation check during mint.
*/functionsetProjectMaxInvocations(uint256 _projectId)
externalonlyCoreWhitelisted{
uint256 invocations;
uint256 maxInvocations;
(, , invocations, maxInvocations, , , , , ) = genArtCoreContract
.projectTokenInfo(_projectId);
// update storage with results
projectMaxInvocations[_projectId] = maxInvocations;
if (invocations < maxInvocations) {
projectMaxHasBeenInvoked[_projectId] =false;
}
}
/**
* @notice Toggles if contracts are allowed to mint tokens for
* project `_projectId`.
* @param _projectId Project ID to be toggled.
*/functiontoggleContractMintable(uint256 _projectId)
externalonlyCoreWhitelisted{
contractMintable[_projectId] =!contractMintable[_projectId];
}
/**
* @notice Toggles if purchases to other address are enabled for
* project `_projectId`.
* @param _projectId Project ID to be toggled.
*/functiontogglePurchaseToDisabled(uint256 _projectId)
externalonlyCoreWhitelisted{
purchaseToDisabled[_projectId] =!purchaseToDisabled[_projectId];
emit PurchaseToDisabledUpdated(
_projectId,
purchaseToDisabled[_projectId]
);
}
/**
* @notice Updates this minter's price per token of project `_projectId`
* to be '_pricePerTokenInWei`, in Wei.
* This price supersedes any legacy core contract price per token value.
*/functionupdatePricePerTokenInWei(uint256 _projectId,
uint256 _pricePerTokenInWei
) externalonlyArtist(_projectId) {
projectIdToPricePerTokenInWei[_projectId] = _pricePerTokenInWei;
projectIdToPriceIsConfigured[_projectId] =true;
emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);
}
/**
* @notice Purchases a token from project `_projectId`.
* @param _projectId Project ID to mint a token on.
* @return tokenId Token ID of minted token
*/functionpurchase(uint256 _projectId)
externalpayablereturns (uint256 tokenId)
{
tokenId = purchaseTo(msg.sender, _projectId);
return tokenId;
}
/**
* @notice Purchases a token from project `_projectId` and sets
* the token's owner to `_to`.
* @param _to Address to be the new token's owner.
* @param _projectId Project ID to mint a token on.
* @return tokenId Token ID of minted token
*/functionpurchaseTo(address _to, uint256 _projectId)
publicpayablenonReentrantreturns (uint256 tokenId)
{
// CHECKSrequire(
!projectMaxHasBeenInvoked[_projectId],
"Maximum number of invocations reached"
);
// require artist to have configured price of token on this minterrequire(
projectIdToPriceIsConfigured[_projectId],
"Price not configured"
);
// if contract filter is off, allow calls from another contractif (!contractMintable[_projectId]) {
require(msg.sender==tx.origin, "No Contract Buys");
}
// if purchaseTo is disabled, enforce purchase destination to be the TX// sending address.if (purchaseToDisabled[_projectId]) {
require(msg.sender== _to, "No `purchaseTo` Allowed");
}
require(
msg.value>= projectIdToPricePerTokenInWei[_projectId],
"Must send minimum value to mint!"
);
// limit mints per address by projectif (projectMintLimit[_projectId] >0) {
require(
projectMintCounter[msg.sender][_projectId] <
projectMintLimit[_projectId],
"Reached minting limit"
);
// EFFECTS
projectMintCounter[msg.sender][_projectId]++;
}
tokenId = minterFilter.mint(_to, _projectId, msg.sender);
// what if projectMaxInvocations[_projectId] is 0 (default value)?// that is intended, so that by default the minter allows infinite transactions,// allowing the artblocks contract to stop minting// uint256 tokenInvocation = tokenId % ONE_MILLION;if (
projectMaxInvocations[_projectId] >0&&
tokenId % ONE_MILLION == projectMaxInvocations[_projectId] -1
) {
projectMaxHasBeenInvoked[_projectId] =true;
}
// INTERACTIONS
_splitFundsETH(_projectId);
return tokenId;
}
/**
* @dev splits ETH funds between sender (if refund), foundation,
* artist, and artist's additional payee for a token purchased on
* project `_projectId`.
* @dev utilizes transfer() to send ETH, so access lists may need to be
* populated when purchasing tokens.
*/function_splitFundsETH(uint256 _projectId) internal{
if (msg.value>0) {
uint256 pricePerTokenInWei = projectIdToPricePerTokenInWei[
_projectId
];
uint256 refund =msg.value- pricePerTokenInWei;
if (refund >0) {
(bool success_, ) =msg.sender.call{value: refund}("");
require(success_, "Refund failed");
}
uint256 foundationAmount = (pricePerTokenInWei *
genArtCoreContract.artblocksPercentage()) /100;
if (foundationAmount >0) {
(bool success_, ) = genArtCoreContract.artblocksAddress().call{
value: foundationAmount
}("");
require(success_, "Foundation payment failed");
}
uint256 projectFunds = pricePerTokenInWei - foundationAmount;
uint256 additionalPayeeAmount;
if (
genArtCoreContract.projectIdToAdditionalPayeePercentage(
_projectId
) >0
) {
additionalPayeeAmount =
(projectFunds *
genArtCoreContract.projectIdToAdditionalPayeePercentage(
_projectId
)) /100;
if (additionalPayeeAmount >0) {
(bool success_, ) = genArtCoreContract
.projectIdToAdditionalPayee(_projectId)
.call{value: additionalPayeeAmount}("");
require(success_, "Additional payment failed");
}
}
uint256 creatorFunds = projectFunds - additionalPayeeAmount;
if (creatorFunds >0) {
(bool success_, ) = genArtCoreContract
.projectIdToArtistAddress(_projectId)
.call{value: creatorFunds}("");
require(success_, "Artist payment failed");
}
}
}
/**
* @notice Gets if price of token is configured, price of minting a
* token on project `_projectId`, and currency symbol and address to be
* used as payment. Supersedes any core contract price information.
* @param _projectId Project ID to get price information for.
* @return isConfigured true only if token price has been configured on
* this minter
* @return tokenPriceInWei current price of token on this minter - invalid
* if price has not yet been configured
* @return currencySymbol currency symbol for purchases of project on this
* minter. This minter always returns "ETH"
* @return currencyAddress currency address for purchases of project on
* this minter. This minter always returns null address, reserved for ether
*/functiongetPriceInfo(uint256 _projectId)
externalviewreturns (bool isConfigured,
uint256 tokenPriceInWei,
stringmemory currencySymbol,
address currencyAddress
)
{
isConfigured = projectIdToPriceIsConfigured[_projectId];
tokenPriceInWei = projectIdToPricePerTokenInWei[_projectId];
currencySymbol ="ETH";
currencyAddress =address(0);
}
}
Contract Source Code
File 5 of 5: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (security/ReentrancyGuard.sol)pragmasolidity ^0.8.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
// On the first call to nonReentrant, _notEntered will be truerequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}