The setCollectionCosts() function within the MinterContract is utilized to establish the collection costs and sales model for an upcoming collection sale. As outlined in the documentation, the expectation is that one token can be minted and auctioned during each time period. Therefore, it is crucial to invoke the setCollectionCosts() function and specify a non-zero time period before executing mintAndAuction().
However, a vulnerability exists in the mintAndAuction() contract as it fails to ensure that the time period is greater than zero. This flaw can result in a division by zero error when the time period is zero. Additionally, an arithmetic underflow error may occur if the allowlistStartTime is not set in the setCollectionPhases() function.
285:if (lastMintDate[_collectionID] == 0) {
286: // for public sale set the allowlist the same time as publicsale
287: @> timeOfLastMint = collectionPhases[_collectionID].allowlistStartTime - collectionPhases[_collectionID].timePeriod;
288: } else {
289: timeOfLastMint = lastMintDate[_collectionID];
290: }
291: // uint calculates if period has passed in order to allow minting
292: @> uint tDiff = (block.timestamp - timeOfLastMint) / collectionPhases[_collectionID].timePeriod;
import "forge-std/Test.sol";
import "../../smart-contracts/NextGenCore.sol";
import "../../smart-contracts/NextGenAdmins.sol";
import "../../smart-contracts/RandomizerNXT.sol";
import "../../smart-contracts/XRandoms.sol";
import "../../smart-contracts/MinterContract.sol";
import "../../smart-contracts/NFTdelegation.sol";
import "../../smart-contracts/AuctionDemo.sol";
import "../../smart-contracts/ERC721.sol";
contract MintAuction is Test {
NextGenCore public nextGenCore;
NextGenAdmins public nextGenAdmin;
NextGenRandomizerNXT public nextGenRandomizerNXT;
randomPool public xRandoms;
NextGenMinterContract public nextGenMinter;
DelegationManagementContract public delegationManager;
auctionDemo public auctionContract;
address public owner;
address public admin;
address public artist;
string _collectionName;
string _collectionArtist;
string _collectionDescription;
string _collectionWebsite;
string _collectionLicense;
string _collectionBaseURI;
string _collectionLibrary;
string[] _collectionScript;
function setUp() public virtual {
owner = vm.addr(0xA11CE);
admin = vm.addr(0xB44DE);
artist = vm.addr(3);
vm.deal(admin, 10 ether);
vm.prank(owner);
nextGenAdmin = new NextGenAdmins();
nextGenCore = new NextGenCore(
"ART Token",
"ART",
address(nextGenAdmin)
);
xRandoms = new randomPool();
nextGenRandomizerNXT = new NextGenRandomizerNXT(
address(xRandoms),
address(nextGenAdmin),
address(nextGenCore)
);
delegationManager = new DelegationManagementContract();
nextGenMinter = new NextGenMinterContract(
address(nextGenCore),
address(delegationManager),
address(nextGenAdmin)
);
auctionContract = new auctionDemo(address(nextGenMinter), address(nextGenCore), address(nextGenAdmin));
vm.prank(owner);
nextGenCore.addMinterContract(address(nextGenMinter));
vm.stopPrank();
_collectionName = "Test Collection 1";
_collectionArtist = "Artist 1";
_collectionDescription = "For testing";
_collectionWebsite = "www.test.com";
_collectionLicense = "CCO";
_collectionBaseURI = "https://ipfs.io/ipfs/hash/";
_collectionLibrary = "";
_collectionScript = ["desc"];
}
function createCollection(address user) public {
vm.prank(user);
nextGenCore.createCollection(
_collectionName,
_collectionArtist,
_collectionDescription,
_collectionWebsite,
_collectionLicense,
_collectionBaseURI,
_collectionLibrary,
_collectionScript
);
vm.stopPrank();
}
function registerFunctionAdmin(address user, bytes4 selector) public {
vm.prank(owner);
nextGenAdmin.registerFunctionAdmin(address(user), selector, true);
vm.stopPrank();
}
function testAuction() public {
// Registering function admin
registerFunctionAdmin(
address(admin),
nextGenCore.setCollectionData.selector
);
registerFunctionAdmin(
address(admin),
nextGenCore.createCollection.selector
);
registerFunctionAdmin(
address(admin),
nextGenCore.addRandomizer.selector
);
registerFunctionAdmin(
address(admin),
nextGenMinter.setCollectionCosts.selector
);
registerFunctionAdmin(
address(admin),
nextGenMinter.setCollectionPhases.selector
);
registerFunctionAdmin(
address(admin),
nextGenMinter.mintAndAuction.selector
);
uint256 collectionId = nextGenCore.newCollectionIndex();
// Step 1: Create collection
createCollection(address(admin));
// Step 2: Set collection data
vm.prank(admin);
nextGenCore.setCollectionData(
collectionId,
address(artist),
2,
10000000000,
0
);
// Step 3: Add randomizer
vm.prank(admin);
nextGenCore.addRandomizer(collectionId, address(nextGenRandomizerNXT));
// Step 4: Set collection costs
vm.prank(admin);
nextGenMinter.setCollectionCosts(
collectionId, // _collectionID
1 ether, // _collectionMintCost 1 eth
1 ether, // _collectionEndMintCost 0.1 eth
0, // _rate
0, // _timePeriod
1, // _salesOptions
address(delegationManager) // delAddress
);
// Step 5: Set collection phases
vm.prank(admin);
nextGenMinter.setCollectionPhases(
collectionId, // _collectionID
block.timestamp, // _allowlistStartTime
block.timestamp, // _allowlistEndTime
block.timestamp, // _publicStartTime
block.timestamp + 1 hours, // _publicEndTime
0x8e3c1713145650ce646f7eccd42c4541ecee8f07040fc1ac36fe071bbfebb870 // _merkleRoot
);
address _recipient = address(artist);
string memory _tokenData = '{"tdh": "100"}';
uint256 _saltfun_o = 2;
uint _auctionEndTime = block.timestamp + 2 hours;
// Step 6: Mint
vm.prank(admin);
nextGenMinter.mintAndAuction(
_recipient,
_tokenData,
_saltfun_o,
collectionId,
_auctionEndTime
);
}
}
Test Result
├─ [268855] NextGenMinterContract::mintAndAuction(0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69, {"tdh": "100"}, 2, 1, 7201)
│ ├─ [856] NextGenAdmins::retrieveFunctionAdmin(0xb742c2a92B070997Def5fB9e125039a4498834D9, 0x46372ba600000000000000000000000000000000000000000000000000000000) [staticcall]
│ │ └─ ← true
│ ├─ [506] NextGenCore::retrievewereDataAdded(1) [staticcall]
│ │ └─ ← true
│ ├─ [534] NextGenCore::viewCirSupply(1) [staticcall]
│ │ └─ ← 0
│ ├─ [555] NextGenCore::viewTokensIndexMin(1) [staticcall]
│ │ └─ ← 10000000000 [1e10]
│ ├─ [534] NextGenCore::viewTokensIndexMax(1) [staticcall]
│ │ └─ ← 19999999999 [1.999e10]
│ ├─ [534] NextGenCore::viewCirSupply(1) [staticcall]
│ │ └─ ← 0
│ ├─ [555] NextGenCore::viewTokensIndexMin(1) [staticcall]
│ │ └─ ← 10000000000 [1e10]
│ ├─ [256331] NextGenCore::airDropTokens(10000000000 [1e10], 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69, {"tdh": "100"}, 2, 1)
│ │ ├─ [43723] NextGenRandomizerNXT::calculateTokenHash(1, 10000000000 [1e10], 2)
│ │ │ ├─ [558] randomPool::randomNumber() [staticcall]
│ │ │ │ └─ ← 897
│ │ │ ├─ [8912] randomPool::randomWord() [staticcall]
│ │ │ │ └─ ← Tangerine
│ │ │ ├─ [22851] NextGenCore::setTokenHash(1, 10000000000 [1e10], 0x64555ac2feade9bad70b104b2d9a08caa47916a21c544381bade3a49b5496a58)
│ │ │ │ └─ ← ()
│ │ │ └─ ← ()
│ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: 0x6813Eb9362372EEF6200f3b1dbC3f819671cBA69, tokenId: 10000000000 [1e10])
│ │ └─ ← ()
│ └─ ← "Division or modulo by 0"
└─ ← "Division or modulo by 0"
Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 5.95ms
Ran 1 test suites: 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in test/foundry/MintAuction2.t.sol:MintAuction
[FAIL. Reason: Division or modulo by 0] testAuction() (gas: 1104932)
Encountered a total of 1 failing tests, 0 tests succeeded
Manual Review and Foundry
Implement necessary sanity checks to avoid error and unnecessary situations.
Other
The text was updated successfully, but these errors were encountered:
All reactions