Damn Vulnerable Defi. Задача 9. Puppet v2
Задача очень похожа на предыдущую, только сейчас уже используется Uniswap v2. Мы стартуем с 20 Эфиром и 10 000 токенами, и целью является захват всего миллиона токенов в пуле.
В чем суть?
Тут я просидел пару часов, но так и не смог дойти до финала решения задачи, поэтому пришлось идти гуглить, как довести до конца.
Из первой задачи я понял, что и тут нужно сделать обмен на бирже, чтобы понизить цену токена, но потом затормозил в расчетах.
Итак, пойдем по шагам:
У нас на счету есть 20 Эфира и 10 000 токенов DTV. В пуле 1 млн токенов. Нам нужно узнать, сколько Эфира потребуется для займа.
Сумма депозита для займа рассчитывается функцией calculateDepositOfWETHRequired(), которая, в свою очередь, обращается к служебной функции _getOracleQuote(), и которая получает значение из Uniswap функции UniswapV2Library.quote().
В библиотеки Uniswap эта функция выглядит так:
function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
require(
reserveA > 0 && reserveB > 0,
'UniswapV2Library:INSUFFICIENT_LIQUIDITY'
);
amountB = amountA.mul(reserveB) / reserveA;
}
Т.е. amountB = amountA.mul(reserveB) / reserveA можно перевести для нашей задачи в:
costForAllDVTInPool = amountToBorrow * (reserveWETH / reserveDVT)
Теперь заменим строки значениями, которые даны нам по условию задачи:
ИтоговаяЦенаТокеновПула = 1 000 000 (10 / 100) получается 100 000 WETH.
И так как, опять же по контракту, нам нужно иметь сумму х3 для возможности займа, то всего требуется 300 000 WETH.
С этим понятно. Идем дальше.
На бирже Uniswap мы совершаем обмен всех наших токенов DVT на WETH, из чего их соотношение меняется с (100/10) до (10 100 / 0,09), соответственно цена DVT падает, а мы получаем 9.9 WETH.
Вернемся к формуле теперь:
newCostForAllDVTInPool = 1000000 * (0,09... / 10100) = 9.8...
И для полного займа нам потребуется 9.8 х 3 = 29.4... WETH.
У нас, после обмена, уже есть 9.9, а значит недостает 19.6 WETH.
По условиям задачи у нас уже есть 20 ETH, которые прораниваются 1/1 к WETH. Мы делаем депозит на контракт 19.6 ETH и получаем столько же WETH.
Все готово, теперь мы можем взять займ с пула на весь млн токенов!
Решение может выглядеть так:
await this.token.connect(attacker).approve(this.uniswapRouter.address, ATTACKER_INITIAL_TOKEN_BALANCE)
await this.uniswapRouter.connect(attacker).swapExactTokensForTokens(
ATTACKER_INITIAL_TOKEN_BALANCE,
0,
[this.token.address, this.weth.address],
attacker.address,
(await ethers.provider.getBlock('latest')).timestamp * 2
)
await this.weth.connect(attacker).deposit({ value: ethers.utils.parseEther('19.6') })
const wethRequired = await this.lendingPool.calculateDepositOfWETHRequired(
POOL_INITIAL_TOKEN_BALANCE)
await this.weth.connect(attacker).approve(this.lendingPool.address, wethRequired)
await this.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE)
Проверяем командой yarn run puppet-v2.
Для меня тут сложность вызвало то, что нужно было держать в голове два токена (DTV, WETH) и сам Эфир, жонглируя ими в течение всех операций. Только смотря в решение и разбирая каждую строку, я смог, наконец, понять все идею взлома.
#dvd #DamnVulnerableDefi
Post #448
166
- 👍 1