-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcreateLiquidationScenario.ts
164 lines (141 loc) · 5.18 KB
/
createLiquidationScenario.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
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
// NOTE: This script will only work if the stETH price is near WETH price normal
// and WETH oracle config is pointing to the mock oracle
import { parseEther, Address, encodeFunctionData } from 'viem';
import dotenv from 'dotenv';
import { resolve } from 'path';
import { MockOracleAbi } from './abis/MockOracleAbi';
import { BaseScenario } from './utils/baseScenario';
import { validateEnvKeys } from './utils/validateEnvKeys';
import { PTokenAbi } from '../abis/PTokenAbi';
import { MockTokenAbi } from './abis/MockTokenAbi';
// Load environment variables
dotenv.config({ path: resolve(process.cwd(), '.env.local') });
validateEnvKeys([
'FUNDING_PRIVATE_KEY',
'RISK_ENGINE_ADDRESS',
'STETH_ADDRESS',
'PSTETH_ADDRESS',
'WETH_ADDRESS',
'PWETH_ADDRESS',
]);
const MOCK_ORACLE = '0xc34df3ce38773ae01a61a6cfc93a76060fbb5d84';
const WETH_PRICE_NORMAL = BigInt('3285000000'); // $3285 with 6 decimals
const WETH_PRICE_LOW = BigInt('1001000000'); // $1001 with 6 decimals
const DECIMALS_18 = 18n;
class LiquidationScenario extends BaseScenario {
private liquidatorAccount;
private borrowerAccount;
constructor() {
super(process.env.FUNDING_PRIVATE_KEY as `0x${string}`);
// Create random wallets
this.liquidatorAccount = this.createRandomAccount();
this.borrowerAccount = this.createRandomAccount();
console.log('Liquidator address:', this.liquidatorAccount.address);
console.log('Borrower address:', this.borrowerAccount.address);
}
private async setWethPrice(price: bigint) {
const hash = await this.sendTransaction({
account: this.fundingAccount,
to: MOCK_ORACLE,
data: encodeFunctionData({
abi: MockOracleAbi,
functionName: 'setPrice',
args: [process.env.WETH_ADDRESS as Address, price, DECIMALS_18],
}),
});
await this.publicClient.waitForTransactionReceipt({ hash });
console.log(`Set WETH price to: ${price}`);
}
private async liquidate(repayAmount: bigint) {
// Approve stETH transfer for liquidation
const approveHash = await this.sendTransaction({
account: this.liquidatorAccount,
to: process.env.STETH_ADDRESS as Address,
data: encodeFunctionData({
abi: MockTokenAbi,
functionName: 'approve',
args: [process.env.PSTETH_ADDRESS as Address, repayAmount],
}),
});
await this.publicClient.waitForTransactionReceipt({ hash: approveHash });
// Liquidate
const liquidateHash = await this.sendTransaction({
account: this.liquidatorAccount,
to: process.env.PSTETH_ADDRESS as Address,
data: encodeFunctionData({
abi: PTokenAbi,
functionName: 'liquidateBorrow',
args: [
this.borrowerAccount.address,
repayAmount,
process.env.PWETH_ADDRESS as Address,
],
}),
});
await this.publicClient.waitForTransactionReceipt({ hash: liquidateHash });
console.log(`Liquidated borrower with ${repayAmount} stETH`);
}
async start() {
console.log('Starting liquidation scenario...');
try {
// Send initial ETH to both accounts
console.log('\nSending initial ETH...');
await this.sendInitialEth(this.liquidatorAccount.address);
await this.sendInitialEth(this.borrowerAccount.address);
// Mint WETH for borrower
console.log('\nMinting WETH for borrower...');
await this.mintToken(
this.borrowerAccount,
process.env.WETH_ADDRESS as Address
);
// Deposit 0.1 WETH and enter market
console.log('\nDepositing WETH and entering market...');
const depositAmount = parseEther('0.1');
await this.deposit(
this.borrowerAccount,
depositAmount,
process.env.WETH_ADDRESS as Address,
process.env.PWETH_ADDRESS as Address
);
await this.enterMarket(
this.borrowerAccount,
process.env.PWETH_ADDRESS as Address,
process.env.RISK_ENGINE_ADDRESS as Address
);
// Borrow 0.05 stETH
console.log('\nBorrowing stETH...');
const borrowAmount = parseEther('0.05');
await this.borrow(
this.borrowerAccount,
process.env.PSTETH_ADDRESS as Address,
borrowAmount
);
// Set WETH price low to trigger liquidation
console.log('\nSetting WETH price low...');
await this.setWethPrice(WETH_PRICE_LOW);
// Mint stETH for liquidator
console.log('\nMinting stETH for liquidator...');
await this.mintToken(
this.liquidatorAccount,
process.env.STETH_ADDRESS as Address
);
// Liquidate 0.005 stETH
console.log('\nPerforming liquidation...');
const liquidateAmount = parseEther('0.005');
await this.liquidate(liquidateAmount);
// Set WETH price back to normal
console.log('\nSetting WETH price back to normal...');
await this.setWethPrice(WETH_PRICE_NORMAL);
console.log('\nLiquidation scenario completed successfully!');
} catch (error) {
console.error('Error in liquidation scenario:', error);
process.exit(1);
}
}
}
// Execute the scenario
const scenario = new LiquidationScenario();
scenario.start().catch(error => {
console.error('Failed to execute liquidation scenario:', error);
process.exit(1);
});