Skip to content

Commit

Permalink
Merge pull request #491 from ArtBlocks/minor-max-invocation-patch
Browse files Browse the repository at this point in the history
Minor Max Invocation Patch
  • Loading branch information
ryley-o authored Feb 15, 2023
2 parents 0c0d666 + b9d6607 commit 3f74ff1
Show file tree
Hide file tree
Showing 19 changed files with 639 additions and 482 deletions.
36 changes: 32 additions & 4 deletions contracts/minter-suite/Minters/MinterDAExpV4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
/// minterType for this minter
string public constant minterType = "MinterDAExpV4";

/// minter version for this minter
string public constant minterVersion = "v4.1.0";

uint256 constant ONE_MILLION = 1_000_000;

struct ProjectConfig {
Expand Down Expand Up @@ -147,7 +150,7 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
*/
function setProjectMaxInvocations(
uint256 _projectId
) external onlyArtist(_projectId) {
) public onlyArtist(_projectId) {
uint256 maxInvocations;
uint256 invocations;
(invocations, maxInvocations, , , , ) = genArtCoreContract_Base
Expand Down Expand Up @@ -372,6 +375,16 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
_startPrice,
_basePrice
);

// sync local max invocations if not initially populated
// @dev if local max invocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated.
if (
_projectConfig.maxInvocations == 0 &&
_projectConfig.maxHasBeenInvoked == false
) {
setProjectMaxInvocations(_projectId);
}
}

/**
Expand Down Expand Up @@ -460,10 +473,25 @@ contract MinterDAExpV4 is ReentrancyGuard, MinterBase, IFilteredMinterDAExpV1 {
// EFFECTS
tokenId = minterFilter.mint(_to, _projectId, msg.sender);

// okay if this underflows because if statement will always eval false.
// this is only for gas optimization (core enforces maxInvocations).
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
uint256 localMaxInvocations = _projectConfig.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Maximum invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
_projectConfig.maxHasBeenInvoked = true;
}
}
Expand Down
37 changes: 32 additions & 5 deletions contracts/minter-suite/Minters/MinterDALinV4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
/// minterType for this minter
string public constant minterType = "MinterDALinV4";

/// minter version for this minter
string public constant minterVersion = "v4.1.0";

uint256 constant ONE_MILLION = 1_000_000;

struct ProjectConfig {
Expand Down Expand Up @@ -143,7 +146,7 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
*/
function setProjectMaxInvocations(
uint256 _projectId
) external onlyArtist(_projectId) {
) public onlyArtist(_projectId) {
uint256 maxInvocations;
uint256 invocations;
(invocations, maxInvocations, , , , ) = genArtCoreContract_Base
Expand Down Expand Up @@ -343,6 +346,16 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {
_startPrice,
_basePrice
);

// sync local max invocations if not initially populated
// @dev if local max invocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated.
if (
_projectConfig.maxInvocations == 0 &&
_projectConfig.maxHasBeenInvoked == false
) {
setProjectMaxInvocations(_projectId);
}
}

/**
Expand Down Expand Up @@ -430,11 +443,25 @@ contract MinterDALinV4 is ReentrancyGuard, MinterBase, IFilteredMinterDALinV1 {

// EFFECTS
tokenId = minterFilter.mint(_to, _projectId, msg.sender);

// okay if this underflows because if statement will always eval false.
// this is only for gas optimization (core enforces maxInvocations).
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
uint256 localMaxInvocations = _projectConfig.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Maximum invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
_projectConfig.maxHasBeenInvoked = true;
}
}
Expand Down
41 changes: 35 additions & 6 deletions contracts/minter-suite/Minters/MinterHolderV4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,9 @@ contract MinterHolderV4 is
/// minterType for this minter
string public constant minterType = "MinterHolderV4";

/// minter version for this minter
string public constant minterVersion = "v4.1.0";

uint256 constant ONE_MILLION = 1_000_000;

struct ProjectConfig {
Expand Down Expand Up @@ -352,7 +355,7 @@ contract MinterHolderV4 is
* @dev this enables gas reduction after maxInvocations have been reached -
* core contracts shall still enforce a maxInvocation check during mint.
*/
function setProjectMaxInvocations(uint256 _projectId) external {
function setProjectMaxInvocations(uint256 _projectId) public {
uint256 maxInvocations;
uint256 invocations;
(invocations, maxInvocations, , , , ) = genArtCoreContract_Base
Expand Down Expand Up @@ -475,9 +478,20 @@ contract MinterHolderV4 is
uint256 _projectId,
uint256 _pricePerTokenInWei
) external onlyArtist(_projectId) {
projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
projectConfig[_projectId].priceIsConfigured = true;
ProjectConfig storage _projectConfig = projectConfig[_projectId];
_projectConfig.pricePerTokenInWei = _pricePerTokenInWei;
_projectConfig.priceIsConfigured = true;
emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);

// sync local max invocations if not initially populated
// @dev if local max invocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated.
if (
_projectConfig.maxInvocations == 0 &&
_projectConfig.maxHasBeenInvoked == false
) {
setProjectMaxInvocations(_projectId);
}
}

/**
Expand Down Expand Up @@ -669,10 +683,25 @@ contract MinterHolderV4 is

// NOTE: delegate-vault handling **ends here**.

// okay if this underflows because if statement will always eval false.
// this is only for gas optimization (core enforces maxInvocations).
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
uint256 localMaxInvocations = _projectConfig.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Maximum invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
_projectConfig.maxHasBeenInvoked = true;
}
}
Expand Down
41 changes: 35 additions & 6 deletions contracts/minter-suite/Minters/MinterMerkleV5.sol
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ contract MinterMerkleV5 is
/// minterType for this minter
string public constant minterType = "MinterMerkleV5";

/// minter version for this minter
string public constant minterVersion = "v5.1.0";

/// project minter configuration keys used by this minter
bytes32 private constant CONFIG_MERKLE_ROOT = "merkleRoot";
bytes32 private constant CONFIG_USE_MAX_INVOCATIONS_PER_ADDRESS_OVERRIDE =
Expand Down Expand Up @@ -252,7 +255,7 @@ contract MinterMerkleV5 is
*/
function setProjectMaxInvocations(
uint256 _projectId
) external onlyArtist(_projectId) {
) public onlyArtist(_projectId) {
uint256 maxInvocations;
uint256 invocations;
(invocations, maxInvocations, , , , ) = genArtCoreContract_Base
Expand Down Expand Up @@ -375,9 +378,20 @@ contract MinterMerkleV5 is
uint256 _projectId,
uint256 _pricePerTokenInWei
) external onlyArtist(_projectId) {
projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
projectConfig[_projectId].priceIsConfigured = true;
ProjectConfig storage _projectConfig = projectConfig[_projectId];
_projectConfig.pricePerTokenInWei = _pricePerTokenInWei;
_projectConfig.priceIsConfigured = true;
emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);

// sync local max invocations if not initially populated
// @dev if local max invocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated.
if (
_projectConfig.maxInvocations == 0 &&
_projectConfig.maxHasBeenInvoked == false
) {
setProjectMaxInvocations(_projectId);
}
}

/**
Expand Down Expand Up @@ -551,10 +565,25 @@ contract MinterMerkleV5 is

// NOTE: delegate-vault handling **ends here**.

// okay if this underflows because if statement will always eval false.
// this is only for gas optimization (core enforces maxInvocations).
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
uint256 localMaxInvocations = _projectConfig.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Maximum invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
_projectConfig.maxHasBeenInvoked = true;
}
}
Expand Down
41 changes: 35 additions & 6 deletions contracts/minter-suite/Minters/MinterPolyptychV0.sol
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ contract MinterPolyptychV0 is
/// minterType for this minter
string public constant minterType = "MinterPolyptychV0";

/// minter version for this minter
string public constant minterVersion = "v0.1.0";

uint256 constant ONE_MILLION = 1_000_000;

struct ProjectConfig {
Expand Down Expand Up @@ -304,7 +307,7 @@ contract MinterPolyptychV0 is
* @dev this enables gas reduction after maxInvocations have been reached -
* core contracts shall still enforce a maxInvocation check during mint.
*/
function setProjectMaxInvocations(uint256 _projectId) external {
function setProjectMaxInvocations(uint256 _projectId) public {
uint256 maxInvocations;
uint256 invocations;
(invocations, maxInvocations, , , , ) = genArtCoreContract
Expand Down Expand Up @@ -374,9 +377,20 @@ contract MinterPolyptychV0 is
uint256 _projectId,
uint256 _pricePerTokenInWei
) external onlyArtist(_projectId) {
projectConfig[_projectId].pricePerTokenInWei = _pricePerTokenInWei;
projectConfig[_projectId].priceIsConfigured = true;
ProjectConfig storage _projectConfig = projectConfig[_projectId];
_projectConfig.pricePerTokenInWei = _pricePerTokenInWei;
_projectConfig.priceIsConfigured = true;
emit PricePerTokenInWeiUpdated(_projectId, _pricePerTokenInWei);

// sync local max invocations if not initially populated
// @dev if local max invocations and maxHasBeenInvoked are both
// initial values, we know they have not been populated.
if (
_projectConfig.maxInvocations == 0 &&
_projectConfig.maxHasBeenInvoked == false
) {
setProjectMaxInvocations(_projectId);
}
}

/**
Expand Down Expand Up @@ -771,10 +785,25 @@ contract MinterPolyptychV0 is
"Unexpected token hash seed"
);

// okay if this underflows because if statement will always eval false.
// this is only for gas optimization (core enforces maxInvocations).
// invocation is token number plus one, and will never overflow due to
// limit of 1e6 invocations per project. block scope for gas efficiency
// (i.e. avoid an unnecessary var initialization to 0).
unchecked {
if (tokenId % ONE_MILLION == _projectConfig.maxInvocations - 1) {
uint256 tokenInvocation = (tokenId % ONE_MILLION) + 1;
uint256 localMaxInvocations = _projectConfig.maxInvocations;
// handle the case where the token invocation == minter local max
// invocations occurred on a different minter, and we have a stale
// local maxHasBeenInvoked value returning a false negative.
// @dev this is a CHECK after EFFECTS, so security was considered
// in detail here.
require(
tokenInvocation <= localMaxInvocations,
"Maximum invocations reached"
);
// in typical case, update the local maxHasBeenInvoked value
// to true if the token invocation == minter local max invocations
// (enables gas efficient reverts after sellout)
if (tokenInvocation == localMaxInvocations) {
_projectConfig.maxHasBeenInvoked = true;
}
}
Expand Down
Loading

0 comments on commit 3f74ff1

Please sign in to comment.