@kamino-finance/klend-sdk 7.0.11 → 7.0.13
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/manager.d.ts.map +1 -1
- package/dist/classes/manager.js +13 -5
- package/dist/classes/manager.js.map +1 -1
- package/dist/classes/market.d.ts +1 -1
- package/dist/classes/market.d.ts.map +1 -1
- package/dist/classes/market.js +15 -9
- package/dist/classes/market.js.map +1 -1
- package/dist/classes/reserve.d.ts.map +1 -1
- package/dist/classes/reserve.js +1 -1
- package/dist/classes/reserve.js.map +1 -1
- package/dist/classes/vault.d.ts.map +1 -1
- package/dist/classes/vault.js +18 -4
- package/dist/classes/vault.js.map +1 -1
- package/dist/utils/oracle.d.ts +3 -2
- package/dist/utils/oracle.d.ts.map +1 -1
- package/dist/utils/oracle.js +5 -3
- package/dist/utils/oracle.js.map +1 -1
- package/package.json +1 -1
- package/src/classes/manager.ts +15 -7
- package/src/classes/market.ts +31 -13
- package/src/classes/reserve.ts +5 -1
- package/src/classes/vault.ts +22 -6
- package/src/utils/oracle.ts +14 -6
package/src/classes/market.ts
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
Slot,
|
|
18
18
|
} from '@solana/kit';
|
|
19
19
|
import { KaminoObligation } from './obligation';
|
|
20
|
-
import { KaminoReserve, KaminoReserveRpcApi } from './reserve';
|
|
20
|
+
import { KaminoReserve, KaminoReserveRpcApi, ReserveWithAddress } from './reserve';
|
|
21
21
|
import { LendingMarket, Obligation, ReferrerTokenState, Reserve, UserMetadata } from '../@codegen/klend/accounts';
|
|
22
22
|
import {
|
|
23
23
|
AllOracleAccounts,
|
|
@@ -446,13 +446,13 @@ export class KaminoMarket {
|
|
|
446
446
|
/**
|
|
447
447
|
* @returns The max borrowable amount for leverage positions
|
|
448
448
|
*/
|
|
449
|
-
|
|
449
|
+
getMaxLeverageBorrowableAmount(
|
|
450
450
|
collReserve: KaminoReserve,
|
|
451
451
|
debtReserve: KaminoReserve,
|
|
452
452
|
slot: Slot,
|
|
453
453
|
requestElevationGroup: boolean,
|
|
454
454
|
obligation?: KaminoObligation
|
|
455
|
-
):
|
|
455
|
+
): Decimal {
|
|
456
456
|
return obligation
|
|
457
457
|
? obligation.getMaxBorrowAmount(this, debtReserve.getLiquidityMint(), slot, requestElevationGroup)
|
|
458
458
|
: debtReserve.getMaxBorrowAmountWithCollReserve(this, collReserve, slot);
|
|
@@ -463,7 +463,7 @@ export class KaminoMarket {
|
|
|
463
463
|
const reserveAccounts = await this.rpc
|
|
464
464
|
.getMultipleAccounts(addresses, { commitment: 'processed', encoding: 'base64' })
|
|
465
465
|
.send();
|
|
466
|
-
const deserializedReserves = reserveAccounts.value.map((reserve, i) => {
|
|
466
|
+
const deserializedReserves: ReserveWithAddress[] = reserveAccounts.value.map((reserve, i) => {
|
|
467
467
|
if (reserve === null) {
|
|
468
468
|
// maybe reuse old here
|
|
469
469
|
throw new Error(`Reserve account ${addresses[i]} was not found`);
|
|
@@ -472,13 +472,20 @@ export class KaminoMarket {
|
|
|
472
472
|
if (!reserveAccount) {
|
|
473
473
|
throw Error(`Could not parse reserve ${addresses[i]}`);
|
|
474
474
|
}
|
|
475
|
-
return
|
|
475
|
+
return {
|
|
476
|
+
address: addresses[i],
|
|
477
|
+
state: reserveAccount,
|
|
478
|
+
};
|
|
476
479
|
});
|
|
477
480
|
const reservesAndOracles = await getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts);
|
|
478
481
|
const kaminoReserves = new Map<Address, KaminoReserve>();
|
|
479
482
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
480
483
|
if (!oracle) {
|
|
481
|
-
throw Error(
|
|
484
|
+
throw Error(
|
|
485
|
+
`Could not find oracle for ${parseTokenSymbol(reserve.config.tokenInfo.name)} (${
|
|
486
|
+
addresses[index]
|
|
487
|
+
}) reserve in market ${reserve.lendingMarket}`
|
|
488
|
+
);
|
|
482
489
|
}
|
|
483
490
|
const kaminoReserve = KaminoReserve.initialize(
|
|
484
491
|
addresses[index],
|
|
@@ -1020,7 +1027,7 @@ export class KaminoMarket {
|
|
|
1020
1027
|
obligationAddresses.push(await new VanillaObligation(this.programId).toPda(this.getAddress(), user));
|
|
1021
1028
|
const targetReserve = new Map<Address, KaminoReserve>(Array.from(this.reserves.entries())).get(reserve);
|
|
1022
1029
|
if (!targetReserve) {
|
|
1023
|
-
throw Error(
|
|
1030
|
+
throw Error(`Could not find reserve ${reserve}`);
|
|
1024
1031
|
}
|
|
1025
1032
|
for (const [key, kaminoReserve] of this.reserves) {
|
|
1026
1033
|
if (targetReserve.address === key) {
|
|
@@ -1087,7 +1094,7 @@ export class KaminoMarket {
|
|
|
1087
1094
|
const obligation = await this.getObligationByAddress(vanillaObligationAddress);
|
|
1088
1095
|
|
|
1089
1096
|
if (!obligation) {
|
|
1090
|
-
throw new Error(
|
|
1097
|
+
throw new Error(`Could not find vanilla obligation ${vanillaObligationAddress}`);
|
|
1091
1098
|
}
|
|
1092
1099
|
|
|
1093
1100
|
return obligation;
|
|
@@ -1594,7 +1601,7 @@ export async function getReservesForMarket(
|
|
|
1594
1601
|
encoding: 'base64',
|
|
1595
1602
|
})
|
|
1596
1603
|
.send();
|
|
1597
|
-
const deserializedReserves = reserves.map((reserve) => {
|
|
1604
|
+
const deserializedReserves: ReserveWithAddress[] = reserves.map((reserve) => {
|
|
1598
1605
|
if (reserve.account === null) {
|
|
1599
1606
|
throw new Error(`Reserve account ${reserve.pubkey} does not exist`);
|
|
1600
1607
|
}
|
|
@@ -1604,13 +1611,20 @@ export async function getReservesForMarket(
|
|
|
1604
1611
|
if (!reserveAccount) {
|
|
1605
1612
|
throw Error(`Could not parse reserve ${reserve.pubkey}`);
|
|
1606
1613
|
}
|
|
1607
|
-
return
|
|
1614
|
+
return {
|
|
1615
|
+
address: reserve.pubkey,
|
|
1616
|
+
state: reserveAccount,
|
|
1617
|
+
};
|
|
1608
1618
|
});
|
|
1609
1619
|
const reservesAndOracles = await getTokenOracleData(rpc, deserializedReserves, oracleAccounts);
|
|
1610
1620
|
const reservesByAddress = new Map<Address, KaminoReserve>();
|
|
1611
1621
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
1612
1622
|
if (!oracle) {
|
|
1613
|
-
throw Error(
|
|
1623
|
+
throw Error(
|
|
1624
|
+
`Could not find oracle for ${parseTokenSymbol(reserve.config.tokenInfo.name)} (${
|
|
1625
|
+
reserves[index].pubkey
|
|
1626
|
+
}) reserve in market ${reserve.lendingMarket}`
|
|
1627
|
+
);
|
|
1614
1628
|
}
|
|
1615
1629
|
const kaminoReserve = KaminoReserve.initialize(reserves[index].pubkey, reserve, oracle, rpc, recentSlotDurationMs);
|
|
1616
1630
|
reservesByAddress.set(kaminoReserve.address, kaminoReserve);
|
|
@@ -1630,11 +1644,15 @@ export async function getSingleReserve(
|
|
|
1630
1644
|
if (reserve === null) {
|
|
1631
1645
|
throw new Error(`Reserve account ${reservePk} does not exist`);
|
|
1632
1646
|
}
|
|
1633
|
-
const reservesAndOracles = await getTokenOracleData(rpc, [reserve], oracleAccounts);
|
|
1647
|
+
const reservesAndOracles = await getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts);
|
|
1634
1648
|
const [, oracle] = reservesAndOracles[0];
|
|
1635
1649
|
|
|
1636
1650
|
if (!oracle) {
|
|
1637
|
-
throw Error(
|
|
1651
|
+
throw Error(
|
|
1652
|
+
`Could not find oracle for ${parseTokenSymbol(reserve.config.tokenInfo.name)} (${reservePk}) reserve in market ${
|
|
1653
|
+
reserve.lendingMarket
|
|
1654
|
+
}`
|
|
1655
|
+
);
|
|
1638
1656
|
}
|
|
1639
1657
|
return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs);
|
|
1640
1658
|
}
|
package/src/classes/reserve.ts
CHANGED
|
@@ -112,7 +112,11 @@ export class KaminoReserve {
|
|
|
112
112
|
throw new Error(`Reserve account ${address} does not exist`);
|
|
113
113
|
}
|
|
114
114
|
|
|
115
|
-
const tokenOracleDataWithReserve = await getTokenOracleData(
|
|
115
|
+
const tokenOracleDataWithReserve = await getTokenOracleData(
|
|
116
|
+
rpc,
|
|
117
|
+
[{ address: address, state: reserve }],
|
|
118
|
+
oracleAccounts
|
|
119
|
+
);
|
|
116
120
|
if (!tokenOracleDataWithReserve[0]) {
|
|
117
121
|
throw new Error('Token oracle data not found');
|
|
118
122
|
}
|
package/src/classes/vault.ts
CHANGED
|
@@ -2386,7 +2386,10 @@ export class KaminoVaultClient {
|
|
|
2386
2386
|
if (!reserveAccount) {
|
|
2387
2387
|
throw Error(`Could not parse reserve ${vaultReservesAddresses[i]}`);
|
|
2388
2388
|
}
|
|
2389
|
-
return
|
|
2389
|
+
return {
|
|
2390
|
+
address: vaultReservesAddresses[i],
|
|
2391
|
+
state: reserveAccount,
|
|
2392
|
+
};
|
|
2390
2393
|
});
|
|
2391
2394
|
|
|
2392
2395
|
const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts);
|
|
@@ -2395,7 +2398,11 @@ export class KaminoVaultClient {
|
|
|
2395
2398
|
|
|
2396
2399
|
reservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
2397
2400
|
if (!oracle) {
|
|
2398
|
-
throw Error(
|
|
2401
|
+
throw Error(
|
|
2402
|
+
`Could not find oracle for ${parseTokenSymbol(reserve.config.tokenInfo.name)} (${
|
|
2403
|
+
vaultReservesAddresses[index]
|
|
2404
|
+
}) reserve in market ${reserve.lendingMarket}`
|
|
2405
|
+
);
|
|
2399
2406
|
}
|
|
2400
2407
|
const kaminoReserve = KaminoReserve.initialize(
|
|
2401
2408
|
vaultReservesAddresses[index],
|
|
@@ -2445,9 +2452,18 @@ export class KaminoVaultClient {
|
|
|
2445
2452
|
});
|
|
2446
2453
|
|
|
2447
2454
|
// read missing reserves
|
|
2448
|
-
const
|
|
2449
|
-
|
|
2450
|
-
|
|
2455
|
+
const missingReserveAddresses = [...missingReserves];
|
|
2456
|
+
const missingReservesStates = (await Reserve.fetchMultiple(this.getConnection(), missingReserveAddresses))
|
|
2457
|
+
.map((reserve, index) => {
|
|
2458
|
+
if (!reserve) {
|
|
2459
|
+
return null;
|
|
2460
|
+
}
|
|
2461
|
+
return {
|
|
2462
|
+
address: missingReserveAddresses[index],
|
|
2463
|
+
state: reserve,
|
|
2464
|
+
};
|
|
2465
|
+
})
|
|
2466
|
+
.filter((state) => state !== null);
|
|
2451
2467
|
const missingReservesAndOracles = await getTokenOracleData(
|
|
2452
2468
|
this.getConnection(),
|
|
2453
2469
|
missingReservesStates,
|
|
@@ -2456,7 +2472,7 @@ export class KaminoVaultClient {
|
|
|
2456
2472
|
missingReservesAndOracles.forEach(([reserve, oracle], index) => {
|
|
2457
2473
|
const fetchedReserve = new KaminoReserve(
|
|
2458
2474
|
reserve,
|
|
2459
|
-
[
|
|
2475
|
+
missingReserveAddresses[index]!, // Set maintains order
|
|
2460
2476
|
oracle!,
|
|
2461
2477
|
this.getConnection(),
|
|
2462
2478
|
this.recentSlotDurationMs
|
package/src/utils/oracle.ts
CHANGED
|
@@ -3,7 +3,7 @@ import Decimal from 'decimal.js';
|
|
|
3
3
|
import { Scope } from '@kamino-finance/scope-sdk';
|
|
4
4
|
import { OraclePrices } from '@kamino-finance/scope-sdk/dist/@codegen/scope/accounts/OraclePrices';
|
|
5
5
|
import { isNotNullPubkey } from './pubkey';
|
|
6
|
-
import { parseTokenSymbol } from '../classes';
|
|
6
|
+
import { parseTokenSymbol, ReserveWithAddress } from '../classes';
|
|
7
7
|
import { Reserve } from '../lib';
|
|
8
8
|
import { batchFetch } from '@kamino-finance/kliquidity-sdk';
|
|
9
9
|
import BN from 'bn.js';
|
|
@@ -43,12 +43,12 @@ export type ScopePriceRefreshConfig = {
|
|
|
43
43
|
scopeConfigurations: [Address, Configuration][];
|
|
44
44
|
};
|
|
45
45
|
|
|
46
|
-
export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, reserves:
|
|
46
|
+
export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, reserves: ReserveWithAddress[]) {
|
|
47
47
|
const tokenOracleDataForReserves: Array<[Reserve, TokenOracleData | undefined]> = [];
|
|
48
48
|
const pythCache = new Map<Address, PythPrices>();
|
|
49
49
|
const switchboardCache = new Map<Address, CandidatePrice>();
|
|
50
50
|
const scopeCache = new Map<Address, OraclePrices>();
|
|
51
|
-
for (const reserve of reserves) {
|
|
51
|
+
for (const { address, state: reserve } of reserves) {
|
|
52
52
|
let currentBest: CandidatePrice | undefined = undefined;
|
|
53
53
|
const oracle = {
|
|
54
54
|
pythAddress: reserve.config.tokenInfo.pythConfiguration.price,
|
|
@@ -86,7 +86,10 @@ export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, res
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
if (!currentBest) {
|
|
89
|
-
|
|
89
|
+
const reserveSymbol = parseTokenSymbol(reserve.config.tokenInfo.name);
|
|
90
|
+
console.error(
|
|
91
|
+
`No price found for reserve: ${reserveSymbol ?? 'unknown'} (${address}) in market: ${reserve.lendingMarket}`
|
|
92
|
+
);
|
|
90
93
|
tokenOracleDataForReserves.push([reserve, undefined]);
|
|
91
94
|
continue;
|
|
92
95
|
}
|
|
@@ -105,10 +108,15 @@ export function getTokenOracleDataSync(allOracleAccounts: AllOracleAccounts, res
|
|
|
105
108
|
// TODO: Add freshness of the latest price to match sc logic
|
|
106
109
|
export async function getTokenOracleData(
|
|
107
110
|
rpc: Rpc<GetMultipleAccountsApi>,
|
|
108
|
-
reserves:
|
|
111
|
+
reserves: ReserveWithAddress[],
|
|
109
112
|
oracleAccounts?: AllOracleAccounts
|
|
110
113
|
): Promise<Array<[Reserve, TokenOracleData | undefined]>> {
|
|
111
|
-
const allOracleAccounts =
|
|
114
|
+
const allOracleAccounts =
|
|
115
|
+
oracleAccounts ??
|
|
116
|
+
(await getAllOracleAccounts(
|
|
117
|
+
rpc,
|
|
118
|
+
reserves.map((r) => r.state)
|
|
119
|
+
));
|
|
112
120
|
return getTokenOracleDataSync(allOracleAccounts, reserves);
|
|
113
121
|
}
|
|
114
122
|
|