@gearbox-protocol/sdk 3.0.0-vfour.120 → 3.0.0-vfour.122
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/dev/index.cjs +309 -47
- package/dist/cjs/dev/index.d.ts +21 -2
- package/dist/cjs/sdk/index.cjs +35 -2
- package/dist/cjs/sdk/index.d.ts +14 -0
- package/dist/esm/dev/index.d.mts +21 -2
- package/dist/esm/dev/index.mjs +310 -49
- package/dist/esm/sdk/index.d.mts +14 -0
- package/dist/esm/sdk/index.mjs +35 -2
- package/package.json +2 -1
package/dist/cjs/dev/index.cjs
CHANGED
|
@@ -1,54 +1,12 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
3
|
+
var sdkGov = require('@gearbox-protocol/sdk-gov');
|
|
4
4
|
var viem = require('viem');
|
|
5
|
+
var accounts = require('viem/accounts');
|
|
6
|
+
var sdk = require('../sdk');
|
|
7
|
+
var abi = require('../sdk/abi');
|
|
5
8
|
|
|
6
|
-
// src/dev/
|
|
7
|
-
async function calcLiquidatableLTs(sdk$1, ca, factor = 9990n, logger) {
|
|
8
|
-
const cm = sdk$1.marketRegister.findCreditManager(ca.creditManager);
|
|
9
|
-
const market = sdk$1.marketRegister.findByCreditManager(ca.creditManager);
|
|
10
|
-
const weightedBalances = ca.tokens.map((t) => {
|
|
11
|
-
const { token, balance } = t;
|
|
12
|
-
const balanceU = market.priceOracle.convertToUnderlying(token, balance);
|
|
13
|
-
const lt = BigInt(cm.collateralTokens[token]);
|
|
14
|
-
return {
|
|
15
|
-
token,
|
|
16
|
-
weightedBalance: balanceU * lt / sdk.PERCENTAGE_FACTOR,
|
|
17
|
-
lt
|
|
18
|
-
};
|
|
19
|
-
}).sort((a, b) => {
|
|
20
|
-
if (a.token === ca.underlying) return 1;
|
|
21
|
-
if (b.token === ca.underlying) return -1;
|
|
22
|
-
return b.weightedBalance > a.weightedBalance ? 1 : -1;
|
|
23
|
-
});
|
|
24
|
-
let divisor = 0n;
|
|
25
|
-
let dividend = factor * (ca.debt + ca.accruedInterest + ca.accruedFees) / sdk.PERCENTAGE_FACTOR;
|
|
26
|
-
for (const { token, weightedBalance } of weightedBalances) {
|
|
27
|
-
if (token === ca.underlying) {
|
|
28
|
-
dividend -= weightedBalance;
|
|
29
|
-
} else {
|
|
30
|
-
divisor += weightedBalance;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (divisor === 0n) {
|
|
34
|
-
throw new Error("warning: assets have zero weighted value in underlying");
|
|
35
|
-
}
|
|
36
|
-
if (dividend <= 0n) {
|
|
37
|
-
throw new Error(`warning: account balance in underlying covers debt`);
|
|
38
|
-
}
|
|
39
|
-
const k = sdk.WAD * dividend / divisor;
|
|
40
|
-
const result = {};
|
|
41
|
-
for (const { token, lt: oldLT } of weightedBalances) {
|
|
42
|
-
if (token !== ca.underlying) {
|
|
43
|
-
const newLT = oldLT * k / sdk.WAD;
|
|
44
|
-
logger?.debug(
|
|
45
|
-
`proposed ${sdk$1.tokensMeta.symbol(token)} LT change: ${oldLT} => ${newLT} `
|
|
46
|
-
);
|
|
47
|
-
result[token] = Number(newLT);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return result;
|
|
51
|
-
}
|
|
9
|
+
// src/dev/AccountOpener.ts
|
|
52
10
|
function createAnvilClient({
|
|
53
11
|
chain,
|
|
54
12
|
transport
|
|
@@ -95,6 +53,309 @@ async function evmMineDetailed(client, timestamp) {
|
|
|
95
53
|
}
|
|
96
54
|
}
|
|
97
55
|
|
|
56
|
+
// src/dev/AccountOpener.ts
|
|
57
|
+
var AccountOpener = class {
|
|
58
|
+
#service;
|
|
59
|
+
#anvil;
|
|
60
|
+
#logger;
|
|
61
|
+
#borrower;
|
|
62
|
+
#faucet;
|
|
63
|
+
constructor(service, options = {}) {
|
|
64
|
+
this.#service = service;
|
|
65
|
+
this.#logger = sdk.childLogger("AccountOpener", service.sdk.logger);
|
|
66
|
+
this.#anvil = createAnvilClient({
|
|
67
|
+
chain: service.sdk.provider.chain,
|
|
68
|
+
transport: service.sdk.provider.transport
|
|
69
|
+
});
|
|
70
|
+
this.#faucet = options.faucet ?? service.sdk.addressProvider.getAddress("FAUCET");
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Tries to open account with underlying only in each CM
|
|
74
|
+
*/
|
|
75
|
+
async openCreditAccounts(targets) {
|
|
76
|
+
const borrower = await this.#prepareBorrower(targets);
|
|
77
|
+
for (const target of targets) {
|
|
78
|
+
try {
|
|
79
|
+
await this.#openAccount(target);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
this.#logger?.error(e);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const accounts = await this.#service.getCreditAccounts({
|
|
85
|
+
owner: borrower.address
|
|
86
|
+
});
|
|
87
|
+
this.#logger?.info(`opened ${accounts.length} accounts`);
|
|
88
|
+
}
|
|
89
|
+
async #openAccount({
|
|
90
|
+
creditManager,
|
|
91
|
+
collateral,
|
|
92
|
+
leverage = 4,
|
|
93
|
+
slippage = 50
|
|
94
|
+
}) {
|
|
95
|
+
const borrower = await this.#getBorrower();
|
|
96
|
+
const cm = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
97
|
+
const symbol = this.sdk.tokensMeta.symbol(collateral);
|
|
98
|
+
const logger = this.#logger?.child?.({
|
|
99
|
+
creditManager: cm.name,
|
|
100
|
+
collateral: symbol
|
|
101
|
+
});
|
|
102
|
+
const { minDebt, underlying } = cm.creditFacade;
|
|
103
|
+
const expectedBalances = [];
|
|
104
|
+
const leftoverBalances = [];
|
|
105
|
+
for (const t of Object.keys(cm.collateralTokens)) {
|
|
106
|
+
const token = t;
|
|
107
|
+
expectedBalances.push({
|
|
108
|
+
token,
|
|
109
|
+
balance: token === underlying ? BigInt(leverage) * minDebt : 1n
|
|
110
|
+
});
|
|
111
|
+
leftoverBalances.push({
|
|
112
|
+
token,
|
|
113
|
+
balance: 1n
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
logger?.debug("looking for open strategy");
|
|
117
|
+
const strategy = await this.sdk.router.findOpenStrategyPath({
|
|
118
|
+
creditManager: cm.creditManager,
|
|
119
|
+
expectedBalances,
|
|
120
|
+
leftoverBalances,
|
|
121
|
+
slippage,
|
|
122
|
+
target: collateral
|
|
123
|
+
});
|
|
124
|
+
logger?.debug("found open strategy");
|
|
125
|
+
const { tx, calls } = await this.#service.openCA({
|
|
126
|
+
creditManager: cm.creditManager.address,
|
|
127
|
+
averageQuota: [],
|
|
128
|
+
minQuota: [],
|
|
129
|
+
collateral: [{ token: underlying, balance: minDebt }],
|
|
130
|
+
debt: minDebt * BigInt(leverage - 1),
|
|
131
|
+
calls: strategy.calls,
|
|
132
|
+
ethAmount: 0n,
|
|
133
|
+
permits: {},
|
|
134
|
+
to: borrower.address,
|
|
135
|
+
referralCode: 0n
|
|
136
|
+
});
|
|
137
|
+
for (let i = 0; i < calls.length; i++) {
|
|
138
|
+
const call = calls[i];
|
|
139
|
+
logger?.debug(
|
|
140
|
+
`call #${i + 1}: ${this.sdk.parseFunctionData(call.target, call.callData)}`
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
logger?.debug("prepared open account transaction");
|
|
144
|
+
const hash = await sdk.sendRawTx(this.#anvil, {
|
|
145
|
+
tx,
|
|
146
|
+
account: borrower
|
|
147
|
+
});
|
|
148
|
+
logger?.debug(`send transaction ${hash}`);
|
|
149
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({ hash });
|
|
150
|
+
if (receipt.status === "reverted") {
|
|
151
|
+
throw new Error(`open credit account tx ${hash} reverted`);
|
|
152
|
+
}
|
|
153
|
+
logger?.debug(
|
|
154
|
+
`opened credit account in ${cm.name} with collateral ${symbol}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Creates borrower wallet,
|
|
159
|
+
* Sets ETH balance,
|
|
160
|
+
* Gets tokens from faucet,
|
|
161
|
+
* Approves collateral tokens to credit manager,
|
|
162
|
+
* Gets DEGEN_NFT,
|
|
163
|
+
*/
|
|
164
|
+
async #prepareBorrower(targets) {
|
|
165
|
+
const borrower = await this.#getBorrower();
|
|
166
|
+
let claimUSD = 0n;
|
|
167
|
+
let degenNFTS = {};
|
|
168
|
+
for (const target of targets) {
|
|
169
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
170
|
+
target.creditManager
|
|
171
|
+
);
|
|
172
|
+
const market = this.sdk.marketRegister.findByCreditManager(
|
|
173
|
+
target.creditManager
|
|
174
|
+
);
|
|
175
|
+
const { minDebt, degenNFT } = cm.creditFacade;
|
|
176
|
+
claimUSD += market.priceOracle.convertToUSD(cm.underlying, minDebt);
|
|
177
|
+
if (viem.isAddress(degenNFT) && degenNFT !== sdkGov.ADDRESS_0X0) {
|
|
178
|
+
degenNFTS[degenNFT] = (degenNFTS[degenNFT] ?? 0) + 1;
|
|
179
|
+
}
|
|
180
|
+
for (const t of Object.keys(cm.collateralTokens)) {
|
|
181
|
+
await this.#approve(t, cm);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
claimUSD = claimUSD * 11n / 10n;
|
|
185
|
+
this.#logger?.debug(`claiming ${sdkGov.formatBN(claimUSD, 8)} USD from faucet`);
|
|
186
|
+
let hash = await this.#anvil.writeContract({
|
|
187
|
+
account: borrower,
|
|
188
|
+
address: this.#faucet,
|
|
189
|
+
abi: [
|
|
190
|
+
{
|
|
191
|
+
type: "function",
|
|
192
|
+
inputs: [
|
|
193
|
+
{ name: "amountUSD", internalType: "uint256", type: "uint256" }
|
|
194
|
+
],
|
|
195
|
+
name: "claim",
|
|
196
|
+
outputs: [],
|
|
197
|
+
stateMutability: "nonpayable"
|
|
198
|
+
}
|
|
199
|
+
],
|
|
200
|
+
functionName: "claim",
|
|
201
|
+
args: [claimUSD],
|
|
202
|
+
chain: this.#anvil.chain
|
|
203
|
+
});
|
|
204
|
+
let receipt = await this.#anvil.waitForTransactionReceipt({
|
|
205
|
+
hash
|
|
206
|
+
});
|
|
207
|
+
if (receipt.status === "reverted") {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`borrower ${borrower.address} failed to claimed equivalent of ${sdkGov.formatBN(claimUSD, 8)} USD from faucet, tx: ${hash}`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
this.#logger?.debug(
|
|
213
|
+
`borrower ${borrower.address} claimed equivalent of ${sdkGov.formatBN(claimUSD, 8)} USD from faucet, tx: ${hash}`
|
|
214
|
+
);
|
|
215
|
+
for (const [degenNFT, amount] of Object.entries(degenNFTS)) {
|
|
216
|
+
await this.#mintDegenNft(degenNFT, borrower.address, amount);
|
|
217
|
+
}
|
|
218
|
+
this.#logger?.debug("prepared borrower");
|
|
219
|
+
return borrower;
|
|
220
|
+
}
|
|
221
|
+
async #approve(token, cm) {
|
|
222
|
+
const borrower = await this.#getBorrower();
|
|
223
|
+
try {
|
|
224
|
+
const hash = await this.#anvil.writeContract({
|
|
225
|
+
account: borrower,
|
|
226
|
+
address: token,
|
|
227
|
+
abi: abi.ierc20Abi,
|
|
228
|
+
functionName: "approve",
|
|
229
|
+
args: [cm.creditManager.address, sdk.MAX_UINT256],
|
|
230
|
+
chain: this.#anvil.chain
|
|
231
|
+
});
|
|
232
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({
|
|
233
|
+
hash
|
|
234
|
+
});
|
|
235
|
+
if (receipt.status === "reverted") {
|
|
236
|
+
this.#logger?.error(
|
|
237
|
+
`failed to allowed credit manager ${cm.creditManager.name} to spend ${token}, tx reverted: ${hash}`
|
|
238
|
+
);
|
|
239
|
+
} else {
|
|
240
|
+
this.#logger?.debug(
|
|
241
|
+
`allowed credit manager ${cm.creditManager.name} to spend ${token}, tx: ${hash}`
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
} catch (e) {
|
|
245
|
+
this.#logger?.error(
|
|
246
|
+
`failed to allowed credit manager ${cm.creditManager.name} to spend ${token}: ${e}`
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async #mintDegenNft(degenNFT, to, amount) {
|
|
251
|
+
if (amount <= 0) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
let minter;
|
|
255
|
+
try {
|
|
256
|
+
minter = await this.#anvil.readContract({
|
|
257
|
+
address: degenNFT,
|
|
258
|
+
abi: abi.iDegenNftv2Abi,
|
|
259
|
+
functionName: "minter"
|
|
260
|
+
});
|
|
261
|
+
} catch (e) {
|
|
262
|
+
this.#logger?.error(`failed to get minter of degenNFT ${degenNFT}: ${e}`);
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
try {
|
|
266
|
+
await this.#anvil.impersonateAccount({ address: minter });
|
|
267
|
+
await this.#anvil.setBalance({
|
|
268
|
+
address: minter,
|
|
269
|
+
value: viem.parseEther("100")
|
|
270
|
+
});
|
|
271
|
+
const hash = await this.#anvil.writeContract({
|
|
272
|
+
account: minter,
|
|
273
|
+
address: degenNFT,
|
|
274
|
+
abi: abi.iDegenNftv2Abi,
|
|
275
|
+
functionName: "mint",
|
|
276
|
+
args: [to, BigInt(amount)],
|
|
277
|
+
chain: this.#anvil.chain
|
|
278
|
+
});
|
|
279
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({
|
|
280
|
+
hash
|
|
281
|
+
});
|
|
282
|
+
if (receipt.status === "reverted") {
|
|
283
|
+
this.#logger?.error(
|
|
284
|
+
`failed to mint ${amount} degenNFT ${degenNFT} to borrower ${to}, tx reverted: ${hash}`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
this.#logger?.debug(
|
|
288
|
+
`minted ${amount} degenNFT ${degenNFT} to borrower ${to}, tx: ${hash}`
|
|
289
|
+
);
|
|
290
|
+
} catch (e) {
|
|
291
|
+
this.#logger?.error(
|
|
292
|
+
`failed to mint ${amount} degenNFT ${degenNFT} to borrower ${to}: ${e}`
|
|
293
|
+
);
|
|
294
|
+
} finally {
|
|
295
|
+
await this.#anvil.stopImpersonatingAccount({ address: minter });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
async #getBorrower() {
|
|
299
|
+
if (!this.#borrower) {
|
|
300
|
+
this.#borrower = accounts.privateKeyToAccount(accounts.generatePrivateKey());
|
|
301
|
+
await this.#anvil.setBalance({
|
|
302
|
+
address: this.#borrower.address,
|
|
303
|
+
value: viem.parseEther("100")
|
|
304
|
+
});
|
|
305
|
+
this.#logger?.info(`created borrower ${this.#borrower.address}`);
|
|
306
|
+
}
|
|
307
|
+
return this.#borrower;
|
|
308
|
+
}
|
|
309
|
+
get sdk() {
|
|
310
|
+
return this.#service.sdk;
|
|
311
|
+
}
|
|
312
|
+
};
|
|
313
|
+
async function calcLiquidatableLTs(sdk$1, ca, factor = 9990n, logger) {
|
|
314
|
+
const cm = sdk$1.marketRegister.findCreditManager(ca.creditManager);
|
|
315
|
+
const market = sdk$1.marketRegister.findByCreditManager(ca.creditManager);
|
|
316
|
+
const weightedBalances = ca.tokens.map((t) => {
|
|
317
|
+
const { token, balance } = t;
|
|
318
|
+
const balanceU = market.priceOracle.convertToUnderlying(token, balance);
|
|
319
|
+
const lt = BigInt(cm.collateralTokens[token]);
|
|
320
|
+
return {
|
|
321
|
+
token,
|
|
322
|
+
weightedBalance: balanceU * lt / sdk.PERCENTAGE_FACTOR,
|
|
323
|
+
lt
|
|
324
|
+
};
|
|
325
|
+
}).sort((a, b) => {
|
|
326
|
+
if (a.token === ca.underlying) return 1;
|
|
327
|
+
if (b.token === ca.underlying) return -1;
|
|
328
|
+
return b.weightedBalance > a.weightedBalance ? 1 : -1;
|
|
329
|
+
});
|
|
330
|
+
let divisor = 0n;
|
|
331
|
+
let dividend = factor * (ca.debt + ca.accruedInterest + ca.accruedFees) / sdk.PERCENTAGE_FACTOR;
|
|
332
|
+
for (const { token, weightedBalance } of weightedBalances) {
|
|
333
|
+
if (token === ca.underlying) {
|
|
334
|
+
dividend -= weightedBalance;
|
|
335
|
+
} else {
|
|
336
|
+
divisor += weightedBalance;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (divisor === 0n) {
|
|
340
|
+
throw new Error("warning: assets have zero weighted value in underlying");
|
|
341
|
+
}
|
|
342
|
+
if (dividend <= 0n) {
|
|
343
|
+
throw new Error(`warning: account balance in underlying covers debt`);
|
|
344
|
+
}
|
|
345
|
+
const k = sdk.WAD * dividend / divisor;
|
|
346
|
+
const result = {};
|
|
347
|
+
for (const { token, lt: oldLT } of weightedBalances) {
|
|
348
|
+
if (token !== ca.underlying) {
|
|
349
|
+
const newLT = oldLT * k / sdk.WAD;
|
|
350
|
+
logger?.debug(
|
|
351
|
+
`proposed ${sdk$1.tokensMeta.symbol(token)} LT change: ${oldLT} => ${newLT} `
|
|
352
|
+
);
|
|
353
|
+
result[token] = Number(newLT);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
|
|
98
359
|
// src/dev/abi/v3.ts
|
|
99
360
|
var iaclAbi = [
|
|
100
361
|
{
|
|
@@ -1602,6 +1863,7 @@ async function setLTZero(anvil, cm, logger) {
|
|
|
1602
1863
|
await anvil.stopImpersonatingAccount({ address: configuratorAddr });
|
|
1603
1864
|
}
|
|
1604
1865
|
|
|
1866
|
+
exports.AccountOpener = AccountOpener;
|
|
1605
1867
|
exports.anvilNodeInfo = anvilNodeInfo;
|
|
1606
1868
|
exports.calcLiquidatableLTs = calcLiquidatableLTs;
|
|
1607
1869
|
exports.createAnvilClient = createAnvilClient;
|
package/dist/cjs/dev/index.d.ts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { Address, TestRpcSchema, Hex, Block, Prettify, Client, Transport, Chain, TestActions, PublicClient, WalletClient } from 'viem';
|
|
2
|
-
import { GearboxSDK, CreditAccountData, ILogger, CreditManagerState } from '../sdk';
|
|
2
|
+
import { CreditAccountsService, GearboxSDK, CreditAccountData, ILogger, CreditManagerState } from '../sdk';
|
|
3
|
+
|
|
4
|
+
interface AccountOpenerOptions {
|
|
5
|
+
faucet?: Address;
|
|
6
|
+
}
|
|
7
|
+
interface TargetAccount {
|
|
8
|
+
creditManager: Address;
|
|
9
|
+
collateral: Address;
|
|
10
|
+
leverage?: number;
|
|
11
|
+
slippage?: number;
|
|
12
|
+
}
|
|
13
|
+
declare class AccountOpener {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(service: CreditAccountsService, options?: AccountOpenerOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Tries to open account with underlying only in each CM
|
|
18
|
+
*/
|
|
19
|
+
openCreditAccounts(targets: TargetAccount[]): Promise<void>;
|
|
20
|
+
private get sdk();
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
/**
|
|
5
24
|
* Given credit accounts, calculates new liquidation thresholds that needs to be set to drop account health factor a bit to make it eligible for partial liquidation
|
|
@@ -85,4 +104,4 @@ declare function setLTs(anvil: AnvilClient, cm: CreditManagerState, newLTs: Reco
|
|
|
85
104
|
|
|
86
105
|
declare function setLTZero(anvil: AnvilClient, cm: CreditManagerState, logger?: ILogger): Promise<void>;
|
|
87
106
|
|
|
88
|
-
export { type AnvilActions, type AnvilClient, type AnvilClientConfig, type AnvilNodeInfo, type AnvilRPCSchema, anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
|
107
|
+
export { AccountOpener, type AccountOpenerOptions, type AnvilActions, type AnvilClient, type AnvilClientConfig, type AnvilNodeInfo, type AnvilRPCSchema, type TargetAccount, anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
package/dist/cjs/sdk/index.cjs
CHANGED
|
@@ -27052,16 +27052,37 @@ var MellowLRTPriceFeedContract = class extends AbstractLPPriceFeedContract {
|
|
|
27052
27052
|
return stack.totalValue * BigInt(1e18) / stack.totalSupply;
|
|
27053
27053
|
}
|
|
27054
27054
|
};
|
|
27055
|
-
|
|
27056
|
-
// src/sdk/market/pricefeeds/PendleTWAPPTPriceFeed.ts
|
|
27057
27055
|
var abi28 = pendleTWAPPTPriceFeedAbi;
|
|
27058
27056
|
var PendleTWAPPTPriceFeed = class extends AbstractPriceFeedContract {
|
|
27057
|
+
market;
|
|
27058
|
+
sy;
|
|
27059
|
+
yt;
|
|
27060
|
+
expiry;
|
|
27061
|
+
twapWindow;
|
|
27062
|
+
priceToSy;
|
|
27059
27063
|
constructor(sdk, args) {
|
|
27060
27064
|
super(sdk, {
|
|
27061
27065
|
...args,
|
|
27062
27066
|
name: "PendleTWAPPTPriceFeed",
|
|
27063
27067
|
abi: abi28
|
|
27064
27068
|
});
|
|
27069
|
+
const decoded = viem.decodeAbiParameters(
|
|
27070
|
+
[
|
|
27071
|
+
{ type: "address", name: "market" },
|
|
27072
|
+
{ type: "address", name: "sy" },
|
|
27073
|
+
{ type: "address", name: "yt" },
|
|
27074
|
+
{ type: "uint256", name: "expiry" },
|
|
27075
|
+
{ type: "uint32", name: "twapWindow" },
|
|
27076
|
+
{ type: "bool", name: "priceToSy" }
|
|
27077
|
+
],
|
|
27078
|
+
args.baseParams.serializedParams
|
|
27079
|
+
);
|
|
27080
|
+
this.market = decoded[0];
|
|
27081
|
+
this.sy = decoded[1];
|
|
27082
|
+
this.yt = decoded[2];
|
|
27083
|
+
this.expiry = decoded[3];
|
|
27084
|
+
this.twapWindow = decoded[4];
|
|
27085
|
+
this.priceToSy = decoded[5];
|
|
27065
27086
|
}
|
|
27066
27087
|
};
|
|
27067
27088
|
|
|
@@ -27704,6 +27725,18 @@ var PriceOracleBaseContract = class extends BaseContract {
|
|
|
27704
27725
|
const toScale = 10n ** BigInt(this.sdk.tokensMeta.decimals(to));
|
|
27705
27726
|
return amount * fromPrice * toScale / (toPrice * fromScale);
|
|
27706
27727
|
}
|
|
27728
|
+
/**
|
|
27729
|
+
* Tries to convert amount of token to USD, using latest known prices
|
|
27730
|
+
* @param from
|
|
27731
|
+
* @param to
|
|
27732
|
+
* @param amount
|
|
27733
|
+
* @param reserve
|
|
27734
|
+
*/
|
|
27735
|
+
convertToUSD(from, amount, reserve = false) {
|
|
27736
|
+
const price = reserve ? this.reservePrices.mustGet(from) : this.mainPrices.mustGet(from);
|
|
27737
|
+
const scale = 10n ** BigInt(this.sdk.tokensMeta.decimals(from));
|
|
27738
|
+
return amount * price / scale;
|
|
27739
|
+
}
|
|
27707
27740
|
/**
|
|
27708
27741
|
* Loads new prices for this oracle from PriceFeedCompressor
|
|
27709
27742
|
* Does not update price feeds, only updates prices
|
package/dist/cjs/sdk/index.d.ts
CHANGED
|
@@ -25312,6 +25312,12 @@ declare const abi$j: readonly [{
|
|
|
25312
25312
|
}];
|
|
25313
25313
|
type abi$j = typeof abi$j;
|
|
25314
25314
|
declare class PendleTWAPPTPriceFeed extends AbstractPriceFeedContract<abi$j> {
|
|
25315
|
+
readonly market: Address;
|
|
25316
|
+
readonly sy: Address;
|
|
25317
|
+
readonly yt: Address;
|
|
25318
|
+
readonly expiry: bigint;
|
|
25319
|
+
readonly twapWindow: number;
|
|
25320
|
+
readonly priceToSy: boolean;
|
|
25315
25321
|
constructor(sdk: GearboxSDK, args: PartialPriceFeedTreeNode);
|
|
25316
25322
|
}
|
|
25317
25323
|
|
|
@@ -25581,6 +25587,14 @@ declare class PriceOracleBaseContract<abi extends Abi | readonly unknown[]> exte
|
|
|
25581
25587
|
* @param reserve
|
|
25582
25588
|
*/
|
|
25583
25589
|
convert(from: Address, to: Address, amount: bigint, reserve?: boolean): bigint;
|
|
25590
|
+
/**
|
|
25591
|
+
* Tries to convert amount of token to USD, using latest known prices
|
|
25592
|
+
* @param from
|
|
25593
|
+
* @param to
|
|
25594
|
+
* @param amount
|
|
25595
|
+
* @param reserve
|
|
25596
|
+
*/
|
|
25597
|
+
convertToUSD(from: Address, amount: bigint, reserve?: boolean): bigint;
|
|
25584
25598
|
/**
|
|
25585
25599
|
* Loads new prices for this oracle from PriceFeedCompressor
|
|
25586
25600
|
* Does not update price feeds, only updates prices
|
package/dist/esm/dev/index.d.mts
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
import { Address, TestRpcSchema, Hex, Block, Prettify, Client, Transport, Chain, TestActions, PublicClient, WalletClient } from 'viem';
|
|
2
|
-
import { GearboxSDK, CreditAccountData, ILogger, CreditManagerState } from '../sdk';
|
|
2
|
+
import { CreditAccountsService, GearboxSDK, CreditAccountData, ILogger, CreditManagerState } from '../sdk';
|
|
3
|
+
|
|
4
|
+
interface AccountOpenerOptions {
|
|
5
|
+
faucet?: Address;
|
|
6
|
+
}
|
|
7
|
+
interface TargetAccount {
|
|
8
|
+
creditManager: Address;
|
|
9
|
+
collateral: Address;
|
|
10
|
+
leverage?: number;
|
|
11
|
+
slippage?: number;
|
|
12
|
+
}
|
|
13
|
+
declare class AccountOpener {
|
|
14
|
+
#private;
|
|
15
|
+
constructor(service: CreditAccountsService, options?: AccountOpenerOptions);
|
|
16
|
+
/**
|
|
17
|
+
* Tries to open account with underlying only in each CM
|
|
18
|
+
*/
|
|
19
|
+
openCreditAccounts(targets: TargetAccount[]): Promise<void>;
|
|
20
|
+
private get sdk();
|
|
21
|
+
}
|
|
3
22
|
|
|
4
23
|
/**
|
|
5
24
|
* Given credit accounts, calculates new liquidation thresholds that needs to be set to drop account health factor a bit to make it eligible for partial liquidation
|
|
@@ -85,4 +104,4 @@ declare function setLTs(anvil: AnvilClient, cm: CreditManagerState, newLTs: Reco
|
|
|
85
104
|
|
|
86
105
|
declare function setLTZero(anvil: AnvilClient, cm: CreditManagerState, logger?: ILogger): Promise<void>;
|
|
87
106
|
|
|
88
|
-
export { type AnvilActions, type AnvilClient, type AnvilClientConfig, type AnvilNodeInfo, type AnvilRPCSchema, anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
|
107
|
+
export { AccountOpener, type AccountOpenerOptions, type AnvilActions, type AnvilClient, type AnvilClientConfig, type AnvilNodeInfo, type AnvilRPCSchema, type TargetAccount, anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
package/dist/esm/dev/index.mjs
CHANGED
|
@@ -1,52 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createTestClient, publicActions, walletActions, toHex, parseEther } from 'viem';
|
|
1
|
+
import { ADDRESS_0X0, formatBN } from '@gearbox-protocol/sdk-gov';
|
|
2
|
+
import { createTestClient, publicActions, walletActions, toHex, isAddress, parseEther } from 'viem';
|
|
3
|
+
import { privateKeyToAccount, generatePrivateKey } from 'viem/accounts';
|
|
4
|
+
import { childLogger, sendRawTx, MAX_UINT256, PERCENTAGE_FACTOR, WAD } from '../sdk/index.mjs';
|
|
5
|
+
import { ierc20Abi, iDegenNftv2Abi } from '../sdk/abi';
|
|
3
6
|
|
|
4
|
-
// src/dev/
|
|
5
|
-
async function calcLiquidatableLTs(sdk, ca, factor = 9990n, logger) {
|
|
6
|
-
const cm = sdk.marketRegister.findCreditManager(ca.creditManager);
|
|
7
|
-
const market = sdk.marketRegister.findByCreditManager(ca.creditManager);
|
|
8
|
-
const weightedBalances = ca.tokens.map((t) => {
|
|
9
|
-
const { token, balance } = t;
|
|
10
|
-
const balanceU = market.priceOracle.convertToUnderlying(token, balance);
|
|
11
|
-
const lt = BigInt(cm.collateralTokens[token]);
|
|
12
|
-
return {
|
|
13
|
-
token,
|
|
14
|
-
weightedBalance: balanceU * lt / PERCENTAGE_FACTOR,
|
|
15
|
-
lt
|
|
16
|
-
};
|
|
17
|
-
}).sort((a, b) => {
|
|
18
|
-
if (a.token === ca.underlying) return 1;
|
|
19
|
-
if (b.token === ca.underlying) return -1;
|
|
20
|
-
return b.weightedBalance > a.weightedBalance ? 1 : -1;
|
|
21
|
-
});
|
|
22
|
-
let divisor = 0n;
|
|
23
|
-
let dividend = factor * (ca.debt + ca.accruedInterest + ca.accruedFees) / PERCENTAGE_FACTOR;
|
|
24
|
-
for (const { token, weightedBalance } of weightedBalances) {
|
|
25
|
-
if (token === ca.underlying) {
|
|
26
|
-
dividend -= weightedBalance;
|
|
27
|
-
} else {
|
|
28
|
-
divisor += weightedBalance;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
if (divisor === 0n) {
|
|
32
|
-
throw new Error("warning: assets have zero weighted value in underlying");
|
|
33
|
-
}
|
|
34
|
-
if (dividend <= 0n) {
|
|
35
|
-
throw new Error(`warning: account balance in underlying covers debt`);
|
|
36
|
-
}
|
|
37
|
-
const k = WAD * dividend / divisor;
|
|
38
|
-
const result = {};
|
|
39
|
-
for (const { token, lt: oldLT } of weightedBalances) {
|
|
40
|
-
if (token !== ca.underlying) {
|
|
41
|
-
const newLT = oldLT * k / WAD;
|
|
42
|
-
logger?.debug(
|
|
43
|
-
`proposed ${sdk.tokensMeta.symbol(token)} LT change: ${oldLT} => ${newLT} `
|
|
44
|
-
);
|
|
45
|
-
result[token] = Number(newLT);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
return result;
|
|
49
|
-
}
|
|
7
|
+
// src/dev/AccountOpener.ts
|
|
50
8
|
function createAnvilClient({
|
|
51
9
|
chain,
|
|
52
10
|
transport
|
|
@@ -93,6 +51,309 @@ async function evmMineDetailed(client, timestamp) {
|
|
|
93
51
|
}
|
|
94
52
|
}
|
|
95
53
|
|
|
54
|
+
// src/dev/AccountOpener.ts
|
|
55
|
+
var AccountOpener = class {
|
|
56
|
+
#service;
|
|
57
|
+
#anvil;
|
|
58
|
+
#logger;
|
|
59
|
+
#borrower;
|
|
60
|
+
#faucet;
|
|
61
|
+
constructor(service, options = {}) {
|
|
62
|
+
this.#service = service;
|
|
63
|
+
this.#logger = childLogger("AccountOpener", service.sdk.logger);
|
|
64
|
+
this.#anvil = createAnvilClient({
|
|
65
|
+
chain: service.sdk.provider.chain,
|
|
66
|
+
transport: service.sdk.provider.transport
|
|
67
|
+
});
|
|
68
|
+
this.#faucet = options.faucet ?? service.sdk.addressProvider.getAddress("FAUCET");
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Tries to open account with underlying only in each CM
|
|
72
|
+
*/
|
|
73
|
+
async openCreditAccounts(targets) {
|
|
74
|
+
const borrower = await this.#prepareBorrower(targets);
|
|
75
|
+
for (const target of targets) {
|
|
76
|
+
try {
|
|
77
|
+
await this.#openAccount(target);
|
|
78
|
+
} catch (e) {
|
|
79
|
+
this.#logger?.error(e);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const accounts = await this.#service.getCreditAccounts({
|
|
83
|
+
owner: borrower.address
|
|
84
|
+
});
|
|
85
|
+
this.#logger?.info(`opened ${accounts.length} accounts`);
|
|
86
|
+
}
|
|
87
|
+
async #openAccount({
|
|
88
|
+
creditManager,
|
|
89
|
+
collateral,
|
|
90
|
+
leverage = 4,
|
|
91
|
+
slippage = 50
|
|
92
|
+
}) {
|
|
93
|
+
const borrower = await this.#getBorrower();
|
|
94
|
+
const cm = this.sdk.marketRegister.findCreditManager(creditManager);
|
|
95
|
+
const symbol = this.sdk.tokensMeta.symbol(collateral);
|
|
96
|
+
const logger = this.#logger?.child?.({
|
|
97
|
+
creditManager: cm.name,
|
|
98
|
+
collateral: symbol
|
|
99
|
+
});
|
|
100
|
+
const { minDebt, underlying } = cm.creditFacade;
|
|
101
|
+
const expectedBalances = [];
|
|
102
|
+
const leftoverBalances = [];
|
|
103
|
+
for (const t of Object.keys(cm.collateralTokens)) {
|
|
104
|
+
const token = t;
|
|
105
|
+
expectedBalances.push({
|
|
106
|
+
token,
|
|
107
|
+
balance: token === underlying ? BigInt(leverage) * minDebt : 1n
|
|
108
|
+
});
|
|
109
|
+
leftoverBalances.push({
|
|
110
|
+
token,
|
|
111
|
+
balance: 1n
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
logger?.debug("looking for open strategy");
|
|
115
|
+
const strategy = await this.sdk.router.findOpenStrategyPath({
|
|
116
|
+
creditManager: cm.creditManager,
|
|
117
|
+
expectedBalances,
|
|
118
|
+
leftoverBalances,
|
|
119
|
+
slippage,
|
|
120
|
+
target: collateral
|
|
121
|
+
});
|
|
122
|
+
logger?.debug("found open strategy");
|
|
123
|
+
const { tx, calls } = await this.#service.openCA({
|
|
124
|
+
creditManager: cm.creditManager.address,
|
|
125
|
+
averageQuota: [],
|
|
126
|
+
minQuota: [],
|
|
127
|
+
collateral: [{ token: underlying, balance: minDebt }],
|
|
128
|
+
debt: minDebt * BigInt(leverage - 1),
|
|
129
|
+
calls: strategy.calls,
|
|
130
|
+
ethAmount: 0n,
|
|
131
|
+
permits: {},
|
|
132
|
+
to: borrower.address,
|
|
133
|
+
referralCode: 0n
|
|
134
|
+
});
|
|
135
|
+
for (let i = 0; i < calls.length; i++) {
|
|
136
|
+
const call = calls[i];
|
|
137
|
+
logger?.debug(
|
|
138
|
+
`call #${i + 1}: ${this.sdk.parseFunctionData(call.target, call.callData)}`
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
logger?.debug("prepared open account transaction");
|
|
142
|
+
const hash = await sendRawTx(this.#anvil, {
|
|
143
|
+
tx,
|
|
144
|
+
account: borrower
|
|
145
|
+
});
|
|
146
|
+
logger?.debug(`send transaction ${hash}`);
|
|
147
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({ hash });
|
|
148
|
+
if (receipt.status === "reverted") {
|
|
149
|
+
throw new Error(`open credit account tx ${hash} reverted`);
|
|
150
|
+
}
|
|
151
|
+
logger?.debug(
|
|
152
|
+
`opened credit account in ${cm.name} with collateral ${symbol}`
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Creates borrower wallet,
|
|
157
|
+
* Sets ETH balance,
|
|
158
|
+
* Gets tokens from faucet,
|
|
159
|
+
* Approves collateral tokens to credit manager,
|
|
160
|
+
* Gets DEGEN_NFT,
|
|
161
|
+
*/
|
|
162
|
+
async #prepareBorrower(targets) {
|
|
163
|
+
const borrower = await this.#getBorrower();
|
|
164
|
+
let claimUSD = 0n;
|
|
165
|
+
let degenNFTS = {};
|
|
166
|
+
for (const target of targets) {
|
|
167
|
+
const cm = this.sdk.marketRegister.findCreditManager(
|
|
168
|
+
target.creditManager
|
|
169
|
+
);
|
|
170
|
+
const market = this.sdk.marketRegister.findByCreditManager(
|
|
171
|
+
target.creditManager
|
|
172
|
+
);
|
|
173
|
+
const { minDebt, degenNFT } = cm.creditFacade;
|
|
174
|
+
claimUSD += market.priceOracle.convertToUSD(cm.underlying, minDebt);
|
|
175
|
+
if (isAddress(degenNFT) && degenNFT !== ADDRESS_0X0) {
|
|
176
|
+
degenNFTS[degenNFT] = (degenNFTS[degenNFT] ?? 0) + 1;
|
|
177
|
+
}
|
|
178
|
+
for (const t of Object.keys(cm.collateralTokens)) {
|
|
179
|
+
await this.#approve(t, cm);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
claimUSD = claimUSD * 11n / 10n;
|
|
183
|
+
this.#logger?.debug(`claiming ${formatBN(claimUSD, 8)} USD from faucet`);
|
|
184
|
+
let hash = await this.#anvil.writeContract({
|
|
185
|
+
account: borrower,
|
|
186
|
+
address: this.#faucet,
|
|
187
|
+
abi: [
|
|
188
|
+
{
|
|
189
|
+
type: "function",
|
|
190
|
+
inputs: [
|
|
191
|
+
{ name: "amountUSD", internalType: "uint256", type: "uint256" }
|
|
192
|
+
],
|
|
193
|
+
name: "claim",
|
|
194
|
+
outputs: [],
|
|
195
|
+
stateMutability: "nonpayable"
|
|
196
|
+
}
|
|
197
|
+
],
|
|
198
|
+
functionName: "claim",
|
|
199
|
+
args: [claimUSD],
|
|
200
|
+
chain: this.#anvil.chain
|
|
201
|
+
});
|
|
202
|
+
let receipt = await this.#anvil.waitForTransactionReceipt({
|
|
203
|
+
hash
|
|
204
|
+
});
|
|
205
|
+
if (receipt.status === "reverted") {
|
|
206
|
+
throw new Error(
|
|
207
|
+
`borrower ${borrower.address} failed to claimed equivalent of ${formatBN(claimUSD, 8)} USD from faucet, tx: ${hash}`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
this.#logger?.debug(
|
|
211
|
+
`borrower ${borrower.address} claimed equivalent of ${formatBN(claimUSD, 8)} USD from faucet, tx: ${hash}`
|
|
212
|
+
);
|
|
213
|
+
for (const [degenNFT, amount] of Object.entries(degenNFTS)) {
|
|
214
|
+
await this.#mintDegenNft(degenNFT, borrower.address, amount);
|
|
215
|
+
}
|
|
216
|
+
this.#logger?.debug("prepared borrower");
|
|
217
|
+
return borrower;
|
|
218
|
+
}
|
|
219
|
+
async #approve(token, cm) {
|
|
220
|
+
const borrower = await this.#getBorrower();
|
|
221
|
+
try {
|
|
222
|
+
const hash = await this.#anvil.writeContract({
|
|
223
|
+
account: borrower,
|
|
224
|
+
address: token,
|
|
225
|
+
abi: ierc20Abi,
|
|
226
|
+
functionName: "approve",
|
|
227
|
+
args: [cm.creditManager.address, MAX_UINT256],
|
|
228
|
+
chain: this.#anvil.chain
|
|
229
|
+
});
|
|
230
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({
|
|
231
|
+
hash
|
|
232
|
+
});
|
|
233
|
+
if (receipt.status === "reverted") {
|
|
234
|
+
this.#logger?.error(
|
|
235
|
+
`failed to allowed credit manager ${cm.creditManager.name} to spend ${token}, tx reverted: ${hash}`
|
|
236
|
+
);
|
|
237
|
+
} else {
|
|
238
|
+
this.#logger?.debug(
|
|
239
|
+
`allowed credit manager ${cm.creditManager.name} to spend ${token}, tx: ${hash}`
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
} catch (e) {
|
|
243
|
+
this.#logger?.error(
|
|
244
|
+
`failed to allowed credit manager ${cm.creditManager.name} to spend ${token}: ${e}`
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
async #mintDegenNft(degenNFT, to, amount) {
|
|
249
|
+
if (amount <= 0) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
let minter;
|
|
253
|
+
try {
|
|
254
|
+
minter = await this.#anvil.readContract({
|
|
255
|
+
address: degenNFT,
|
|
256
|
+
abi: iDegenNftv2Abi,
|
|
257
|
+
functionName: "minter"
|
|
258
|
+
});
|
|
259
|
+
} catch (e) {
|
|
260
|
+
this.#logger?.error(`failed to get minter of degenNFT ${degenNFT}: ${e}`);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
try {
|
|
264
|
+
await this.#anvil.impersonateAccount({ address: minter });
|
|
265
|
+
await this.#anvil.setBalance({
|
|
266
|
+
address: minter,
|
|
267
|
+
value: parseEther("100")
|
|
268
|
+
});
|
|
269
|
+
const hash = await this.#anvil.writeContract({
|
|
270
|
+
account: minter,
|
|
271
|
+
address: degenNFT,
|
|
272
|
+
abi: iDegenNftv2Abi,
|
|
273
|
+
functionName: "mint",
|
|
274
|
+
args: [to, BigInt(amount)],
|
|
275
|
+
chain: this.#anvil.chain
|
|
276
|
+
});
|
|
277
|
+
const receipt = await this.#anvil.waitForTransactionReceipt({
|
|
278
|
+
hash
|
|
279
|
+
});
|
|
280
|
+
if (receipt.status === "reverted") {
|
|
281
|
+
this.#logger?.error(
|
|
282
|
+
`failed to mint ${amount} degenNFT ${degenNFT} to borrower ${to}, tx reverted: ${hash}`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
this.#logger?.debug(
|
|
286
|
+
`minted ${amount} degenNFT ${degenNFT} to borrower ${to}, tx: ${hash}`
|
|
287
|
+
);
|
|
288
|
+
} catch (e) {
|
|
289
|
+
this.#logger?.error(
|
|
290
|
+
`failed to mint ${amount} degenNFT ${degenNFT} to borrower ${to}: ${e}`
|
|
291
|
+
);
|
|
292
|
+
} finally {
|
|
293
|
+
await this.#anvil.stopImpersonatingAccount({ address: minter });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
async #getBorrower() {
|
|
297
|
+
if (!this.#borrower) {
|
|
298
|
+
this.#borrower = privateKeyToAccount(generatePrivateKey());
|
|
299
|
+
await this.#anvil.setBalance({
|
|
300
|
+
address: this.#borrower.address,
|
|
301
|
+
value: parseEther("100")
|
|
302
|
+
});
|
|
303
|
+
this.#logger?.info(`created borrower ${this.#borrower.address}`);
|
|
304
|
+
}
|
|
305
|
+
return this.#borrower;
|
|
306
|
+
}
|
|
307
|
+
get sdk() {
|
|
308
|
+
return this.#service.sdk;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
async function calcLiquidatableLTs(sdk, ca, factor = 9990n, logger) {
|
|
312
|
+
const cm = sdk.marketRegister.findCreditManager(ca.creditManager);
|
|
313
|
+
const market = sdk.marketRegister.findByCreditManager(ca.creditManager);
|
|
314
|
+
const weightedBalances = ca.tokens.map((t) => {
|
|
315
|
+
const { token, balance } = t;
|
|
316
|
+
const balanceU = market.priceOracle.convertToUnderlying(token, balance);
|
|
317
|
+
const lt = BigInt(cm.collateralTokens[token]);
|
|
318
|
+
return {
|
|
319
|
+
token,
|
|
320
|
+
weightedBalance: balanceU * lt / PERCENTAGE_FACTOR,
|
|
321
|
+
lt
|
|
322
|
+
};
|
|
323
|
+
}).sort((a, b) => {
|
|
324
|
+
if (a.token === ca.underlying) return 1;
|
|
325
|
+
if (b.token === ca.underlying) return -1;
|
|
326
|
+
return b.weightedBalance > a.weightedBalance ? 1 : -1;
|
|
327
|
+
});
|
|
328
|
+
let divisor = 0n;
|
|
329
|
+
let dividend = factor * (ca.debt + ca.accruedInterest + ca.accruedFees) / PERCENTAGE_FACTOR;
|
|
330
|
+
for (const { token, weightedBalance } of weightedBalances) {
|
|
331
|
+
if (token === ca.underlying) {
|
|
332
|
+
dividend -= weightedBalance;
|
|
333
|
+
} else {
|
|
334
|
+
divisor += weightedBalance;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
if (divisor === 0n) {
|
|
338
|
+
throw new Error("warning: assets have zero weighted value in underlying");
|
|
339
|
+
}
|
|
340
|
+
if (dividend <= 0n) {
|
|
341
|
+
throw new Error(`warning: account balance in underlying covers debt`);
|
|
342
|
+
}
|
|
343
|
+
const k = WAD * dividend / divisor;
|
|
344
|
+
const result = {};
|
|
345
|
+
for (const { token, lt: oldLT } of weightedBalances) {
|
|
346
|
+
if (token !== ca.underlying) {
|
|
347
|
+
const newLT = oldLT * k / WAD;
|
|
348
|
+
logger?.debug(
|
|
349
|
+
`proposed ${sdk.tokensMeta.symbol(token)} LT change: ${oldLT} => ${newLT} `
|
|
350
|
+
);
|
|
351
|
+
result[token] = Number(newLT);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
|
|
96
357
|
// src/dev/abi/v3.ts
|
|
97
358
|
var iaclAbi = [
|
|
98
359
|
{
|
|
@@ -1600,4 +1861,4 @@ async function setLTZero(anvil, cm, logger) {
|
|
|
1600
1861
|
await anvil.stopImpersonatingAccount({ address: configuratorAddr });
|
|
1601
1862
|
}
|
|
1602
1863
|
|
|
1603
|
-
export { anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
|
1864
|
+
export { AccountOpener, anvilNodeInfo, calcLiquidatableLTs, createAnvilClient, evmMineDetailed, isAnvil, setLTZero, setLTs };
|
package/dist/esm/sdk/index.d.mts
CHANGED
|
@@ -25312,6 +25312,12 @@ declare const abi$j: readonly [{
|
|
|
25312
25312
|
}];
|
|
25313
25313
|
type abi$j = typeof abi$j;
|
|
25314
25314
|
declare class PendleTWAPPTPriceFeed extends AbstractPriceFeedContract<abi$j> {
|
|
25315
|
+
readonly market: Address;
|
|
25316
|
+
readonly sy: Address;
|
|
25317
|
+
readonly yt: Address;
|
|
25318
|
+
readonly expiry: bigint;
|
|
25319
|
+
readonly twapWindow: number;
|
|
25320
|
+
readonly priceToSy: boolean;
|
|
25315
25321
|
constructor(sdk: GearboxSDK, args: PartialPriceFeedTreeNode);
|
|
25316
25322
|
}
|
|
25317
25323
|
|
|
@@ -25581,6 +25587,14 @@ declare class PriceOracleBaseContract<abi extends Abi | readonly unknown[]> exte
|
|
|
25581
25587
|
* @param reserve
|
|
25582
25588
|
*/
|
|
25583
25589
|
convert(from: Address, to: Address, amount: bigint, reserve?: boolean): bigint;
|
|
25590
|
+
/**
|
|
25591
|
+
* Tries to convert amount of token to USD, using latest known prices
|
|
25592
|
+
* @param from
|
|
25593
|
+
* @param to
|
|
25594
|
+
* @param amount
|
|
25595
|
+
* @param reserve
|
|
25596
|
+
*/
|
|
25597
|
+
convertToUSD(from: Address, amount: bigint, reserve?: boolean): bigint;
|
|
25584
25598
|
/**
|
|
25585
25599
|
* Loads new prices for this oracle from PriceFeedCompressor
|
|
25586
25600
|
* Does not update price feeds, only updates prices
|
package/dist/esm/sdk/index.mjs
CHANGED
|
@@ -27050,16 +27050,37 @@ var MellowLRTPriceFeedContract = class extends AbstractLPPriceFeedContract {
|
|
|
27050
27050
|
return stack.totalValue * BigInt(1e18) / stack.totalSupply;
|
|
27051
27051
|
}
|
|
27052
27052
|
};
|
|
27053
|
-
|
|
27054
|
-
// src/sdk/market/pricefeeds/PendleTWAPPTPriceFeed.ts
|
|
27055
27053
|
var abi28 = pendleTWAPPTPriceFeedAbi;
|
|
27056
27054
|
var PendleTWAPPTPriceFeed = class extends AbstractPriceFeedContract {
|
|
27055
|
+
market;
|
|
27056
|
+
sy;
|
|
27057
|
+
yt;
|
|
27058
|
+
expiry;
|
|
27059
|
+
twapWindow;
|
|
27060
|
+
priceToSy;
|
|
27057
27061
|
constructor(sdk, args) {
|
|
27058
27062
|
super(sdk, {
|
|
27059
27063
|
...args,
|
|
27060
27064
|
name: "PendleTWAPPTPriceFeed",
|
|
27061
27065
|
abi: abi28
|
|
27062
27066
|
});
|
|
27067
|
+
const decoded = decodeAbiParameters(
|
|
27068
|
+
[
|
|
27069
|
+
{ type: "address", name: "market" },
|
|
27070
|
+
{ type: "address", name: "sy" },
|
|
27071
|
+
{ type: "address", name: "yt" },
|
|
27072
|
+
{ type: "uint256", name: "expiry" },
|
|
27073
|
+
{ type: "uint32", name: "twapWindow" },
|
|
27074
|
+
{ type: "bool", name: "priceToSy" }
|
|
27075
|
+
],
|
|
27076
|
+
args.baseParams.serializedParams
|
|
27077
|
+
);
|
|
27078
|
+
this.market = decoded[0];
|
|
27079
|
+
this.sy = decoded[1];
|
|
27080
|
+
this.yt = decoded[2];
|
|
27081
|
+
this.expiry = decoded[3];
|
|
27082
|
+
this.twapWindow = decoded[4];
|
|
27083
|
+
this.priceToSy = decoded[5];
|
|
27063
27084
|
}
|
|
27064
27085
|
};
|
|
27065
27086
|
|
|
@@ -27702,6 +27723,18 @@ var PriceOracleBaseContract = class extends BaseContract {
|
|
|
27702
27723
|
const toScale = 10n ** BigInt(this.sdk.tokensMeta.decimals(to));
|
|
27703
27724
|
return amount * fromPrice * toScale / (toPrice * fromScale);
|
|
27704
27725
|
}
|
|
27726
|
+
/**
|
|
27727
|
+
* Tries to convert amount of token to USD, using latest known prices
|
|
27728
|
+
* @param from
|
|
27729
|
+
* @param to
|
|
27730
|
+
* @param amount
|
|
27731
|
+
* @param reserve
|
|
27732
|
+
*/
|
|
27733
|
+
convertToUSD(from, amount, reserve = false) {
|
|
27734
|
+
const price = reserve ? this.reservePrices.mustGet(from) : this.mainPrices.mustGet(from);
|
|
27735
|
+
const scale = 10n ** BigInt(this.sdk.tokensMeta.decimals(from));
|
|
27736
|
+
return amount * price / scale;
|
|
27737
|
+
}
|
|
27705
27738
|
/**
|
|
27706
27739
|
* Loads new prices for this oracle from PriceFeedCompressor
|
|
27707
27740
|
* Does not update price feeds, only updates prices
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gearbox-protocol/sdk",
|
|
3
|
-
"version": "3.0.0-vfour.
|
|
3
|
+
"version": "3.0.0-vfour.122",
|
|
4
4
|
"description": "Gearbox SDK",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "./dist/cjs/sdk/index.cjs",
|
|
@@ -38,6 +38,7 @@
|
|
|
38
38
|
"build": "tsup",
|
|
39
39
|
"dev": "tsup --watch",
|
|
40
40
|
"example": "tsx --env-file .env scripts/example.ts | pino-pretty",
|
|
41
|
+
"accounts": "tsx --env-file .env scripts/accounts.ts | pino-pretty",
|
|
41
42
|
"test": "yarn -v",
|
|
42
43
|
"prepare": "husky",
|
|
43
44
|
"prettier": "prettier --write .",
|