Platform: Code4rena
Start Date: 30/10/2023
Pot Size: $49,250 USDC
Total HM: 14
Participants: 243
Period: 14 days
Judge: 0xsomeone
Id: 302
League: ETH
Rank: 97/243
Findings: 1
Award: $25.24
π Selected for report: 0
π Solo Findings: 0
π Selected for report: bird-flu
Also found by: 00decree, 0xAadi, AS, Audinarey, DeFiHackLabs, Eigenvectors, Fitro, Hama, Kaysoft, Krace, REKCAH, SovaSlava, The_Kakers, Viktor_Cortess, cartlex_, degensec, devival, evmboi32, funkornaut, jacopod, openwide, peanuts, rotcivegaf, smiling_heretic, xAriextz, xiao
25.2356 USDC - $25.24
In claimAuction
function of AuctionDemo.sol
, token owner should receive funds, but it will be transferred to AuctionDemo
contract's owner.
(bool success, ) = payable(owner()).call{value: highestBid}(""); emit ClaimAuction(owner(), _tokenid, success, highestBid);
I tried to write tests for mintAndAuction and I'm attaching them below.
AuctionDemo.t.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import {console} from "forge-std/console.sol"; import {DeployHelper} from "./DeployHelper.t.sol"; import {NextGenRandomizerNXT} from "../smart-contracts/RandomizerNXT.sol"; import {auctionDemo} from "../smart-contracts/AuctionDemo.sol"; import {NextGenMinterContract} from "../smart-contracts/MinterContract.sol"; import {IERC721Receiver} from "../smart-contracts/IERC721Receiver.sol"; contract AuctionDemoTest is DeployHelper { function setupEnv() public virtual override { super.setupEnv(); // burn collection 0 vm.startPrank(funcAdminColManager); coreContract.createCollection("BurnCol", "", "ColDesc", "", "", "", "", new string[](0)); // new collection 1 coreContract.createCollection("NewCol", "", "ColDesc", "", "", "", "", new string[](0)); vm.stopPrank(); } function test_auction() public { uint256 collectionID = 2; uint256 collectionTotalSupply = 10; vm.warp(1 days); uint256 auctionEndTime = block.timestamp + 1 days; uint allowlistStartTime = block.timestamp; uint256 timePeriod = 2 minutes; uint256 tokenId; // mint and auction vm.startPrank(funcAdminColManager); vm.deal(funcAdminColManager, 1 ether); coreContract.addRandomizer(collectionID, address(new NextGenRandomizerNXT(address(randomPoolContract), address(adminContract), address(coreContract)))); coreContract.setCollectionData(collectionID, artist, 100, collectionTotalSupply, 0); minterContract.setCollectionCosts(collectionID, 0, 0, 0, timePeriod, 0, address(0x0)); minterContract.setCollectionPhases(collectionID, allowlistStartTime, 0, 0, 0, 0); minterContract.mintAndAuction(customer4, "", 0, collectionID, auctionEndTime); tokenId = coreContract.viewTokensIndexMin(collectionID) + coreContract.viewCirSupply(collectionID) - 1; vm.stopPrank(); // do auction vm.deal(customer1, 1 ether); vm.deal(customer2, 1 ether); vm.deal(customer3, 1 ether); vm.prank(customer1); auctionContract.participateToAuction{value: 0.1 ether}(tokenId); vm.prank(customer2); auctionContract.participateToAuction{value: 0.2 ether}(tokenId); vm.prank(customer3); auctionContract.participateToAuction{value: 0.4 ether}(tokenId); assertEq(customer4.balance, 0, "token owner balance isn't zero"); assertEq(coreContract.balanceOf(customer4), 1); assertEq(coreContract.balanceOf(customer1), 0, "customer1 has some nfts"); assertEq(coreContract.balanceOf(customer2), 0, "customer2 has some nfts"); assertEq(coreContract.balanceOf(customer3), 0, "customer3 has some nfts"); vm.prank(customer4); coreContract.approve(address(auctionContract), tokenId); vm.warp(minterContract.getAuctionEndTime(tokenId)); vm.prank(customer3); auctionContract.claimAuction(tokenId); assertEq(customer1.balance, 1 ether, "1"); assertEq(coreContract.balanceOf(customer1), 0); assertEq(customer2.balance, 1 ether, "2"); assertEq(coreContract.balanceOf(customer2), 0); assertEq(customer3.balance, 0.6 ether, "3"); assertEq(coreContract.balanceOf(customer3), 1); assertEq(customer4.balance, 0.4 ether, "token owner didn't get eth"); assertEq(coreContract.balanceOf(customer4), 0); } }
DeployHelper.t.sol
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "forge-std/Test.sol"; import {NextGenAdmins} from "../smart-contracts/NextGenAdmins.sol"; import {NextGenMinterContract} from "../smart-contracts/MinterContract.sol"; import {NextGenCore} from "../smart-contracts/NextGenCore.sol"; import {auctionDemo} from "../smart-contracts/AuctionDemo.sol"; import {randomPool} from "../smart-contracts/XRandoms.sol"; abstract contract DeployHelper is Test { NextGenCore coreContract; NextGenAdmins adminContract; NextGenMinterContract minterContract; randomPool randomPoolContract; auctionDemo auctionContract; address public admin = vm.addr(0x01); address public funcAdminColManager = vm.addr(0x02); address public artist = vm.addr(0x04); address public customer1 = vm.addr(0x05); address public customer2 = vm.addr(0x06); address public customer3 = vm.addr(0x07); address public customer4 = vm.addr(0x08); function setUp() public { setupEnv(); } function setupEnv() public virtual { vm.startPrank(admin); adminContract = new NextGenAdmins(); coreContract = new NextGenCore("TNextGen", "TNG", address(adminContract)); minterContract = new NextGenMinterContract(address(coreContract), address(0x0), address(adminContract)); randomPoolContract = new randomPool(); coreContract.addMinterContract(address(minterContract)); auctionContract = new auctionDemo(address(minterContract), address(coreContract), address(adminContract)); // register collection func admin adminContract.registerFunctionAdmin(funcAdminColManager, NextGenCore.createCollection.selector, true); adminContract.registerFunctionAdmin(funcAdminColManager, NextGenCore.setCollectionData.selector, true); adminContract.registerFunctionAdmin(funcAdminColManager, NextGenMinterContract.setCollectionCosts.selector, true); adminContract.registerFunctionAdmin(funcAdminColManager, NextGenMinterContract.setCollectionPhases.selector, true); adminContract.registerFunctionAdmin(funcAdminColManager, NextGenCore.addRandomizer.selector, true); adminContract.registerFunctionAdmin(funcAdminColManager, NextGenMinterContract.mintAndAuction.selector, true); vm.stopPrank(); } }
VS Code, Manual Review
(bool success, ) = payable(ownerOfToken).call{value: highestBid}(""); emit ClaimAuction(ownerOfToken, _tokenid, success, highestBid);
Other
#0 - c4-pre-sort
2023-11-14T08:42:52Z
141345 marked the issue as primary issue
#1 - c4-sponsor
2023-11-22T12:38:37Z
a2rocket marked the issue as disagree with severity
#2 - a2rocket
2023-11-22T12:41:08Z
mintAndAuction function can only be called by trusted parties. The _recipient of that function will be a trusted wallet that will also call setApprovalForAll() from the Core contract for the Auction Contract. In our case the _recipient will be the deployer of the Auction contract so at the end of the day the token owner and auction owner are the same person.
#3 - c4-sponsor
2023-11-24T14:28:08Z
a2rocket (sponsor) disputed
#4 - c4-sponsor
2023-11-24T14:28:25Z
a2rocket (sponsor) confirmed
#5 - c4-pre-sort
2023-11-27T13:47:53Z
141345 marked the issue as sufficient quality report
#6 - c4-judge
2023-12-05T16:55:40Z
alex-ppg marked issue #738 as primary and marked this issue as a duplicate of 738
#7 - c4-judge
2023-12-08T22:28:35Z
alex-ppg marked the issue as satisfactory
#8 - c4-judge
2023-12-09T00:22:20Z
alex-ppg changed the severity to 2 (Med Risk)