Summary
The SantaList::buyPresent()
function can be callable paying 1 SantaToken not the expected cost of PURCHASED_PRESENT_COST = 2e18
(requirement in the Readme documentation). Consequently, individuals can obtain presents at half the intended cost.
Vulnerability Details
function buyPresent(address presentReceiver) external {
@> i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}
function burn(address from) external {
if (msg.sender != i_santasList) {
revert SantaToken__NotSantasList();
}
@> _burn(from, 1e18);
}
Impact
function testBuyPresentForOneSantaToken() public {
vm.startPrank(santa);
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
vm.startPrank(user);
santaToken.approve(address(santasList), 1e18);
santasList.collectPresent();
console.log("User SantaToken balance before", santaToken.balanceOf(user));
console.log("User Present NFT balance before", santasList.balanceOf(user));
santasList.buyPresent(user);
assertEq(santaToken.balanceOf(user), 0);
console.log("User SantaToken balance after", santaToken.balanceOf(user));
console.log("User Present NFT balance after", santasList.balanceOf(user));
vm.stopPrank();
}
Logs:
User SantaToken balance before 1000000000000000000
User Present NFT balance before 1
User SantaToken balance after 0
User Present NFT balance after 2
Tools Used
Manual Review
Recommendations
Add an if statement for checking the SantaToken balanceOf
the msg.sender
and modify the SantaToken::burn()
function.
+ error SantasList__NotEnoughtToken();
function buyPresent(address presentReceiver) external {
+ if (i_santaToken.balanceOf(msg.sender) < PURCHASED_PRESENT_COST) {
+ revert SantasList__NotEnoughtToken();
+ }
i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}
function burn(address from) external {
if (msg.sender != i_santasList) {
revert SantaToken__NotSantasList();
}
- _burn(from, 1e18);
+ _burn(from, 2e18);
}