@kamino-finance/klend-sdk 6.0.5-beta.10 → 6.0.5-beta.11
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/market.d.ts +9 -7
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +49 -16
- 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 +6 -4
- package/dist/classes/vault.js.map +1 -1
- package/dist/utils/oracle.d.ts +1 -1
- 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/package.json +1 -1
- package/src/classes/market.ts +94 -21
- package/src/classes/vault.ts +15 -4
- package/src/utils/oracle.ts +3 -2
package/src/classes/market.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
PublicKeySet,
|
|
24
24
|
DEPOSITS_LIMIT,
|
|
25
25
|
setOrAppend,
|
|
26
|
+
AllOracleAccounts,
|
|
26
27
|
} from '../utils';
|
|
27
28
|
import base58 from 'bs58';
|
|
28
29
|
import { BN } from '@coral-xyz/anchor';
|
|
@@ -32,7 +33,7 @@ import { PROGRAM_ID } from '../idl_codegen/programId';
|
|
|
32
33
|
import bs58 from 'bs58';
|
|
33
34
|
import { OraclePrices, Scope, U16_MAX } from '@kamino-finance/scope-sdk';
|
|
34
35
|
import { Fraction } from './fraction';
|
|
35
|
-
import { chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
|
|
36
|
+
import { batchFetch, chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
|
|
36
37
|
import { parseTokenSymbol, parseZeroPaddedUtf8 } from './utils';
|
|
37
38
|
import SwitchboardProgram from '@switchboard-xyz/sbv2-lite';
|
|
38
39
|
import { ObligationZP } from '../idl_codegen/zero_padding';
|
|
@@ -73,6 +74,10 @@ export class KaminoMarket {
|
|
|
73
74
|
recentSlotDurationMs: number,
|
|
74
75
|
programId: PublicKey = PROGRAM_ID
|
|
75
76
|
) {
|
|
77
|
+
if (recentSlotDurationMs <= 0) {
|
|
78
|
+
throw new Error('Recent slot duration cannot be 0 or less');
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
this.address = marketAddress;
|
|
77
82
|
this.connection = connection;
|
|
78
83
|
this.state = state;
|
|
@@ -109,10 +114,6 @@ export class KaminoMarket {
|
|
|
109
114
|
return null;
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
if (recentSlotDurationMs <= 0) {
|
|
113
|
-
throw new Error('Recent slot duration cannot be 0');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
117
|
const reserves = withReserves
|
|
117
118
|
? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs)
|
|
118
119
|
: new Map<PublicKey, KaminoReserve>();
|
|
@@ -131,6 +132,68 @@ export class KaminoMarket {
|
|
|
131
132
|
return new KaminoMarket(connection, market, marketAddress.toString(), reserves, recentSlotDurationMs, programId);
|
|
132
133
|
}
|
|
133
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
|
+
|
|
134
197
|
async reload(): Promise<void> {
|
|
135
198
|
const market = await LendingMarket.fetch(this.connection, this.getAddress(), this.programId);
|
|
136
199
|
if (market === null) {
|
|
@@ -383,7 +446,7 @@ export class KaminoMarket {
|
|
|
383
446
|
: debtReserve.getMaxBorrowAmountWithCollReserve(this, collReserve, slot);
|
|
384
447
|
}
|
|
385
448
|
|
|
386
|
-
async loadReserves() {
|
|
449
|
+
async loadReserves(oracleAccounts?: AllOracleAccounts) {
|
|
387
450
|
const addresses = [...this.reserves.keys()];
|
|
388
451
|
const reserveAccounts = await this.connection.getMultipleAccountsInfo(addresses, 'processed');
|
|
389
452
|
const deserializedReserves = reserveAccounts.map((reserve, i) => {
|
|
@@ -397,7 +460,7 @@ export class KaminoMarket {
|
|
|
397
460
|
}
|
|
398
461
|
return reserveAccount;
|
|
399
462
|
});
|
|
400
|
-
const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves);
|
|
463
|
+
const reservesAndOracles = await getTokenOracleData(this.connection, deserializedReserves, oracleAccounts);
|
|
401
464
|
const kaminoReserves = new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
402
465
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
403
466
|
if (!oracle) {
|
|
@@ -849,9 +912,13 @@ export class KaminoMarket {
|
|
|
849
912
|
return finalObligations;
|
|
850
913
|
}
|
|
851
914
|
|
|
852
|
-
async getAllUserObligations(
|
|
915
|
+
async getAllUserObligations(
|
|
916
|
+
user: PublicKey,
|
|
917
|
+
commitment = this.connection.commitment,
|
|
918
|
+
slot?: number
|
|
919
|
+
): Promise<KaminoObligation[]> {
|
|
853
920
|
const [currentSlot, obligations] = await Promise.all([
|
|
854
|
-
this.connection.getSlot(),
|
|
921
|
+
slot !== undefined ? Promise.resolve(slot) : this.connection.getSlot(),
|
|
855
922
|
this.connection.getProgramAccounts(this.programId, {
|
|
856
923
|
filters: [
|
|
857
924
|
{
|
|
@@ -1226,8 +1293,10 @@ export class KaminoMarket {
|
|
|
1226
1293
|
/**
|
|
1227
1294
|
* Get all Scope prices used by all the market reserves
|
|
1228
1295
|
*/
|
|
1229
|
-
async getAllScopePrices(
|
|
1230
|
-
|
|
1296
|
+
async getAllScopePrices(
|
|
1297
|
+
scope: Scope,
|
|
1298
|
+
allOraclePrices: PubkeyHashMap<PublicKey, OraclePrices>
|
|
1299
|
+
): Promise<KaminoPrices> {
|
|
1231
1300
|
const spot: MintToPriceMap = {};
|
|
1232
1301
|
const twaps: MintToPriceMap = {};
|
|
1233
1302
|
for (const reserve of this.reserves.values()) {
|
|
@@ -1236,7 +1305,7 @@ export class KaminoMarket {
|
|
|
1236
1305
|
const oracle = reserve.state.config.tokenInfo.scopeConfiguration.priceFeed;
|
|
1237
1306
|
const chain = reserve.state.config.tokenInfo.scopeConfiguration.priceChain;
|
|
1238
1307
|
const twapChain = reserve.state.config.tokenInfo.scopeConfiguration.twapChain.filter((x) => x > 0);
|
|
1239
|
-
const oraclePrices = allOraclePrices.get(
|
|
1308
|
+
const oraclePrices = allOraclePrices.get(oracle);
|
|
1240
1309
|
if (oraclePrices && oracle && isNotNullPubkey(oracle) && chain && Scope.isScopeChainValid(chain)) {
|
|
1241
1310
|
const spotPrice = await scope.getPriceFromChain(chain, oraclePrices);
|
|
1242
1311
|
spot[tokenMint] = { price: spotPrice.price, name: tokenName };
|
|
@@ -1252,16 +1321,18 @@ export class KaminoMarket {
|
|
|
1252
1321
|
/**
|
|
1253
1322
|
* Get all Scope/Pyth/Switchboard prices used by all the market reserves
|
|
1254
1323
|
*/
|
|
1255
|
-
async getAllPrices(): Promise<KlendPrices> {
|
|
1324
|
+
async getAllPrices(oracleAccounts?: AllOracleAccounts): Promise<KlendPrices> {
|
|
1256
1325
|
const klendPrices: KlendPrices = {
|
|
1257
1326
|
scope: { spot: {}, twap: {} },
|
|
1258
1327
|
pyth: { spot: {}, twap: {} },
|
|
1259
1328
|
switchboard: { spot: {}, twap: {} },
|
|
1260
1329
|
};
|
|
1261
|
-
const allOracleAccounts =
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1330
|
+
const allOracleAccounts =
|
|
1331
|
+
oracleAccounts ??
|
|
1332
|
+
(await getAllOracleAccounts(
|
|
1333
|
+
this.connection,
|
|
1334
|
+
this.getReserves().map((x) => x.state)
|
|
1335
|
+
));
|
|
1265
1336
|
const pythCache = new PubkeyHashMap<PublicKey, PythPrices>();
|
|
1266
1337
|
const switchboardCache = new PubkeyHashMap<PublicKey, CandidatePrice>();
|
|
1267
1338
|
const scopeCache = new PubkeyHashMap<PublicKey, OraclePrices>();
|
|
@@ -1473,7 +1544,8 @@ export async function getReservesForMarket(
|
|
|
1473
1544
|
marketAddress: PublicKey,
|
|
1474
1545
|
connection: Connection,
|
|
1475
1546
|
programId: PublicKey,
|
|
1476
|
-
recentSlotDurationMs: number
|
|
1547
|
+
recentSlotDurationMs: number,
|
|
1548
|
+
oracleAccounts?: AllOracleAccounts
|
|
1477
1549
|
): Promise<Map<PublicKey, KaminoReserve>> {
|
|
1478
1550
|
const reserves = await connection.getProgramAccounts(programId, {
|
|
1479
1551
|
filters: [
|
|
@@ -1501,7 +1573,7 @@ export async function getReservesForMarket(
|
|
|
1501
1573
|
return reserveAccount;
|
|
1502
1574
|
});
|
|
1503
1575
|
const allBuffers = reserves.map((reserve) => reserve.account);
|
|
1504
|
-
const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves);
|
|
1576
|
+
const reservesAndOracles = await getTokenOracleData(connection, deserializedReserves, oracleAccounts);
|
|
1505
1577
|
const reservesByAddress = new PubkeyHashMap<PublicKey, KaminoReserve>();
|
|
1506
1578
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
1507
1579
|
if (!oracle) {
|
|
@@ -1524,7 +1596,8 @@ export async function getSingleReserve(
|
|
|
1524
1596
|
reservePk: PublicKey,
|
|
1525
1597
|
connection: Connection,
|
|
1526
1598
|
recentSlotDurationMs: number,
|
|
1527
|
-
accountData?: AccountInfo<Buffer
|
|
1599
|
+
accountData?: AccountInfo<Buffer>,
|
|
1600
|
+
oracleAccounts?: AllOracleAccounts
|
|
1528
1601
|
): Promise<KaminoReserve> {
|
|
1529
1602
|
const reserve = accountData ? accountData : await connection.getAccountInfo(reservePk);
|
|
1530
1603
|
|
|
@@ -1538,7 +1611,7 @@ export async function getSingleReserve(
|
|
|
1538
1611
|
throw Error(`Could not parse reserve ${reservePk.toBase58()}`);
|
|
1539
1612
|
}
|
|
1540
1613
|
|
|
1541
|
-
const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount]);
|
|
1614
|
+
const reservesAndOracles = await getTokenOracleData(connection, [reserveAccount], oracleAccounts);
|
|
1542
1615
|
const [reserveState, oracle] = reservesAndOracles[0];
|
|
1543
1616
|
|
|
1544
1617
|
if (!oracle) {
|
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,
|
|
@@ -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/utils/oracle.ts
CHANGED
|
@@ -107,9 +107,10 @@ export function getTokenOracleDataSync(
|
|
|
107
107
|
// TODO: Add freshness of the latest price to match sc logic
|
|
108
108
|
export async function getTokenOracleData(
|
|
109
109
|
connection: Connection,
|
|
110
|
-
reserves: Reserve[]
|
|
110
|
+
reserves: Reserve[],
|
|
111
|
+
oracleAccounts?: AllOracleAccounts
|
|
111
112
|
): Promise<Array<[Reserve, TokenOracleData | undefined]>> {
|
|
112
|
-
const allOracleAccounts = await getAllOracleAccounts(connection, reserves);
|
|
113
|
+
const allOracleAccounts = oracleAccounts ?? (await getAllOracleAccounts(connection, reserves));
|
|
113
114
|
const switchboardV2 = await SwitchboardProgram.loadMainnet(connection);
|
|
114
115
|
return getTokenOracleDataSync(allOracleAccounts, switchboardV2, reserves);
|
|
115
116
|
}
|