@kamino-finance/klend-sdk 6.0.5-beta.2 → 6.0.5-beta.20
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/classes/action.d.ts +1 -1
- package/dist/classes/action.d.ts.map +1 -1
- package/dist/classes/action.js +32 -16
- package/dist/classes/action.js.map +1 -1
- package/dist/classes/manager.d.ts +29 -18
- package/dist/classes/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +66 -49
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts +12 -11
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +77 -37
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/vault.d.ts +5 -3
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +8 -6
- package/dist/classes/vault.js.map +1 -1
- package/dist/client_kamino_manager.d.ts.map +1 -1
- package/dist/client_kamino_manager.js +30 -22
- package/dist/client_kamino_manager.js.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/repay_with_collateral_operations.js +36 -32
- package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.d.ts.map +1 -1
- package/dist/lending_operations/swap_collateral_operations.js +4 -4
- package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
- package/dist/leverage/operations.d.ts +4 -3
- package/dist/leverage/operations.d.ts.map +1 -1
- package/dist/leverage/operations.js +186 -154
- package/dist/leverage/operations.js.map +1 -1
- package/dist/leverage/types.d.ts +1 -0
- package/dist/leverage/types.d.ts.map +1 -1
- package/dist/utils/managerTypes.d.ts +1 -2
- package/dist/utils/managerTypes.d.ts.map +1 -1
- package/dist/utils/managerTypes.js +9 -9
- package/dist/utils/managerTypes.js.map +1 -1
- package/dist/utils/obligations.d.ts +5 -0
- package/dist/utils/obligations.d.ts.map +1 -0
- package/dist/utils/obligations.js +53 -0
- package/dist/utils/obligations.js.map +1 -0
- package/dist/utils/oracle.d.ts +3 -3
- package/dist/utils/oracle.d.ts.map +1 -1
- package/dist/utils/oracle.js +2 -2
- package/dist/utils/oracle.js.map +1 -1
- package/dist/utils/pubkey.d.ts +1 -0
- package/dist/utils/pubkey.d.ts.map +1 -1
- package/dist/utils/pubkey.js +10 -0
- package/dist/utils/pubkey.js.map +1 -1
- package/package.json +3 -3
- package/src/classes/action.ts +32 -20
- package/src/classes/manager.ts +87 -53
- package/src/classes/market.ts +132 -52
- package/src/classes/vault.ts +17 -6
- package/src/client.ts +4 -4
- package/src/client_kamino_manager.ts +40 -35
- package/src/lending_operations/repay_with_collateral_operations.ts +76 -72
- package/src/lending_operations/swap_collateral_operations.ts +13 -11
- package/src/leverage/operations.ts +362 -328
- package/src/leverage/types.ts +1 -0
- package/src/utils/managerTypes.ts +1 -2
- package/src/utils/obligations.ts +69 -0
- package/src/utils/oracle.ts +5 -4
- package/src/utils/pubkey.ts +9 -0
package/src/classes/market.ts
CHANGED
|
@@ -22,6 +22,8 @@ import {
|
|
|
22
22
|
CandidatePrice,
|
|
23
23
|
PublicKeySet,
|
|
24
24
|
DEPOSITS_LIMIT,
|
|
25
|
+
setOrAppend,
|
|
26
|
+
AllOracleAccounts,
|
|
25
27
|
} from '../utils';
|
|
26
28
|
import base58 from 'bs58';
|
|
27
29
|
import { BN } from '@coral-xyz/anchor';
|
|
@@ -31,7 +33,7 @@ import { PROGRAM_ID } from '../idl_codegen/programId';
|
|
|
31
33
|
import bs58 from 'bs58';
|
|
32
34
|
import { OraclePrices, Scope, U16_MAX } from '@kamino-finance/scope-sdk';
|
|
33
35
|
import { Fraction } from './fraction';
|
|
34
|
-
import { chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
|
|
36
|
+
import { batchFetch, chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
|
|
35
37
|
import { parseTokenSymbol, parseZeroPaddedUtf8 } from './utils';
|
|
36
38
|
import SwitchboardProgram from '@switchboard-xyz/sbv2-lite';
|
|
37
39
|
import { ObligationZP } from '../idl_codegen/zero_padding';
|
|
@@ -62,9 +64,7 @@ export class KaminoMarket {
|
|
|
62
64
|
|
|
63
65
|
private readonly recentSlotDurationMs: number;
|
|
64
66
|
|
|
65
|
-
|
|
66
|
-
// value = reserve pubkey
|
|
67
|
-
private readonly reserveScopeFeeds: PubkeyHashMap<PublicKey, PublicKey>;
|
|
67
|
+
readonly scopeFeeds: PublicKeySet<PublicKey>;
|
|
68
68
|
|
|
69
69
|
private constructor(
|
|
70
70
|
connection: Connection,
|
|
@@ -74,6 +74,10 @@ export class KaminoMarket {
|
|
|
74
74
|
recentSlotDurationMs: number,
|
|
75
75
|
programId: PublicKey = PROGRAM_ID
|
|
76
76
|
) {
|
|
77
|
+
if (recentSlotDurationMs <= 0) {
|
|
78
|
+
throw new Error('Recent slot duration cannot be 0 or less');
|
|
79
|
+
}
|
|
80
|
+
|
|
77
81
|
this.address = marketAddress;
|
|
78
82
|
this.connection = connection;
|
|
79
83
|
this.state = state;
|
|
@@ -81,10 +85,10 @@ export class KaminoMarket {
|
|
|
81
85
|
this.reservesActive = getReservesActive(this.reserves);
|
|
82
86
|
this.programId = programId;
|
|
83
87
|
this.recentSlotDurationMs = recentSlotDurationMs;
|
|
84
|
-
this.
|
|
88
|
+
this.scopeFeeds = new PublicKeySet(
|
|
85
89
|
Array.from(this.reserves.values())
|
|
86
90
|
.filter((r) => isNotNullPubkey(r.state.config.tokenInfo.scopeConfiguration.priceFeed))
|
|
87
|
-
.map((r) =>
|
|
91
|
+
.map((r) => r.state.config.tokenInfo.scopeConfiguration.priceFeed)
|
|
88
92
|
);
|
|
89
93
|
}
|
|
90
94
|
|
|
@@ -95,7 +99,6 @@ export class KaminoMarket {
|
|
|
95
99
|
* @param recentSlotDurationMs
|
|
96
100
|
* @param programId
|
|
97
101
|
* @param withReserves
|
|
98
|
-
* @param setupLocalTest
|
|
99
102
|
* @param withReserves
|
|
100
103
|
*/
|
|
101
104
|
static async load(
|
|
@@ -111,10 +114,6 @@ export class KaminoMarket {
|
|
|
111
114
|
return null;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
|
-
if (recentSlotDurationMs <= 0) {
|
|
115
|
-
throw new Error('Recent slot duration cannot be 0');
|
|
116
|
-
}
|
|
117
|
-
|
|
118
117
|
const reserves = withReserves
|
|
119
118
|
? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs)
|
|
120
119
|
: new Map<PublicKey, KaminoReserve>();
|
|
@@ -133,6 +132,68 @@ export class KaminoMarket {
|
|
|
133
132
|
return new KaminoMarket(connection, market, marketAddress.toString(), reserves, recentSlotDurationMs, programId);
|
|
134
133
|
}
|
|
135
134
|
|
|
135
|
+
static async loadMultiple(
|
|
136
|
+
connection: Connection,
|
|
137
|
+
markets: PublicKey[],
|
|
138
|
+
recentSlotDurationMs: number,
|
|
139
|
+
programId: PublicKey = PROGRAM_ID,
|
|
140
|
+
withReserves: boolean = true,
|
|
141
|
+
oracleAccounts?: AllOracleAccounts
|
|
142
|
+
) {
|
|
143
|
+
const marketStates = await batchFetch(markets, (market) =>
|
|
144
|
+
LendingMarket.fetchMultiple(connection, market, programId)
|
|
145
|
+
);
|
|
146
|
+
const kaminoMarkets = new PubkeyHashMap<PublicKey, KaminoMarket>();
|
|
147
|
+
for (let i = 0; i < markets.length; i++) {
|
|
148
|
+
const market = marketStates[i];
|
|
149
|
+
const marketAddress = markets[i];
|
|
150
|
+
if (market === null) {
|
|
151
|
+
throw Error(`Could not fetch LendingMarket account state for market ${marketAddress}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const marketReserves = withReserves
|
|
155
|
+
? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs, oracleAccounts)
|
|
156
|
+
: new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
157
|
+
|
|
158
|
+
kaminoMarkets.set(
|
|
159
|
+
marketAddress,
|
|
160
|
+
new KaminoMarket(connection, market, marketAddress.toString(), marketReserves, recentSlotDurationMs, programId)
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
return kaminoMarkets;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
static async loadMultipleWithReserves(
|
|
167
|
+
connection: Connection,
|
|
168
|
+
markets: PublicKey[],
|
|
169
|
+
reserves: PubkeyHashMap<PublicKey, PubkeyHashMap<PublicKey, KaminoReserve>>,
|
|
170
|
+
recentSlotDurationMs: number,
|
|
171
|
+
programId: PublicKey = PROGRAM_ID
|
|
172
|
+
) {
|
|
173
|
+
const marketStates = await batchFetch(markets, (market) =>
|
|
174
|
+
LendingMarket.fetchMultiple(connection, market, programId)
|
|
175
|
+
);
|
|
176
|
+
const kaminoMarkets = new PubkeyHashMap<PublicKey, KaminoMarket>();
|
|
177
|
+
for (let i = 0; i < markets.length; i++) {
|
|
178
|
+
const market = marketStates[i];
|
|
179
|
+
const marketAddress = markets[i];
|
|
180
|
+
if (market === null) {
|
|
181
|
+
throw Error(`Could not fetch LendingMarket account state for market ${marketAddress}`);
|
|
182
|
+
}
|
|
183
|
+
const marketReserves = reserves.get(marketAddress);
|
|
184
|
+
if (!marketReserves) {
|
|
185
|
+
throw Error(
|
|
186
|
+
`Could not get reserves for market ${marketAddress} from the reserves map argument supplied to this method`
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
kaminoMarkets.set(
|
|
190
|
+
marketAddress,
|
|
191
|
+
new KaminoMarket(connection, market, marketAddress.toString(), marketReserves, recentSlotDurationMs, programId)
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
return kaminoMarkets;
|
|
195
|
+
}
|
|
196
|
+
|
|
136
197
|
async reload(): Promise<void> {
|
|
137
198
|
const market = await LendingMarket.fetch(this.connection, this.getAddress(), this.programId);
|
|
138
199
|
if (market === null) {
|
|
@@ -373,19 +434,19 @@ export class KaminoMarket {
|
|
|
373
434
|
/**
|
|
374
435
|
* @returns The max borrowable amount for leverage positions
|
|
375
436
|
*/
|
|
376
|
-
|
|
437
|
+
getMaxLeverageBorrowableAmount(
|
|
377
438
|
collReserve: KaminoReserve,
|
|
378
439
|
debtReserve: KaminoReserve,
|
|
379
440
|
slot: number,
|
|
380
441
|
requestElevationGroup: boolean,
|
|
381
442
|
obligation?: KaminoObligation
|
|
382
|
-
):
|
|
443
|
+
): Decimal {
|
|
383
444
|
return obligation
|
|
384
445
|
? obligation.getMaxBorrowAmount(this, debtReserve.getLiquidityMint(), slot, requestElevationGroup)
|
|
385
446
|
: debtReserve.getMaxBorrowAmountWithCollReserve(this, collReserve, slot);
|
|
386
447
|
}
|
|
387
448
|
|
|
388
|
-
async loadReserves() {
|
|
449
|
+
async loadReserves(oracleAccounts?: AllOracleAccounts) {
|
|
389
450
|
const addresses = [...this.reserves.keys()];
|
|
390
451
|
const reserveAccounts = await this.connection.getMultipleAccountsInfo(addresses, 'processed');
|
|
391
452
|
const deserializedReserves = reserveAccounts.map((reserve, i) => {
|
|
@@ -399,7 +460,7 @@ export class KaminoMarket {
|
|
|
399
460
|
}
|
|
400
461
|
return reserveAccount;
|
|
401
462
|
});
|
|
402
|
-
const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves);
|
|
463
|
+
const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves, oracleAccounts);
|
|
403
464
|
const kaminoReserves = new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
404
465
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
405
466
|
if (!oracle) {
|
|
@@ -851,9 +912,13 @@ export class KaminoMarket {
|
|
|
851
912
|
return finalObligations;
|
|
852
913
|
}
|
|
853
914
|
|
|
854
|
-
async getAllUserObligations(
|
|
915
|
+
async getAllUserObligations(
|
|
916
|
+
user: PublicKey,
|
|
917
|
+
commitment = this.connection.commitment,
|
|
918
|
+
slot?: number
|
|
919
|
+
): Promise<KaminoObligation[]> {
|
|
855
920
|
const [currentSlot, obligations] = await Promise.all([
|
|
856
|
-
this.connection.getSlot(),
|
|
921
|
+
slot !== undefined ? Promise.resolve(slot) : this.connection.getSlot(),
|
|
857
922
|
this.connection.getProgramAccounts(this.programId, {
|
|
858
923
|
filters: [
|
|
859
924
|
{
|
|
@@ -1211,12 +1276,16 @@ export class KaminoMarket {
|
|
|
1211
1276
|
*/
|
|
1212
1277
|
async getReserveOraclePrices(scope: Scope): Promise<PubkeyHashMap<PublicKey, OraclePrices>> {
|
|
1213
1278
|
const reserveOraclePrices: PubkeyHashMap<PublicKey, OraclePrices> = new PubkeyHashMap();
|
|
1214
|
-
const oraclePrices = await scope.getMultipleOraclePrices(
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1279
|
+
const oraclePrices = await scope.getMultipleOraclePrices(this.scopeFeeds.toArray());
|
|
1280
|
+
const oraclePriceMap = new PubkeyHashMap<PublicKey, OraclePrices>();
|
|
1281
|
+
for (const [feed, account] of oraclePrices) {
|
|
1282
|
+
oraclePriceMap.set(feed, account);
|
|
1283
|
+
}
|
|
1284
|
+
for (const [reserveAddress, reserve] of this.reserves) {
|
|
1285
|
+
reserveOraclePrices.set(
|
|
1286
|
+
reserveAddress,
|
|
1287
|
+
oraclePriceMap.get(reserve.state.config.tokenInfo.scopeConfiguration.priceFeed)!
|
|
1288
|
+
);
|
|
1220
1289
|
}
|
|
1221
1290
|
return reserveOraclePrices;
|
|
1222
1291
|
}
|
|
@@ -1224,8 +1293,10 @@ export class KaminoMarket {
|
|
|
1224
1293
|
/**
|
|
1225
1294
|
* Get all Scope prices used by all the market reserves
|
|
1226
1295
|
*/
|
|
1227
|
-
async getAllScopePrices(
|
|
1228
|
-
|
|
1296
|
+
async getAllScopePrices(
|
|
1297
|
+
scope: Scope,
|
|
1298
|
+
allOraclePrices: PubkeyHashMap<PublicKey, OraclePrices>
|
|
1299
|
+
): Promise<KaminoPrices> {
|
|
1229
1300
|
const spot: MintToPriceMap = {};
|
|
1230
1301
|
const twaps: MintToPriceMap = {};
|
|
1231
1302
|
for (const reserve of this.reserves.values()) {
|
|
@@ -1234,7 +1305,7 @@ export class KaminoMarket {
|
|
|
1234
1305
|
const oracle = reserve.state.config.tokenInfo.scopeConfiguration.priceFeed;
|
|
1235
1306
|
const chain = reserve.state.config.tokenInfo.scopeConfiguration.priceChain;
|
|
1236
1307
|
const twapChain = reserve.state.config.tokenInfo.scopeConfiguration.twapChain.filter((x) => x > 0);
|
|
1237
|
-
const oraclePrices = allOraclePrices.get(
|
|
1308
|
+
const oraclePrices = allOraclePrices.get(oracle);
|
|
1238
1309
|
if (oraclePrices && oracle && isNotNullPubkey(oracle) && chain && Scope.isScopeChainValid(chain)) {
|
|
1239
1310
|
const spotPrice = await scope.getPriceFromChain(chain, oraclePrices);
|
|
1240
1311
|
spot[tokenMint] = { price: spotPrice.price, name: tokenName };
|
|
@@ -1250,16 +1321,18 @@ export class KaminoMarket {
|
|
|
1250
1321
|
/**
|
|
1251
1322
|
* Get all Scope/Pyth/Switchboard prices used by all the market reserves
|
|
1252
1323
|
*/
|
|
1253
|
-
async getAllPrices(): Promise<KlendPrices> {
|
|
1324
|
+
async getAllPrices(oracleAccounts?: AllOracleAccounts): Promise<KlendPrices> {
|
|
1254
1325
|
const klendPrices: KlendPrices = {
|
|
1255
1326
|
scope: { spot: {}, twap: {} },
|
|
1256
1327
|
pyth: { spot: {}, twap: {} },
|
|
1257
1328
|
switchboard: { spot: {}, twap: {} },
|
|
1258
1329
|
};
|
|
1259
|
-
const allOracleAccounts =
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1330
|
+
const allOracleAccounts =
|
|
1331
|
+
oracleAccounts ??
|
|
1332
|
+
(await getAllOracleAccounts(
|
|
1333
|
+
this.connection,
|
|
1334
|
+
this.getReserves().map((x) => x.state)
|
|
1335
|
+
));
|
|
1263
1336
|
const pythCache = new PubkeyHashMap<PublicKey, PythPrices>();
|
|
1264
1337
|
const switchboardCache = new PubkeyHashMap<PublicKey, CandidatePrice>();
|
|
1265
1338
|
const scopeCache = new PubkeyHashMap<PublicKey, OraclePrices>();
|
|
@@ -1471,7 +1544,8 @@ export async function getReservesForMarket(
|
|
|
1471
1544
|
marketAddress: PublicKey,
|
|
1472
1545
|
connection: Connection,
|
|
1473
1546
|
programId: PublicKey,
|
|
1474
|
-
recentSlotDurationMs: number
|
|
1547
|
+
recentSlotDurationMs: number,
|
|
1548
|
+
oracleAccounts?: AllOracleAccounts
|
|
1475
1549
|
): Promise<Map<PublicKey, KaminoReserve>> {
|
|
1476
1550
|
const reserves = await connection.getProgramAccounts(programId, {
|
|
1477
1551
|
filters: [
|
|
@@ -1499,7 +1573,7 @@ export async function getReservesForMarket(
|
|
|
1499
1573
|
return reserveAccount;
|
|
1500
1574
|
});
|
|
1501
1575
|
const allBuffers = reserves.map((reserve) => reserve.account);
|
|
1502
|
-
const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves);
|
|
1576
|
+
const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves, oracleAccounts);
|
|
1503
1577
|
const reservesByAddress = new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
1504
1578
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
1505
1579
|
if (!oracle) {
|
|
@@ -1522,7 +1596,8 @@ export async function getSingleReserve(
|
|
|
1522
1596
|
reservePk: PublicKey,
|
|
1523
1597
|
connection: Connection,
|
|
1524
1598
|
recentSlotDurationMs: number,
|
|
1525
|
-
accountData?: AccountInfo<Buffer
|
|
1599
|
+
accountData?: AccountInfo<Buffer>,
|
|
1600
|
+
oracleAccounts?: AllOracleAccounts
|
|
1526
1601
|
): Promise<KaminoReserve> {
|
|
1527
1602
|
const reserve = accountData ? accountData : await connection.getAccountInfo(reservePk);
|
|
1528
1603
|
|
|
@@ -1536,22 +1611,13 @@ export async function getSingleReserve(
|
|
|
1536
1611
|
throw Error(`Could not parse reserve ${reservePk.toBase58()}`);
|
|
1537
1612
|
}
|
|
1538
1613
|
|
|
1539
|
-
const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount]);
|
|
1614
|
+
const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount], oracleAccounts);
|
|
1540
1615
|
const [reserveState, oracle] = reservesAndOracles[0];
|
|
1541
1616
|
|
|
1542
1617
|
if (!oracle) {
|
|
1543
1618
|
throw Error(`Could not find oracle for ${parseTokenSymbol(reserveState.config.tokenInfo.name)} reserve`);
|
|
1544
1619
|
}
|
|
1545
|
-
|
|
1546
|
-
reserve,
|
|
1547
|
-
reservePk,
|
|
1548
|
-
reserveState,
|
|
1549
|
-
oracle,
|
|
1550
|
-
connection,
|
|
1551
|
-
recentSlotDurationMs
|
|
1552
|
-
);
|
|
1553
|
-
|
|
1554
|
-
return kaminoReserve;
|
|
1620
|
+
return KaminoReserve.initialize(reserve, reservePk, reserveState, oracle, connection, recentSlotDurationMs);
|
|
1555
1621
|
}
|
|
1556
1622
|
|
|
1557
1623
|
export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<PublicKey, KaminoReserve> {
|
|
@@ -1564,8 +1630,11 @@ export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<
|
|
|
1564
1630
|
return reservesActive;
|
|
1565
1631
|
}
|
|
1566
1632
|
|
|
1567
|
-
export function getTokenIdsForScopeRefresh(
|
|
1568
|
-
|
|
1633
|
+
export function getTokenIdsForScopeRefresh(
|
|
1634
|
+
kaminoMarket: KaminoMarket,
|
|
1635
|
+
reserves: PublicKey[]
|
|
1636
|
+
): PubkeyHashMap<PublicKey, number[]> {
|
|
1637
|
+
const tokenIds = new PubkeyHashMap<PublicKey, number[]>();
|
|
1569
1638
|
|
|
1570
1639
|
for (const reserveAddress of reserves) {
|
|
1571
1640
|
const reserve = kaminoMarket.getReserveByAddress(reserveAddress);
|
|
@@ -1573,22 +1642,33 @@ export function getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves:
|
|
|
1573
1642
|
throw new Error(`Reserve not found for reserve ${reserveAddress.toBase58()}`);
|
|
1574
1643
|
}
|
|
1575
1644
|
|
|
1576
|
-
|
|
1645
|
+
const { scopeConfiguration } = reserve.state.config.tokenInfo;
|
|
1646
|
+
if (!scopeConfiguration.priceFeed.equals(PublicKey.default)) {
|
|
1577
1647
|
let x = 0;
|
|
1578
1648
|
|
|
1579
|
-
while (
|
|
1580
|
-
tokenIds.
|
|
1649
|
+
while (scopeConfiguration.priceChain[x] !== U16_MAX) {
|
|
1650
|
+
setOrAppend(tokenIds, scopeConfiguration.priceFeed, scopeConfiguration.priceChain[x]);
|
|
1581
1651
|
x++;
|
|
1582
1652
|
}
|
|
1583
1653
|
|
|
1584
1654
|
x = 0;
|
|
1585
|
-
while (
|
|
1586
|
-
tokenIds.
|
|
1655
|
+
while (scopeConfiguration.twapChain[x] !== U16_MAX) {
|
|
1656
|
+
setOrAppend(tokenIds, scopeConfiguration.priceFeed, scopeConfiguration.twapChain[x]);
|
|
1587
1657
|
x++;
|
|
1588
1658
|
}
|
|
1589
1659
|
}
|
|
1590
1660
|
}
|
|
1591
1661
|
|
|
1662
|
+
//TODO: remove code below
|
|
1663
|
+
// - currently Scope program does not allow multiple refreshPricesList instructions in 1 ix
|
|
1664
|
+
// - temporary fix is to only refresh one scope feed at this time
|
|
1665
|
+
const firstFeed = tokenIds.entries().next();
|
|
1666
|
+
tokenIds.clear();
|
|
1667
|
+
if (!firstFeed.done) {
|
|
1668
|
+
const [key, value] = firstFeed.value;
|
|
1669
|
+
tokenIds.set(key, value);
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1592
1672
|
return tokenIds;
|
|
1593
1673
|
}
|
|
1594
1674
|
|
package/src/classes/vault.ts
CHANGED
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
PubkeyHashMap,
|
|
29
29
|
Reserve,
|
|
30
30
|
UserState,
|
|
31
|
+
AllOracleAccounts,
|
|
31
32
|
} from '../lib';
|
|
32
33
|
import {
|
|
33
34
|
DepositAccounts,
|
|
@@ -838,7 +839,7 @@ export class KaminoVaultClient {
|
|
|
838
839
|
const [{ ata: adminTokenAta, createAtaIx }] = createAtasIdempotent(vaultState.vaultAdminAuthority, [
|
|
839
840
|
{
|
|
840
841
|
mint: vaultState.tokenMint,
|
|
841
|
-
tokenProgram:
|
|
842
|
+
tokenProgram: vaultState.tokenProgram,
|
|
842
843
|
},
|
|
843
844
|
]);
|
|
844
845
|
|
|
@@ -1635,7 +1636,7 @@ export class KaminoVaultClient {
|
|
|
1635
1636
|
baseVaultAuthority: vaultState.baseVaultAuthority,
|
|
1636
1637
|
tokenAta: adminTokenAta,
|
|
1637
1638
|
tokenMint: vaultState.tokenMint,
|
|
1638
|
-
tokenProgram:
|
|
1639
|
+
tokenProgram: vaultState.tokenProgram,
|
|
1639
1640
|
/** CPI accounts */
|
|
1640
1641
|
lendingMarket: marketAddress,
|
|
1641
1642
|
lendingMarketAuthority: lendingMarketAuth,
|
|
@@ -2324,9 +2325,13 @@ export class KaminoVaultClient {
|
|
|
2324
2325
|
/**
|
|
2325
2326
|
* This will load the onchain state for all the reserves that the vaults have allocations for, deduplicating the reserves
|
|
2326
2327
|
* @param vaults - the vault states to load reserves for
|
|
2328
|
+
* @param oracleAccounts
|
|
2327
2329
|
* @returns a hashmap from each reserve pubkey to the reserve state
|
|
2328
2330
|
*/
|
|
2329
|
-
async loadVaultsReserves(
|
|
2331
|
+
async loadVaultsReserves(
|
|
2332
|
+
vaults: VaultState[],
|
|
2333
|
+
oracleAccounts?: AllOracleAccounts
|
|
2334
|
+
): Promise<PubkeyHashMap<PublicKey, KaminoReserve>> {
|
|
2330
2335
|
const vaultReservesAddressesSet = new PublicKeySet(vaults.flatMap((vault) => this.getVaultReserves(vault)));
|
|
2331
2336
|
const vaultReservesAddresses = vaultReservesAddressesSet.toArray();
|
|
2332
2337
|
const reserveAccounts = await this.getConnection().getMultipleAccountsInfo(vaultReservesAddresses, 'processed');
|
|
@@ -2343,7 +2348,7 @@ export class KaminoVaultClient {
|
|
|
2343
2348
|
return reserveAccount;
|
|
2344
2349
|
});
|
|
2345
2350
|
|
|
2346
|
-
const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves);
|
|
2351
|
+
const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts);
|
|
2347
2352
|
|
|
2348
2353
|
const kaminoReserves = new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
2349
2354
|
|
|
@@ -2371,13 +2376,15 @@ export class KaminoVaultClient {
|
|
|
2371
2376
|
* @param [slot] - the slot for which to retrieve the vault collaterals for. Optional. If not provided the function will fetch the current slot
|
|
2372
2377
|
* @param [vaultReservesMap] - hashmap from each reserve pubkey to the reserve state. Optional. If provided the function will be significantly faster as it will not have to fetch the reserves
|
|
2373
2378
|
* @param [kaminoMarkets] - a list of all the kamino markets. Optional. If provided the function will be significantly faster as it will not have to fetch the markets
|
|
2379
|
+
* @param oracleAccounts
|
|
2374
2380
|
* @returns a hashmap from each reserve pubkey to the market overview of the collaterals that can be used and the min and max loan to value ratio in that market
|
|
2375
2381
|
*/
|
|
2376
2382
|
async getVaultCollaterals(
|
|
2377
2383
|
vaultState: VaultState,
|
|
2378
2384
|
slot: number,
|
|
2379
2385
|
vaultReservesMap?: PubkeyHashMap<PublicKey, KaminoReserve>,
|
|
2380
|
-
kaminoMarkets?: KaminoMarket[]
|
|
2386
|
+
kaminoMarkets?: KaminoMarket[],
|
|
2387
|
+
oracleAccounts?: AllOracleAccounts
|
|
2381
2388
|
): Promise<PubkeyHashMap<PublicKey, MarketOverview>> {
|
|
2382
2389
|
const vaultReservesStateMap = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
|
|
2383
2390
|
const vaultReservesState: KaminoReserve[] = [];
|
|
@@ -2401,7 +2408,11 @@ export class KaminoVaultClient {
|
|
|
2401
2408
|
const missingReservesStates = (await Reserve.fetchMultiple(this.getConnection(), missingReserves.toArray())).filter(
|
|
2402
2409
|
(reserve) => reserve !== null
|
|
2403
2410
|
);
|
|
2404
|
-
const missingReservesAndOracles = await getTokenOracleData(
|
|
2411
|
+
const missingReservesAndOracles = await getTokenOracleData(
|
|
2412
|
+
this.getConnection(),
|
|
2413
|
+
missingReservesStates,
|
|
2414
|
+
oracleAccounts
|
|
2415
|
+
);
|
|
2405
2416
|
missingReservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
2406
2417
|
const fetchedReserve = new KaminoReserve(
|
|
2407
2418
|
reserve,
|
package/src/client.ts
CHANGED
|
@@ -362,7 +362,7 @@ async function deposit(connection: Connection, wallet: Keypair, token: string, d
|
|
|
362
362
|
wallet.publicKey,
|
|
363
363
|
new VanillaObligation(STAGING_LENDING_MARKET),
|
|
364
364
|
true,
|
|
365
|
-
{ scope: new Scope('mainnet-beta', connection),
|
|
365
|
+
{ scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
|
|
366
366
|
);
|
|
367
367
|
console.log('User obligation', kaminoAction.getObligationPda().toString());
|
|
368
368
|
|
|
@@ -384,7 +384,7 @@ async function withdraw(connection: Connection, wallet: Keypair, token: string,
|
|
|
384
384
|
wallet.publicKey,
|
|
385
385
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
386
386
|
true,
|
|
387
|
-
{ scope: new Scope('mainnet-beta', connection),
|
|
387
|
+
{ scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
|
|
388
388
|
);
|
|
389
389
|
console.log('User obligation', kaminoAction.getObligationPda().toString());
|
|
390
390
|
|
|
@@ -406,7 +406,7 @@ async function borrow(connection: Connection, wallet: Keypair, token: string, bo
|
|
|
406
406
|
wallet.publicKey,
|
|
407
407
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
408
408
|
true,
|
|
409
|
-
{ scope: new Scope('mainnet-beta', connection),
|
|
409
|
+
{ scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] }
|
|
410
410
|
);
|
|
411
411
|
console.log('User obligation', kaminoAction.getObligationPda().toString());
|
|
412
412
|
|
|
@@ -428,7 +428,7 @@ async function repay(connection: Connection, wallet: Keypair, token: string, bor
|
|
|
428
428
|
wallet.publicKey,
|
|
429
429
|
new VanillaObligation(new PublicKey(STAGING_LENDING_MARKET)),
|
|
430
430
|
true,
|
|
431
|
-
{ scope: new Scope('mainnet-beta', connection),
|
|
431
|
+
{ scope: new Scope('mainnet-beta', connection), scopeConfigurations: [] },
|
|
432
432
|
await connection.getSlot()
|
|
433
433
|
);
|
|
434
434
|
console.log('User obligation', kaminoAction.getObligationPda().toString());
|
|
@@ -52,18 +52,18 @@ import {
|
|
|
52
52
|
TokenInfo,
|
|
53
53
|
WithdrawalCaps,
|
|
54
54
|
} from './idl_codegen/types';
|
|
55
|
-
import { Fraction } from './classes
|
|
55
|
+
import { Fraction } from './classes';
|
|
56
56
|
import Decimal from 'decimal.js';
|
|
57
57
|
import { BN } from '@coral-xyz/anchor';
|
|
58
58
|
import { PythConfiguration, SwitchboardConfiguration } from './idl_codegen_kamino_vault/types';
|
|
59
59
|
import * as fs from 'fs';
|
|
60
|
-
import { MarketWithAddress } from './utils
|
|
60
|
+
import { MarketWithAddress } from './utils';
|
|
61
61
|
import {
|
|
62
62
|
ManagementFeeBps,
|
|
63
63
|
PendingVaultAdmin,
|
|
64
64
|
PerformanceFeeBps,
|
|
65
65
|
} from './idl_codegen_kamino_vault/types/VaultConfigField';
|
|
66
|
-
import { getAccountOwner } from './utils
|
|
66
|
+
import { getAccountOwner } from './utils';
|
|
67
67
|
import { getAssociatedTokenAddressSync } from '@solana/spl-token';
|
|
68
68
|
|
|
69
69
|
dotenv.config({
|
|
@@ -157,12 +157,12 @@ async function main() {
|
|
|
157
157
|
|
|
158
158
|
console.log('reserve: ', reserve.publicKey);
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
await processTxn(env.client, env.payer, txnIxs[0], mode, 2500, [reserve]);
|
|
161
161
|
|
|
162
162
|
const [lut, createLutIxs] = await createUpdateReserveConfigLutIxs(env, marketAddress, reserve.publicKey);
|
|
163
163
|
await processTxn(env.client, env.payer, createLutIxs, mode, 2500, []);
|
|
164
164
|
|
|
165
|
-
|
|
165
|
+
await processTxn(env.client, env.payer, txnIxs[1], mode, 2500, [], 400_000, 1000, [lut]);
|
|
166
166
|
|
|
167
167
|
mode === 'execute' &&
|
|
168
168
|
console.log(
|
|
@@ -224,7 +224,7 @@ async function main() {
|
|
|
224
224
|
updateEntireConfig
|
|
225
225
|
);
|
|
226
226
|
|
|
227
|
-
|
|
227
|
+
await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
|
|
228
228
|
|
|
229
229
|
mode === 'execute' && console.log('Reserve Updated with config -> ', JSON.parse(JSON.stringify(reserveConfig)));
|
|
230
230
|
});
|
|
@@ -296,7 +296,7 @@ async function main() {
|
|
|
296
296
|
|
|
297
297
|
const { vault: vaultKp, initVaultIxs: instructions } = await kaminoManager.createVaultIxs(kaminoVaultConfig);
|
|
298
298
|
|
|
299
|
-
|
|
299
|
+
await processTxn(
|
|
300
300
|
env.client,
|
|
301
301
|
env.payer,
|
|
302
302
|
[...instructions.createAtaIfNeededIxs, ...instructions.initVaultIxs, instructions.createLUTIx],
|
|
@@ -305,7 +305,7 @@ async function main() {
|
|
|
305
305
|
[vaultKp]
|
|
306
306
|
);
|
|
307
307
|
await sleep(2000);
|
|
308
|
-
|
|
308
|
+
await processTxn(
|
|
309
309
|
env.client,
|
|
310
310
|
env.payer,
|
|
311
311
|
[...instructions.populateLUTIxs, ...instructions.cleanupIxs],
|
|
@@ -314,14 +314,7 @@ async function main() {
|
|
|
314
314
|
[]
|
|
315
315
|
);
|
|
316
316
|
|
|
317
|
-
|
|
318
|
-
env.client,
|
|
319
|
-
env.payer,
|
|
320
|
-
[instructions.initSharesMetadataIx],
|
|
321
|
-
mode,
|
|
322
|
-
2500,
|
|
323
|
-
[]
|
|
324
|
-
);
|
|
317
|
+
await processTxn(env.client, env.payer, [instructions.initSharesMetadataIx], mode, 2500, []);
|
|
325
318
|
mode === 'execute' && console.log('Vault created:', vaultKp.publicKey.toBase58());
|
|
326
319
|
});
|
|
327
320
|
|
|
@@ -1173,19 +1166,33 @@ async function main() {
|
|
|
1173
1166
|
}
|
|
1174
1167
|
});
|
|
1175
1168
|
|
|
1176
|
-
commands
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1169
|
+
commands
|
|
1170
|
+
.command('get-oracle-mappings')
|
|
1171
|
+
.requiredOption('--lending-market <string>', 'Lending Market Address')
|
|
1172
|
+
.action(async ({ lendingMarket }) => {
|
|
1173
|
+
const env = initializeClient(false, false);
|
|
1174
|
+
const kaminoManager = new KaminoManager(
|
|
1175
|
+
env.connection,
|
|
1176
|
+
DEFAULT_RECENT_SLOT_DURATION_MS,
|
|
1177
|
+
env.kLendProgramId,
|
|
1178
|
+
env.kVaultProgramId
|
|
1179
|
+
);
|
|
1180
|
+
const market = await KaminoMarket.load(
|
|
1181
|
+
env.connection,
|
|
1182
|
+
new PublicKey(lendingMarket),
|
|
1183
|
+
DEFAULT_RECENT_SLOT_DURATION_MS,
|
|
1184
|
+
env.kLendProgramId
|
|
1185
|
+
);
|
|
1186
|
+
if (!market) {
|
|
1187
|
+
throw Error(`Lending market ${lendingMarket} not found`);
|
|
1188
|
+
}
|
|
1184
1189
|
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1190
|
+
console.log('Getting oracle mappings');
|
|
1191
|
+
const oracleConfigs = await kaminoManager.getScopeOracleConfigs(market);
|
|
1192
|
+
for (const [oraclePrices, configs] of oracleConfigs.entries()) {
|
|
1193
|
+
console.log(`OraclePrices pubkey: ${oraclePrices.toString()}`, 'Configs:', JSON.parse(JSON.stringify(configs)));
|
|
1194
|
+
}
|
|
1195
|
+
});
|
|
1189
1196
|
|
|
1190
1197
|
commands.command('get-all-vaults').action(async () => {
|
|
1191
1198
|
const env = initializeClient(false, false);
|
|
@@ -1370,7 +1377,7 @@ async function main() {
|
|
|
1370
1377
|
// executing 6 ixs in a txn to make sure they fit
|
|
1371
1378
|
for (let ixnIndex = 0; ixnIndex < ixs.length; ixnIndex += 6) {
|
|
1372
1379
|
const ixnToExecute = ixs.slice(ixnIndex, ixnIndex + 6);
|
|
1373
|
-
|
|
1380
|
+
await processTxn(env.client, env.payer, ixnToExecute, mode, 2500, [], 400_000);
|
|
1374
1381
|
}
|
|
1375
1382
|
|
|
1376
1383
|
mode === 'execute' &&
|
|
@@ -1410,7 +1417,7 @@ async function main() {
|
|
|
1410
1417
|
|
|
1411
1418
|
const ix = kaminoManager.updateLendingMarketOwnerIxs(marketWithAddress);
|
|
1412
1419
|
|
|
1413
|
-
|
|
1420
|
+
await processTxn(env.client, env.payer, [ix], mode, 2500, [], 400_000);
|
|
1414
1421
|
|
|
1415
1422
|
mode === 'execute' &&
|
|
1416
1423
|
console.log(
|
|
@@ -1465,7 +1472,7 @@ async function main() {
|
|
|
1465
1472
|
|
|
1466
1473
|
const ixs = kaminoManager.updateLendingMarketIxs(marketWithAddress, newLendingMarket);
|
|
1467
1474
|
|
|
1468
|
-
|
|
1475
|
+
await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
|
|
1469
1476
|
|
|
1470
1477
|
mode === 'execute' &&
|
|
1471
1478
|
console.log(
|
|
@@ -1520,7 +1527,7 @@ async function main() {
|
|
|
1520
1527
|
|
|
1521
1528
|
const ixs = await kaminoManager.updateReserveIxs(marketWithAddress, reserveAddress, newReserveConfig);
|
|
1522
1529
|
|
|
1523
|
-
|
|
1530
|
+
await processTxn(env.client, env.payer, ixs, mode, 2500, [], 400_000);
|
|
1524
1531
|
|
|
1525
1532
|
mode === 'execute' &&
|
|
1526
1533
|
console.log(
|
|
@@ -1811,9 +1818,7 @@ function parseBorrowRateCurve(reserveConfigFromFile: any): BorrowRateCurve {
|
|
|
1811
1818
|
|
|
1812
1819
|
curvePoints.forEach((curvePoint, index) => (finalCurvePoints[index] = curvePoint));
|
|
1813
1820
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
return borrowRateCurve;
|
|
1821
|
+
return new BorrowRateCurve({ points: finalCurvePoints });
|
|
1817
1822
|
}
|
|
1818
1823
|
|
|
1819
1824
|
function parseReserveBorrowLimitAgainstCollInEmode(reserveConfigFromFile: any): BN[] {
|