@kamino-finance/klend-sdk 7.3.1 → 7.3.3

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.
@@ -104,6 +104,7 @@ import type { AccountInfoBase, AccountInfoWithJsonData, AccountInfoWithPubkey }
104
104
  import { arrayElementConfigItems, ConfigUpdater } from './configItems';
105
105
  import { OracleMappings } from '@kamino-finance/scope-sdk/dist/@codegen/scope/accounts';
106
106
  import { getReserveFarmRewardsAPY as getReserveFarmRewardsAPYUtils, ReserveIncentives } from '../utils/farmUtils';
107
+ import { fetchKaminoCdnData } from '../utils/readCdnData';
107
108
 
108
109
  const base58Decoder = getBase58Decoder();
109
110
 
@@ -585,16 +586,6 @@ export class KaminoManager {
585
586
  return this._vaultClient.depositIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
586
587
  }
587
588
 
588
- async buyVaultSharesIxs(
589
- user: TransactionSigner,
590
- vault: KaminoVault,
591
- tokenAmount: Decimal,
592
- vaultReservesMap?: Map<Address, KaminoReserve>,
593
- farmState?: FarmState
594
- ): Promise<DepositIxs> {
595
- return this._vaultClient.buySharesIxs(user, vault, tokenAmount, vaultReservesMap, farmState);
596
- }
597
-
598
589
  /**
599
590
  * This function creates instructions to stake the shares in the vault farm if the vault has a farm
600
591
  * @param user - user to stake
@@ -728,17 +719,6 @@ export class KaminoManager {
728
719
  return this._vaultClient.withdrawIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
729
720
  }
730
721
 
731
- async sellVaultSharesIxs(
732
- user: TransactionSigner,
733
- vault: KaminoVault,
734
- shareAmount: Decimal,
735
- slot: Slot,
736
- vaultReservesMap?: Map<Address, KaminoReserve>,
737
- farmState?: FarmState
738
- ): Promise<WithdrawIxs> {
739
- return this._vaultClient.sellSharesIxs(user, vault, shareAmount, slot, vaultReservesMap, farmState);
740
- }
741
-
742
722
  /**
743
723
  * This method withdraws all the pending fees from the vault to the owner's token ATA
744
724
  * @param vault - vault for which the admin withdraws the pending fees
@@ -901,7 +881,10 @@ export class KaminoManager {
901
881
  const allReserves = reservePairs.map(([, reserve]) => reserve);
902
882
 
903
883
  // Get all oracle accounts
904
- const allOracleAccounts = await getAllOracleAccounts(this.getRpc(), allReserves);
884
+ const [allOracleAccounts, cdnResourcesData] = await Promise.all([
885
+ getAllOracleAccounts(this.getRpc(), allReserves),
886
+ fetchKaminoCdnData(),
887
+ ]);
905
888
  // Group reserves by market
906
889
  const marketToReserve = new Map<Address, ReserveWithAddress[]>();
907
890
  for (const [reserveAddress, reserveState] of reservePairs) {
@@ -940,7 +923,8 @@ export class KaminoManager {
940
923
  state,
941
924
  oracle,
942
925
  this.getRpc(),
943
- this.recentSlotDurationMs
926
+ this.recentSlotDurationMs,
927
+ cdnResourcesData
944
928
  );
945
929
  reservesByAddress.set(kaminoReserve.address, kaminoReserve);
946
930
  });
@@ -56,6 +56,7 @@ import { ObligationZP } from '../@codegen/klend/zero_padding';
56
56
  import { checkDefined } from '../utils/validations';
57
57
  import { Buffer } from 'buffer';
58
58
  import { ReserveStatus } from '../@codegen/klend/types';
59
+ import { fetchKaminoCdnData } from '../utils/readCdnData';
59
60
 
60
61
  export type KaminoMarketRpcApi = GetAccountInfoApi &
61
62
  GetMultipleAccountsApi &
@@ -479,7 +480,10 @@ export class KaminoMarket {
479
480
  state: reserveAccount,
480
481
  };
481
482
  });
482
- const reservesAndOracles = await getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts);
483
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
484
+ getTokenOracleData(this.getRpc(), deserializedReserves, oracleAccounts),
485
+ fetchKaminoCdnData(),
486
+ ]);
483
487
  const kaminoReserves = new Map<Address, KaminoReserve>();
484
488
  reservesAndOracles.forEach(([reserve, oracle], index) => {
485
489
  if (!oracle) {
@@ -494,7 +498,8 @@ export class KaminoMarket {
494
498
  reserve,
495
499
  oracle,
496
500
  this.rpc,
497
- this.recentSlotDurationMs
501
+ this.recentSlotDurationMs,
502
+ cdnResourcesData
498
503
  );
499
504
  kaminoReserves.set(kaminoReserve.address, kaminoReserve);
500
505
  });
@@ -1712,7 +1717,10 @@ export async function getReservesForMarket(
1712
1717
  state: reserveAccount,
1713
1718
  };
1714
1719
  });
1715
- const reservesAndOracles = await getTokenOracleData(rpc, deserializedReserves, oracleAccounts);
1720
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
1721
+ getTokenOracleData(rpc, deserializedReserves, oracleAccounts),
1722
+ fetchKaminoCdnData(),
1723
+ ]);
1716
1724
  const reservesByAddress = new Map<Address, KaminoReserve>();
1717
1725
  reservesAndOracles.forEach(([reserve, oracle], index) => {
1718
1726
  if (reserve.config.status === ReserveStatus.Obsolete.discriminator) {
@@ -1725,7 +1733,14 @@ export async function getReservesForMarket(
1725
1733
  }) reserve in market ${reserve.lendingMarket}`
1726
1734
  );
1727
1735
  }
1728
- const kaminoReserve = KaminoReserve.initialize(reserves[index].pubkey, reserve, oracle, rpc, recentSlotDurationMs);
1736
+ const kaminoReserve = KaminoReserve.initialize(
1737
+ reserves[index].pubkey,
1738
+ reserve,
1739
+ oracle,
1740
+ rpc,
1741
+ recentSlotDurationMs,
1742
+ cdnResourcesData
1743
+ );
1729
1744
  reservesByAddress.set(kaminoReserve.address, kaminoReserve);
1730
1745
  });
1731
1746
  return reservesByAddress;
@@ -1743,7 +1758,10 @@ export async function getSingleReserve(
1743
1758
  if (reserve === null) {
1744
1759
  throw new Error(`Reserve account ${reservePk} does not exist`);
1745
1760
  }
1746
- const reservesAndOracles = await getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts);
1761
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
1762
+ getTokenOracleData(rpc, [{ address: reservePk, state: reserve }], oracleAccounts),
1763
+ fetchKaminoCdnData(),
1764
+ ]);
1747
1765
  const [, oracle] = reservesAndOracles[0];
1748
1766
 
1749
1767
  if (!oracle) {
@@ -1753,7 +1771,7 @@ export async function getSingleReserve(
1753
1771
  }`
1754
1772
  );
1755
1773
  }
1756
- return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs);
1774
+ return KaminoReserve.initialize(reservePk, reserve, oracle, rpc, recentSlotDurationMs, cdnResourcesData);
1757
1775
  }
1758
1776
 
1759
1777
  export function getReservesActive(reserves: Map<Address, KaminoReserve>): Map<Address, KaminoReserve> {
@@ -57,6 +57,7 @@ import { SYSVAR_RENT_ADDRESS } from '@solana/sysvars';
57
57
  import { noopSigner } from '../utils/signer';
58
58
  import { getRewardPerTimeUnitSecond } from './farm_utils';
59
59
  import { Scope, ScopeEntryMetadata } from '@kamino-finance/scope-sdk';
60
+ import { fetchKaminoCdnData, KaminoCdnData } from '../utils/readCdnData';
60
61
 
61
62
  export type KaminoReserveRpcApi = GetProgramAccountsApi & GetAccountInfoApi & GetMultipleAccountsApi;
62
63
 
@@ -97,10 +98,11 @@ export class KaminoReserve {
97
98
  state: Reserve,
98
99
  tokenOraclePrice: TokenOracleData,
99
100
  rpc: Rpc<KaminoReserveRpcApi>,
100
- recentSlotDurationMs: number
101
+ recentSlotDurationMs: number,
102
+ cdnResourcesData?: KaminoCdnData
101
103
  ): KaminoReserve {
102
104
  const reserve = new KaminoReserve(state, address, tokenOraclePrice, rpc, recentSlotDurationMs);
103
- reserve.stats = reserve.formatReserveData(state);
105
+ reserve.stats = reserve.formatReserveData(state, cdnResourcesData?.deprecatedAssets ?? []);
104
106
  return reserve;
105
107
  }
106
108
 
@@ -779,13 +781,16 @@ export class KaminoReserve {
779
781
  }
780
782
 
781
783
  async load(tokenOraclePrice: TokenOracleData) {
782
- const parsedData = await Reserve.fetch(this.rpc, this.address);
784
+ const [parsedData, cdnResourcesData] = await Promise.all([
785
+ Reserve.fetch(this.rpc, this.address),
786
+ fetchKaminoCdnData(),
787
+ ]);
783
788
  if (!parsedData) {
784
789
  throw Error(`Unable to parse data of reserve ${this.symbol}`);
785
790
  }
786
791
  this.state = parsedData;
787
792
  this.tokenOraclePrice = tokenOraclePrice;
788
- this.stats = this.formatReserveData(parsedData);
793
+ this.stats = this.formatReserveData(parsedData, cdnResourcesData?.deprecatedAssets ?? []);
789
794
  }
790
795
 
791
796
  totalSupplyAPY(currentSlot: Slot) {
@@ -874,7 +879,7 @@ export class KaminoReserve {
874
879
  return { apy: aprToApy(apr, 365), apr };
875
880
  }
876
881
 
877
- private formatReserveData(parsedData: ReserveFields): ReserveDataType {
882
+ private formatReserveData(parsedData: ReserveFields, deprecatedAssets: string[]): ReserveDataType {
878
883
  const mintTotalSupply = new Decimal(parsedData.collateral.mintTotalSupply.toString()).div(this.getMintFactor());
879
884
  let reserveStatus = ReserveStatus.Active;
880
885
  switch (parsedData.config.status) {
@@ -888,6 +893,8 @@ export class KaminoReserve {
888
893
  reserveStatus = ReserveStatus.Hidden;
889
894
  break;
890
895
  }
896
+ const reserveIsUIDeprecated =
897
+ deprecatedAssets.length > 0 ? deprecatedAssets.includes(this.address.toString()) : undefined;
891
898
  return {
892
899
  // Reserve config
893
900
 
@@ -910,6 +917,7 @@ export class KaminoReserve {
910
917
  depositLimitCrossedTimestamp: parsedData.liquidity.depositLimitCrossedTimestamp.toNumber(),
911
918
  borrowLimitCrossedTimestamp: parsedData.liquidity.borrowLimitCrossedTimestamp.toNumber(),
912
919
  borrowFactor: parsedData.config.borrowFactorPct.toNumber(),
920
+ isUIDeprecated: reserveIsUIDeprecated,
913
921
  };
914
922
  }
915
923
 
@@ -44,6 +44,7 @@ export type ReserveDataType = {
44
44
  accumulatedProtocolFees: Decimal;
45
45
  mintTotalSupply: Decimal;
46
46
  borrowFactor: number;
47
+ isUIDeprecated: boolean | undefined;
47
48
  };
48
49
 
49
50
  export type ReserveRewardYield = {
@@ -49,9 +49,6 @@ import {
49
49
  buy,
50
50
  BuyAccounts,
51
51
  BuyArgs,
52
- deposit,
53
- DepositAccounts,
54
- DepositArgs,
55
52
  giveUpPendingFees,
56
53
  GiveUpPendingFeesAccounts,
57
54
  GiveUpPendingFeesArgs,
@@ -73,9 +70,6 @@ import {
73
70
  updateVaultConfig,
74
71
  UpdateVaultConfigAccounts,
75
72
  UpdateVaultConfigArgs,
76
- withdraw,
77
- WithdrawAccounts,
78
- WithdrawArgs,
79
73
  withdrawFromAvailable,
80
74
  WithdrawFromAvailableAccounts,
81
75
  WithdrawFromAvailableArgs,
@@ -145,6 +139,7 @@ import { Farms } from '@kamino-finance/farms-sdk';
145
139
  import { getFarmIncentives } from '@kamino-finance/farms-sdk/dist/utils/apy';
146
140
  import { computeReservesAllocation } from '../utils/vaultAllocation';
147
141
  import { getReserveFarmRewardsAPY } from '../utils/farmUtils';
142
+ import { fetchKaminoCdnData } from '../utils/readCdnData';
148
143
 
149
144
  export const kaminoVaultId = address('KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd');
150
145
  export const kaminoVaultStagingId = address('stKvQfwRsQiKnLtMNVLHKS3exFJmZFsgfzBPWHECUYK');
@@ -1230,92 +1225,6 @@ export class KaminoVaultClient {
1230
1225
  ]);
1231
1226
  createAtasIxs.push(createSharesAtaIxs);
1232
1227
 
1233
- const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1234
- const depositAccounts: DepositAccounts = {
1235
- user: user,
1236
- vaultState: vault.address,
1237
- tokenVault: vaultState.tokenVault,
1238
- tokenMint: vaultState.tokenMint,
1239
- baseVaultAuthority: vaultState.baseVaultAuthority,
1240
- sharesMint: vaultState.sharesMint,
1241
- userTokenAta: userTokenAta,
1242
- userSharesAta: userSharesAta,
1243
- tokenProgram: tokenProgramID,
1244
- klendProgram: this._kaminoLendProgramId,
1245
- sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1246
- eventAuthority: eventAuthority,
1247
- program: this._kaminoVaultProgramId,
1248
- };
1249
-
1250
- const depositArgs: DepositArgs = {
1251
- maxAmount: new BN(
1252
- numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor().toString()
1253
- ),
1254
- };
1255
-
1256
- let depositIx = deposit(depositArgs, depositAccounts, undefined, this._kaminoVaultProgramId);
1257
-
1258
- const vaultReserves = this.getVaultReserves(vaultState);
1259
-
1260
- const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1261
- depositIx = this.appendRemainingAccountsForVaultReserves(depositIx, vaultReserves, vaultReservesState);
1262
-
1263
- const depositIxs: DepositIxs = {
1264
- depositIxs: [...createAtasIxs, depositIx, ...closeAtasIxs],
1265
- stakeInFarmIfNeededIxs: [],
1266
- };
1267
-
1268
- // if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
1269
- if (!(await vault.hasFarm())) {
1270
- return depositIxs;
1271
- }
1272
-
1273
- // if there is a farm, stake the shares
1274
- const stakeSharesIxs = await this.stakeSharesIxs(user, vault, undefined, farmState);
1275
- depositIxs.stakeInFarmIfNeededIxs = stakeSharesIxs;
1276
- return depositIxs;
1277
- }
1278
-
1279
- // todo (silviu): after all tx indexing works for buy/sell ixs remove this function and use the buyIx in the deposit function above
1280
- async buySharesIxs(
1281
- user: TransactionSigner,
1282
- vault: KaminoVault,
1283
- tokenAmount: Decimal,
1284
- vaultReservesMap?: Map<Address, KaminoReserve>,
1285
- farmState?: FarmState
1286
- ): Promise<DepositIxs> {
1287
- const vaultState = await vault.getState();
1288
-
1289
- const tokenProgramID = vaultState.tokenProgram;
1290
- const userTokenAta = await getAssociatedTokenAddress(vaultState.tokenMint, user.address, tokenProgramID);
1291
- const createAtasIxs: Instruction[] = [];
1292
- const closeAtasIxs: Instruction[] = [];
1293
- if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
1294
- const [{ ata: wsolAta, createAtaIx: createWsolAtaIxn }] = await createAtasIdempotent(user, [
1295
- {
1296
- mint: WRAPPED_SOL_MINT,
1297
- tokenProgram: TOKEN_PROGRAM_ADDRESS,
1298
- },
1299
- ]);
1300
- createAtasIxs.push(createWsolAtaIxn);
1301
- const transferWsolIxs = getTransferWsolIxs(
1302
- user,
1303
- wsolAta,
1304
- lamports(
1305
- BigInt(numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).ceil().toString())
1306
- )
1307
- );
1308
- createAtasIxs.push(...transferWsolIxs);
1309
- }
1310
-
1311
- const [{ ata: userSharesAta, createAtaIx: createSharesAtaIxs }] = await createAtasIdempotent(user, [
1312
- {
1313
- mint: vaultState.sharesMint,
1314
- tokenProgram: TOKEN_PROGRAM_ADDRESS,
1315
- },
1316
- ]);
1317
- createAtasIxs.push(createSharesAtaIxs);
1318
-
1319
1228
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1320
1229
  const buyAccounts: BuyAccounts = {
1321
1230
  user: user,
@@ -1531,135 +1440,6 @@ export class KaminoVaultClient {
1531
1440
  return withdrawIxs;
1532
1441
  }
1533
1442
 
1534
- async sellSharesIxs(
1535
- user: TransactionSigner,
1536
- vault: KaminoVault,
1537
- shareAmountToWithdraw: Decimal,
1538
- slot: Slot,
1539
- vaultReservesMap?: Map<Address, KaminoReserve>,
1540
- farmState?: FarmState
1541
- ): Promise<WithdrawIxs> {
1542
- const vaultState = await vault.getState();
1543
- const hasFarm = await vault.hasFarm();
1544
-
1545
- const withdrawIxs: WithdrawIxs = {
1546
- unstakeFromFarmIfNeededIxs: [],
1547
- withdrawIxs: [],
1548
- postWithdrawIxs: [],
1549
- };
1550
-
1551
- // compute the total shares the user has (in ATA + in farm) and check if they want to withdraw everything or just a part
1552
- let userSharesAtaBalance = new Decimal(0);
1553
- const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
1554
- const userSharesAtaState = await fetchMaybeToken(this.getConnection(), userSharesAta);
1555
- if (userSharesAtaState.exists) {
1556
- const userSharesAtaBalanceInLamports = getTokenBalanceFromAccountInfoLamports(userSharesAtaState);
1557
- userSharesAtaBalance = userSharesAtaBalanceInLamports.div(
1558
- new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
1559
- );
1560
- }
1561
-
1562
- let userSharesInFarm = new Decimal(0);
1563
- if (hasFarm) {
1564
- userSharesInFarm = await getUserSharesInTokensStakedInFarm(
1565
- this.getConnection(),
1566
- user.address,
1567
- vaultState.vaultFarm,
1568
- vaultState.sharesMintDecimals.toNumber()
1569
- );
1570
- }
1571
-
1572
- let sharesToWithdraw = shareAmountToWithdraw;
1573
- const totalUserShares = userSharesAtaBalance.add(userSharesInFarm);
1574
- let withdrawAllShares = false;
1575
- if (sharesToWithdraw.gt(totalUserShares)) {
1576
- sharesToWithdraw = new Decimal(U64_MAX.toString()).div(
1577
- new Decimal(10).pow(vaultState.sharesMintDecimals.toString())
1578
- );
1579
- withdrawAllShares = true;
1580
- }
1581
-
1582
- // if not enough shares in ATA unstake from farm
1583
- const sharesInAtaAreEnoughForWithdraw = sharesToWithdraw.lte(userSharesAtaBalance);
1584
- if (hasFarm && !sharesInAtaAreEnoughForWithdraw && userSharesInFarm.gt(0)) {
1585
- // if we need to unstake we need to make sure share ata is created
1586
- const [{ createAtaIx }] = await createAtasIdempotent(user, [
1587
- {
1588
- mint: vaultState.sharesMint,
1589
- tokenProgram: TOKEN_PROGRAM_ADDRESS,
1590
- },
1591
- ]);
1592
- withdrawIxs.unstakeFromFarmIfNeededIxs.push(createAtaIx);
1593
- let shareLamportsToWithdraw = new Decimal(U64_MAX.toString());
1594
- if (!withdrawAllShares) {
1595
- const sharesToWithdrawFromFarm = sharesToWithdraw.sub(userSharesAtaBalance);
1596
- shareLamportsToWithdraw = collToLamportsDecimal(
1597
- sharesToWithdrawFromFarm,
1598
- vaultState.sharesMintDecimals.toNumber()
1599
- );
1600
- }
1601
- const unstakeAndWithdrawFromFarmIxs = await getFarmUnstakeAndWithdrawIxs(
1602
- this.getConnection(),
1603
- user,
1604
- shareLamportsToWithdraw,
1605
- vaultState.vaultFarm,
1606
- farmState
1607
- );
1608
- withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.unstakeIx);
1609
- withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.withdrawIx);
1610
- }
1611
-
1612
- // if the vault has allocations withdraw otherwise wtihdraw from available ix
1613
- const vaultAllocation = vaultState.vaultAllocationStrategy.find(
1614
- (allocation) => allocation.reserve !== DEFAULT_PUBLIC_KEY
1615
- );
1616
-
1617
- if (vaultAllocation) {
1618
- const withdrawFromVaultIxs = await this.sellSharesWithReserveIxs(
1619
- user,
1620
- vault,
1621
- sharesToWithdraw,
1622
- totalUserShares,
1623
- slot,
1624
- vaultReservesMap
1625
- );
1626
- withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
1627
- } else {
1628
- const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, vault, sharesToWithdraw);
1629
- withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
1630
- }
1631
-
1632
- // if the vault is for SOL return the ix to unwrap the SOL
1633
- if (vaultState.tokenMint === WRAPPED_SOL_MINT) {
1634
- const userWsolAta = await getAssociatedTokenAddress(WRAPPED_SOL_MINT, user.address);
1635
- const unwrapIx = getCloseAccountInstruction(
1636
- {
1637
- account: userWsolAta,
1638
- owner: user,
1639
- destination: user.address,
1640
- },
1641
- { programAddress: TOKEN_PROGRAM_ADDRESS }
1642
- );
1643
- withdrawIxs.postWithdrawIxs.push(unwrapIx);
1644
- }
1645
-
1646
- // if we burn all of user's shares close its shares ATA
1647
- const burnAllUserShares = sharesToWithdraw.gt(totalUserShares);
1648
- if (burnAllUserShares) {
1649
- const closeAtaIx = getCloseAccountInstruction(
1650
- {
1651
- account: userSharesAta,
1652
- owner: user,
1653
- destination: user.address,
1654
- },
1655
- { programAddress: TOKEN_PROGRAM_ADDRESS }
1656
- );
1657
- withdrawIxs.postWithdrawIxs.push(closeAtaIx);
1658
- }
1659
-
1660
- return withdrawIxs;
1661
- }
1662
-
1663
1443
  private async withdrawFromAvailableIxs(
1664
1444
  user: TransactionSigner,
1665
1445
  vault: KaminoVault,
@@ -1688,115 +1468,6 @@ export class KaminoVaultClient {
1688
1468
  return [createAtaIx, withdrawFromAvailableIxn];
1689
1469
  }
1690
1470
 
1691
- private async sellSharesWithReserveIxs(
1692
- user: TransactionSigner,
1693
- vault: KaminoVault,
1694
- shareAmount: Decimal,
1695
- allUserShares: Decimal,
1696
- slot: Slot,
1697
- vaultReservesMap?: Map<Address, KaminoReserve>
1698
- ): Promise<Instruction[]> {
1699
- const vaultState = await vault.getState();
1700
-
1701
- const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1702
- const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
1703
- const [{ ata: userTokenAta, createAtaIx }] = await createAtasIdempotent(user, [
1704
- {
1705
- mint: vaultState.tokenMint,
1706
- tokenProgram: vaultState.tokenProgram,
1707
- },
1708
- ]);
1709
-
1710
- const withdrawAllShares = shareAmount.gte(allUserShares);
1711
- const actualSharesToWithdraw = shareAmount.lte(allUserShares) ? shareAmount : allUserShares;
1712
- const shareLamportsToWithdraw = collToLamportsDecimal(
1713
- actualSharesToWithdraw,
1714
- vaultState.sharesMintDecimals.toNumber()
1715
- );
1716
- const tokensPerShare = await this.getTokensPerShareSingleVault(vault, slot);
1717
- const sharesPerToken = new Decimal(1).div(tokensPerShare);
1718
- const tokensToWithdraw = shareLamportsToWithdraw.mul(tokensPerShare);
1719
- let tokenLeftToWithdraw = tokensToWithdraw;
1720
- const availableTokens = new Decimal(vaultState.tokenAvailable.toString());
1721
- tokenLeftToWithdraw = tokenLeftToWithdraw.sub(availableTokens);
1722
-
1723
- type ReserveWithTokensToWithdraw = { reserve: Address; shares: Decimal };
1724
-
1725
- const reserveWithSharesAmountToWithdraw: ReserveWithTokensToWithdraw[] = [];
1726
- let isFirstWithdraw = true;
1727
-
1728
- if (tokenLeftToWithdraw.lte(0)) {
1729
- // Availabe enough to withdraw all - using the first existent reserve
1730
- const firstReserve = vaultState.vaultAllocationStrategy.find((reserve) => reserve.reserve !== DEFAULT_PUBLIC_KEY);
1731
- if (withdrawAllShares) {
1732
- reserveWithSharesAmountToWithdraw.push({
1733
- reserve: firstReserve!.reserve,
1734
- shares: new Decimal(U64_MAX.toString()),
1735
- });
1736
- } else {
1737
- reserveWithSharesAmountToWithdraw.push({
1738
- reserve: firstReserve!.reserve,
1739
- shares: shareLamportsToWithdraw,
1740
- });
1741
- }
1742
- } else {
1743
- // Get decreasing order sorted available liquidity to withdraw from each reserve allocated to
1744
- const reserveAllocationAvailableLiquidityToWithdraw = await this.getReserveAllocationAvailableLiquidityToWithdraw(
1745
- vault,
1746
- slot,
1747
- vaultReservesState
1748
- );
1749
- // sort
1750
- const reserveAllocationAvailableLiquidityToWithdrawSorted = [
1751
- ...reserveAllocationAvailableLiquidityToWithdraw.entries(),
1752
- ].sort((a, b) => b[1].sub(a[1]).toNumber());
1753
-
1754
- reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw], _) => {
1755
- if (tokenLeftToWithdraw.gt(0)) {
1756
- let tokensToWithdrawFromReserve = Decimal.min(tokenLeftToWithdraw, availableLiquidityToWithdraw);
1757
- if (isFirstWithdraw) {
1758
- tokensToWithdrawFromReserve = tokensToWithdrawFromReserve.add(availableTokens);
1759
- isFirstWithdraw = false;
1760
- }
1761
- if (withdrawAllShares) {
1762
- reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: new Decimal(U64_MAX.toString()) });
1763
- } else {
1764
- // round up to the nearest integer the shares to withdraw
1765
- const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
1766
- reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
1767
- }
1768
-
1769
- tokenLeftToWithdraw = tokenLeftToWithdraw.sub(tokensToWithdrawFromReserve);
1770
- }
1771
- });
1772
- }
1773
-
1774
- const withdrawIxs: Instruction[] = [];
1775
- withdrawIxs.push(createAtaIx);
1776
- for (let reserveIndex = 0; reserveIndex < reserveWithSharesAmountToWithdraw.length; reserveIndex++) {
1777
- const reserveWithTokens = reserveWithSharesAmountToWithdraw[reserveIndex];
1778
- const reserveState = vaultReservesState.get(reserveWithTokens.reserve);
1779
- if (reserveState === undefined) {
1780
- throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
1781
- }
1782
- const marketAddress = reserveState.state.lendingMarket;
1783
-
1784
- const withdrawFromReserveIx = await this.sellIx(
1785
- user,
1786
- vault,
1787
- vaultState,
1788
- marketAddress,
1789
- { address: reserveWithTokens.reserve, state: reserveState.state },
1790
- userSharesAta,
1791
- userTokenAta,
1792
- reserveWithTokens.shares,
1793
- vaultReservesState
1794
- );
1795
- withdrawIxs.push(withdrawFromReserveIx);
1796
- }
1797
-
1798
- return withdrawIxs;
1799
- }
1800
1471
  private async withdrawWithReserveIxs(
1801
1472
  user: TransactionSigner,
1802
1473
  vault: KaminoVault,
@@ -1890,7 +1561,7 @@ export class KaminoVaultClient {
1890
1561
  }
1891
1562
  const marketAddress = reserveState.state.lendingMarket;
1892
1563
 
1893
- const withdrawFromReserveIx = await this.withdrawIx(
1564
+ const sellIx = await this.sellIx(
1894
1565
  user,
1895
1566
  vault,
1896
1567
  vaultState,
@@ -1901,7 +1572,7 @@ export class KaminoVaultClient {
1901
1572
  reserveWithTokens.shares,
1902
1573
  vaultReservesState
1903
1574
  );
1904
- withdrawIxs.push(withdrawFromReserveIx);
1575
+ withdrawIxs.push(sellIx);
1905
1576
  }
1906
1577
 
1907
1578
  return withdrawIxs;
@@ -2177,65 +1848,6 @@ export class KaminoVaultClient {
2177
1848
  return sellIxn;
2178
1849
  }
2179
1850
 
2180
- private async withdrawIx(
2181
- user: TransactionSigner,
2182
- vault: KaminoVault,
2183
- vaultState: VaultState,
2184
- marketAddress: Address,
2185
- reserve: ReserveWithAddress,
2186
- userSharesAta: Address,
2187
- userTokenAta: Address,
2188
- shareAmountLamports: Decimal,
2189
- vaultReservesState: Map<Address, KaminoReserve>
2190
- ): Promise<Instruction> {
2191
- const [lendingMarketAuth] = await lendingMarketAuthPda(marketAddress, this._kaminoLendProgramId);
2192
-
2193
- const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
2194
- const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
2195
- const withdrawAccounts: WithdrawAccounts = {
2196
- withdrawFromAvailable: {
2197
- user,
2198
- vaultState: vault.address,
2199
- globalConfig: globalConfig,
2200
- tokenVault: vaultState.tokenVault,
2201
- baseVaultAuthority: vaultState.baseVaultAuthority,
2202
- userTokenAta: userTokenAta,
2203
- tokenMint: vaultState.tokenMint,
2204
- userSharesAta: userSharesAta,
2205
- sharesMint: vaultState.sharesMint,
2206
- tokenProgram: vaultState.tokenProgram,
2207
- sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
2208
- klendProgram: this._kaminoLendProgramId,
2209
- eventAuthority: eventAuthority,
2210
- program: this._kaminoVaultProgramId,
2211
- },
2212
- withdrawFromReserveAccounts: {
2213
- vaultState: vault.address,
2214
- reserve: reserve.address,
2215
- ctokenVault: await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId),
2216
- lendingMarket: marketAddress,
2217
- lendingMarketAuthority: lendingMarketAuth,
2218
- reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
2219
- reserveCollateralMint: reserve.state.collateral.mintPubkey,
2220
- reserveCollateralTokenProgram: TOKEN_PROGRAM_ADDRESS,
2221
- instructionSysvarAccount: SYSVAR_INSTRUCTIONS_ADDRESS,
2222
- },
2223
- eventAuthority: eventAuthority,
2224
- program: this._kaminoVaultProgramId,
2225
- };
2226
-
2227
- const withdrawArgs: WithdrawArgs = {
2228
- sharesAmount: new BN(shareAmountLamports.floor().toString()),
2229
- };
2230
-
2231
- let withdrawIxn = withdraw(withdrawArgs, withdrawAccounts, undefined, this._kaminoVaultProgramId);
2232
-
2233
- const vaultReserves = this.getVaultReserves(vaultState);
2234
- withdrawIxn = this.appendRemainingAccountsForVaultReserves(withdrawIxn, vaultReserves, vaultReservesState);
2235
-
2236
- return withdrawIxn;
2237
- }
2238
-
2239
1851
  private async withdrawFromAvailableIx(
2240
1852
  user: TransactionSigner,
2241
1853
  vault: KaminoVault,
@@ -2970,7 +2582,10 @@ export class KaminoVaultClient {
2970
2582
  const deserializedReserves = await batchFetch(vaultReservesAddresses, (chunk) =>
2971
2583
  this.loadReserializedReserves(chunk)
2972
2584
  );
2973
- const reservesAndOracles = await getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts);
2585
+ const [reservesAndOracles, cdnResourcesData] = await Promise.all([
2586
+ getTokenOracleData(this.getConnection(), deserializedReserves, oracleAccounts),
2587
+ fetchKaminoCdnData(),
2588
+ ]);
2974
2589
  const kaminoReserves = new Map<Address, KaminoReserve>();
2975
2590
  reservesAndOracles.forEach(([reserve, oracle], index) => {
2976
2591
  if (!oracle) {
@@ -2985,7 +2600,8 @@ export class KaminoVaultClient {
2985
2600
  reserve,
2986
2601
  oracle,
2987
2602
  this.getConnection(),
2988
- this.recentSlotDurationMs
2603
+ this.recentSlotDurationMs,
2604
+ cdnResourcesData
2989
2605
  );
2990
2606
  kaminoReserves.set(kaminoReserve.address, kaminoReserve);
2991
2607
  });