Skip to content

Commit

Permalink
feat: move wave data to a struct
Browse files Browse the repository at this point in the history
  • Loading branch information
Andres Adjimann committed Sep 11, 2024
1 parent ababe9b commit 2259967
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 100 deletions.
2 changes: 0 additions & 2 deletions packages/avatar/contracts/nft-collection/INFTCollection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ interface INFTCollection {
* @param waveMaxTokens the allowed number of tokens to be minted in this wave (cumulative by all minting wallets)
* @param waveMaxTokensToBuy max tokens to buy, per wallet in a given wave
* @param waveSingleTokenPrice the price to mint a token in a given wave, in wei
* @param prevMinted the amount of tokens minted in previous wave
* @param waveIndex the current wave index
*/
event WaveSetup(
address indexed operator,
uint256 waveMaxTokens,
uint256 waveMaxTokensToBuy,
uint256 waveSingleTokenPrice,
uint256 prevMinted,
uint256 waveIndex
);

Expand Down
138 changes: 58 additions & 80 deletions packages/avatar/contracts/nft-collection/NFTCollection.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,51 +59,42 @@ INFTCollection
uint256 amount;
}

/**
* @notice Structure used save minting wave information
* @param waveMaxTokensOverall max tokens to buy per wave, cumulating all addresses
* @param waveMaxTokensPerWallet max tokens to buy, per wallet in a given wave
* @param waveSingleTokenPrice price of one token mint (in the token denoted by the allowedToExecuteMint contract)
* @param waveTotalMinted number of total minted tokens in the current running wave
* @param waveOwnerToClaimedCounts mapping of [owner -> minted count]
*/
struct WaveData {
uint256 waveMaxTokensOverall;
uint256 waveMaxTokensPerWallet;
uint256 waveSingleTokenPrice;
uint256 waveTotalMinted;
mapping(address => uint256) waveOwnerToClaimedCounts;
}

struct NFTCollectionStorage {
/**
* @notice maximum amount of tokens that can be minted
*/
uint256 maxSupply;
uint256 maxSupply; // public

/**
* @notice treasury address where the payment for minting are sent
*/
address mintTreasury;
address mintTreasury; // public

/**
* @notice standard base token URL for ERC721 metadata
*/
string baseTokenURI;

/**
* @notice max tokens to buy per wave, cumulating all addresses
*/
uint256 waveMaxTokensOverall;

/**
* @notice max tokens to buy, per wallet in a given wave
*/
uint256 waveMaxTokensPerWallet;

/**
* @notice price of one token mint (in the token denoted by the allowedToExecuteMint contract)
*/
uint256 waveSingleTokenPrice;

/**
* @notice number of total minted tokens in the current running wave
*/
uint256 waveTotalMinted;

/**
* @notice mapping of [owner -> wave index -> minted count]
* @notice saved information of minting waves
*/
mapping(address => mapping(uint256 => uint256)) waveOwnerToClaimedCounts;

/**
* @notice each wave has an index to help track minting/tokens per wallet
*/
uint256 indexWave;
WaveData[] waveData;

/**
* @notice ERC20 contract through which the minting will be done (approveAndCall)
Expand Down Expand Up @@ -277,16 +268,17 @@ INFTCollection
bytes calldata signature
) external whenNotPaused nonReentrant {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
uint256 _indexWave = $.indexWave;
uint256 _indexWave = $.waveData.length;
if (_indexWave == 0) {
revert ContractNotConfigured();
}
WaveData storage wd = $.waveData[_indexWave - 1];
if (_msgSender() != address($.allowedToExecuteMint)) {
revert ERC721InvalidSender(_msgSender());
}
_checkAndSetSignature(wallet, signatureId, signature);
_checkMintAllowed(wallet, amount);
uint256 _price = price(amount);
uint256 _price = wd.waveSingleTokenPrice * amount;
if (_price > 0) {
SafeERC20.safeTransferFrom($.allowedToExecuteMint, wallet, $.mintTreasury, _price);
}
Expand All @@ -296,8 +288,8 @@ INFTCollection
// @dev start with tokenId = 1
_safeMint(wallet, _totalSupply + i + 1);
}
$.waveOwnerToClaimedCounts[wallet][_indexWave - 1] += amount;
$.waveTotalMinted += amount;
wd.waveOwnerToClaimedCounts[wallet] += amount;
wd.waveTotalMinted += amount;
$.totalSupply += amount;
}

Expand All @@ -309,7 +301,7 @@ INFTCollection
*/
function batchMint(BatchMintingData[] calldata wallets) external whenNotPaused onlyOwner {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
uint256 _indexWave = $.indexWave;
uint256 _indexWave = $.waveData.length;
if (_indexWave == 0) {
revert ContractNotConfigured();
}
Expand All @@ -318,6 +310,7 @@ INFTCollection
revert InvalidBatchData();
}

WaveData storage wd = $.waveData[_indexWave - 1];
for (uint256 i; i < len; i++) {
uint256 _totalSupply = $.totalSupply;
address wallet = wallets[i].wallet;
Expand All @@ -328,8 +321,8 @@ INFTCollection
// @dev start with tokenId = 1
_mint(wallet, _totalSupply + j + 1);
}
$.waveOwnerToClaimedCounts[wallet][_indexWave - 1] += amount;
$.waveTotalMinted += amount;
wd.waveOwnerToClaimedCounts[wallet] += amount;
wd.waveTotalMinted += amount;
$.totalSupply += amount;
}
}
Expand Down Expand Up @@ -690,20 +683,10 @@ INFTCollection
*/
function isMintAllowed(address wallet, uint256 amount) external view returns (bool) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
uint256 waveClaimedCounts = $.waveOwnerToClaimedCounts[wallet][$.indexWave - 1];
uint256 waveClaimedCounts = $.waveData[$.waveData.length - 1].waveOwnerToClaimedCounts[wallet];
return _isMintAllowed(waveClaimedCounts, amount);
}

/**
* @notice get the price of minting the indicated number of tokens for the current wave
* @param count the number of tokens to estimate mint price for
* @return price of minting all the tokens
*/
function price(uint256 count) public view virtual returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveSingleTokenPrice * count;
}

/**
* @dev The denominator with which to interpret the fee set in {_setTokenRoyalty} and {_setDefaultRoyalty} as a
* fraction of the sale price. Defaults to 10000 so fees are expressed in basis points, but may be customized by an
Expand Down Expand Up @@ -747,52 +730,56 @@ INFTCollection

/**
* @notice return max tokens to buy per wave, cumulating all addresses
* @param waveIndex the wave for which the count is returned
*/
function waveMaxTokensOverall() external view returns (uint256) {
function waveMaxTokensOverall(uint256 waveIndex) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveMaxTokensOverall;
return $.waveData[waveIndex].waveMaxTokensOverall;
}

/**
* @notice return max tokens to buy, per wallet in a given wave
* @param waveIndex the wave for which the count is returned
*/
function waveMaxTokensPerWallet() external view returns (uint256) {
function waveMaxTokensPerWallet(uint256 waveIndex) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveMaxTokensPerWallet;
return $.waveData[waveIndex].waveMaxTokensPerWallet;
}

/**
* @notice return price of one token mint (in the token denoted by the allowedToExecuteMint contract)
* @param waveIndex the wave for which the count is returned
*/
function waveSingleTokenPrice() external view returns (uint256) {
function waveSingleTokenPrice(uint256 waveIndex) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveSingleTokenPrice;
return $.waveData[waveIndex].waveSingleTokenPrice;
}

/**
* @notice return number of total minted tokens in the current running wave
* @param waveIndex the wave for which the count is returned
*/
function waveTotalMinted() external view returns (uint256) {
function waveTotalMinted(uint256 waveIndex) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveTotalMinted;
return $.waveData[waveIndex].waveTotalMinted;
}

/**
* @notice return mapping of [owner -> wave index -> minted count]
* @param owner the owner for which the count is returned
* @param waveIndex the wave for which the count is returned
* @param owner the owner for which the count is returned
*/
function waveOwnerToClaimedCounts(address owner, uint256 waveIndex) external view returns (uint256) {
function waveOwnerToClaimedCounts(uint256 waveIndex, address owner) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.waveOwnerToClaimedCounts[owner][waveIndex];
return $.waveData[waveIndex].waveOwnerToClaimedCounts[owner];
}

/**
* @notice return each wave has an index to help track minting/tokens per wallet
*/
function indexWave() external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.indexWave;
return $.waveData.length;
}

/**
Expand All @@ -811,23 +798,13 @@ INFTCollection
return $.signAddress;
}


/**
* @notice return the personalization mask for a tokenId
* @param tokenId the tokenId to check
*/
function personalizationTraits(uint256 tokenId) external view returns (uint256) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.personalizationTraits[tokenId];
}

/**
* @notice return true if the signature id was used
* @param signatureId signing signature ID
*/
function isSignatureUsed(uint256 signatureId) external view returns (uint256) {
function isSignatureUsed(uint256 signatureId) external view returns (bool) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
return $.signatureIds[signatureId];
return $.signatureIds[signatureId] > 0;
}

/**
Expand Down Expand Up @@ -873,12 +850,12 @@ INFTCollection
) {
revert InvalidWaveData(_waveMaxTokensOverall, _waveMaxTokensPerWallet);
}
emit WaveSetup(_msgSender(), _waveMaxTokensOverall, _waveMaxTokensPerWallet, _waveSingleTokenPrice, $.waveTotalMinted, $.indexWave);
$.waveMaxTokensOverall = _waveMaxTokensOverall;
$.waveMaxTokensPerWallet = _waveMaxTokensPerWallet;
$.waveSingleTokenPrice = _waveSingleTokenPrice;
$.waveTotalMinted = 0;
$.indexWave++;
uint256 _indexWave = $.waveData.length;
emit WaveSetup(_msgSender(), _waveMaxTokensOverall, _waveMaxTokensPerWallet, _waveSingleTokenPrice, _indexWave);
$.waveData.push();
$.waveData[_indexWave].waveMaxTokensOverall = _waveMaxTokensOverall;
$.waveData[_indexWave].waveMaxTokensPerWallet = _waveMaxTokensPerWallet;
$.waveData[_indexWave].waveSingleTokenPrice = _waveSingleTokenPrice;
}

/**
Expand Down Expand Up @@ -1149,7 +1126,7 @@ INFTCollection
*/
function _checkMintAllowed(address _wallet, uint256 _amount) internal view {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
uint256 waveClaimedCounts = $.waveOwnerToClaimedCounts[_wallet][$.indexWave - 1];
uint256 waveClaimedCounts = $.waveData[$.waveData.length - 1].waveOwnerToClaimedCounts[_wallet];
if (!_isMintAllowed(waveClaimedCounts, _amount)) {
revert CannotMint(_wallet, _amount);
}
Expand All @@ -1163,9 +1140,10 @@ INFTCollection
*/
function _isMintAllowed(uint256 _waveClaimedCounts, uint256 _amount) internal view returns (bool) {
NFTCollectionStorage storage $ = _getNFTCollectionStorage();
uint256 _indexWave = $.waveData.length;
return _amount > 0
&& ($.waveTotalMinted + _amount <= $.waveMaxTokensOverall)
&& (_waveClaimedCounts + _amount <= $.waveMaxTokensPerWallet)
&& ($.waveData[_indexWave - 1].waveTotalMinted + _amount <= $.waveData[_indexWave - 1].waveMaxTokensOverall)
&& (_waveClaimedCounts + _amount <= $.waveData[_indexWave - 1].waveMaxTokensPerWallet)
&& $.totalSupply + _amount <= $.maxSupply;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ describe('NFTCollection batch mint', function () {
]);
const indexWave = await contract.indexWave();
expect(
await contract.waveOwnerToClaimedCounts(randomWallet, indexWave - 1n)
await contract.waveOwnerToClaimedCounts(indexWave - 1n, randomWallet)
).to.be.eq(7);
expect(
await contract.waveOwnerToClaimedCounts(collectionOwner, indexWave - 1n)
await contract.waveOwnerToClaimedCounts(indexWave - 1n, collectionOwner)
).to.be.eq(5);
expect(await contract.waveTotalMinted()).to.be.eq(12);
expect(await contract.waveTotalMinted(indexWave - 1n)).to.be.eq(12);
expect(await contract.totalSupply()).to.be.eq(12);
const transferEvents = await contract.queryFilter('Transfer');
let i = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ describe('NFTCollection mint', function () {
.approveAndCall(contract, price, encodedData);
expect(await sandContract.balanceOf(treasury)).to.be.eq(price);
expect(await sandContract.balanceOf(randomWallet)).to.be.eq(0);
expect(await contract.isSignatureUsed(222)).to.be.true;
const transferEvents = await contract.queryFilter('Transfer');
for (let i = 0; i < transferEvents.length; i++) {
const tokenId = transferEvents[i].args.tokenId;
expect(await contract.ownerOf(tokenId)).to.be.eq(randomWallet);
}
const indexWave = await contract.indexWave();
expect(
await contract.waveOwnerToClaimedCounts(randomWallet, indexWave - 1n)
await contract.waveOwnerToClaimedCounts(indexWave - 1n, randomWallet)
).to.be.eq(2);
expect(await contract.waveTotalMinted()).to.be.eq(2);
expect(await contract.waveTotalMinted(indexWave - 1n)).to.be.eq(2);
expect(await contract.totalSupply()).to.be.eq(2);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ describe('NFTCollection', function () {
describe('personalize', function () {
it('token owner should be able to be call personalize with a valid signature', async function () {
const {
collectionContractAsOwner,
collectionContractAsRandomWallet: contract,
randomWallet,
personalizeSignature,
Expand All @@ -121,9 +120,9 @@ describe('NFTCollection', function () {
await expect(tx)
.to.emit(contract, 'MetadataUpdate')
.withArgs(tokenIds[0]);
expect(
await collectionContractAsOwner.personalizationOf(tokenIds[0])
).to.be.eq(personalizationMask);
expect(await contract.personalizationOf(tokenIds[0])).to.be.eq(
personalizationMask
);
});

it('other owner should fail to call personalize', async function () {
Expand Down
Loading

1 comment on commit 2259967

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage for this commit

90.00%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
packages/avatar/contracts/avatar
   AvatarCollection.sol74.25%56.56%73.81%90.37%285, 329–337, 339–340, 345, 403–407, 425, 442, 459, 483–487, 491–492, 495, 526, 537, 545, 545–546, 565, 599, 621, 631, 712, 732, 745, 745, 745, 763, 763, 765, 774, 774, 776, 786, 786–787, 797, 797–798, 809, 809–810, 823, 832, 861–862, 992–993, 996, 999
   CollectionAccessControl.sol60.87%53.33%63.64%67.86%120, 130–131, 140, 150–151, 161–162, 162, 162, 164–165, 167, 173, 173–174, 185, 192, 192–194, 98–99
   ERC721BurnMemoryEnumerableUpgradeable.sol93.33%75%100%100%62, 75
packages/avatar/contracts/common
   IERC4906.sol100%100%100%100%
   IERC5313.sol100%100%100%100%
packages/avatar/contracts/common/BaseWithStorage/ERC2771
   ERC2771HandlerUpgradeable.sol44.44%25%60%44.44%22, 26, 29, 38, 38, 38–39, 41
packages/avatar/contracts/common/OperatorFilterer
   IOperatorFilterRegistry.sol100%100%100%100%
   UpdatableOperatorFiltererUpgradeable.sol11.11%10%25%9.52%18, 23–24, 24, 24–25, 27, 27, 27–28, 30, 38, 38, 38, 42, 42, 42–44, 46, 46, 46–47, 50, 55, 55, 55–56, 56, 56–57, 60, 68, 68, 68–69, 71
packages/avatar/contracts/nft-collection
   ERC2771HandlerUpgradeable.sol100%100%100%100%
   ERC721BurnMemoryUpgradeable.sol100%100%100%100%
   INFTCollection.sol100%100%100%100%
   NFTCollection.sol100%100%100%100%
   UpdatableOperatorFiltererUpgradeable.sol100%100%100%100%
packages/avatar/contracts/proxy
   CollectionFactory.sol86.59%89.06%75%87.50%149, 230, 293, 362–366, 368, 385, 402, 410, 410–411, 423, 423–424
   CollectionProxy.sol88.24%50%100%100%55, 70
packages/avatar/contracts/raffle
   DanceFight.sol75%50%100%100%20
   FistOfTheNorthStar.sol75%50%100%100%20
   GenericRaffle.sol67.92%52.22%62.07%84.95%178, 189–195, 225–229, 252–259, 266–267, 270, 288, 288–289, 289, 289–290, 308, 381, 390, 409, 409–410, 419, 419, 421, 430, 430, 432, 442, 442–443, 453, 453–454, 465, 465–466, 566, 575, 626–627, 630, 633
   HellsKitchen.sol75%50%100%100%20
   MadBalls.sol75%50%100%100%20
   ParisHilton.sol75%50%100%100%20
   Rabbids.sol75%50%100%100%20

Please sign in to comment.