Damn Vulnerable Defi. Задача 8. Puppet
Есть пул, который позволяет брать займы токенов, и где нужно сначала сделать депозит в два раза больше суммы, что хочешь взять, в качестве залога. Также на Uniswap v1 exchange открыт маркет, где лежат 10 Эфира и 10 токенов. Начав с 25 эфира и 1000 токенов, нужно захватить все токены с пула.
В чем суть?
Тут необходимы знания и опыт работы с Uniswap v1, так как задача предполагает прямое взаимодействие с биржей и понимание принципов ее ценообразования.
Для начала поговорим кратко о том, как формируется цена на обмен токенов в V1.
Допустим есть пул для обмена токенов DVT на ETH. Чтобы корректировать цену на них применяется формула t / z = k, где k - константа, это число не меняется для пула совсем.
Это приводит к тому, что если, например, t в пуле будет больше, то цена его будет меньше, и наоборот для z - количества в пуле меньше и цена выше.
Этим и предлагают воспользоваться в задаче.
1. Мы берем наши токены и обмениваем их на Эфир на бирже Uniswap. Обменять вообще все и опустошить пул не получится, поэтому максимум для обмена будет 9.9 ETH. Это приведет к тому, что Эфира на бирже почти не останется, зато токенов будет очень и очень много. Соответственно цена на токены упадет в разы.
2. Так как контракт PuppetPool использует функцию calculateDepositRequired() для расчета стоимости займа, которая в свою очередь опирается на цену пары Uniswap, то для займа 100 000 токенов, нам потребуется всего 20 ETH, после всех манипуляций на бирже.
Для решения задачи, нам нужно будет подключиться к бирже и выполнить обмен с помощью метода tokenToEthSwapOutput(), которые принимает в качестве аргументов сумму Эфира для обмена, количество ваших токенов, которые хотите обменять, и дедлайн (uint256). Эту информацию можно найти в официальной документации Uniswap V1 тут в разделе контракта Exchange.
Решение может быть таким:
await this.token.connect(attacker).approve( this.uniswapExchange.address, ATTACKER_INITIAL_TOKEN_BALANCE);
const tx = await this.uniswapExchange.connect(attacker).tokenToEthSwapOutput(ethers.utils.parseEther('9.9'),
ATTACKER_INITIAL_TOKEN_BALANCE,
(await ethers.provider.getBlock('latest')).timestamp * 2,
{ gasLimit: 1e6 }
);
await tx.wait();
const depositRequired = await this.lendingPool.calculateDepositRequired(POOL_INITIAL_TOKEN_BALANCE);
await this.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE, {value: depositRequired});
Для проверки используем команду в консоли yarn run puppet.
#dvd #DamnVulnerableDefi
Post #447
163