@kamino-finance/klend-sdk 7.3.3 → 7.3.4

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.
@@ -49,6 +49,9 @@ import {
49
49
  buy,
50
50
  BuyAccounts,
51
51
  BuyArgs,
52
+ deposit,
53
+ DepositAccounts,
54
+ DepositArgs,
52
55
  giveUpPendingFees,
53
56
  GiveUpPendingFeesAccounts,
54
57
  GiveUpPendingFeesArgs,
@@ -70,6 +73,9 @@ import {
70
73
  updateVaultConfig,
71
74
  UpdateVaultConfigAccounts,
72
75
  UpdateVaultConfigArgs,
76
+ withdraw,
77
+ WithdrawAccounts,
78
+ WithdrawArgs,
73
79
  withdrawFromAvailable,
74
80
  WithdrawFromAvailableAccounts,
75
81
  WithdrawFromAvailableArgs,
@@ -1192,6 +1198,27 @@ export class KaminoVaultClient {
1192
1198
  tokenAmount: Decimal,
1193
1199
  vaultReservesMap?: Map<Address, KaminoReserve>,
1194
1200
  farmState?: FarmState
1201
+ ): Promise<DepositIxs> {
1202
+ return this.buildShareEntryIxs('deposit', user, vault, tokenAmount, vaultReservesMap, farmState);
1203
+ }
1204
+
1205
+ async buySharesIxs(
1206
+ user: TransactionSigner,
1207
+ vault: KaminoVault,
1208
+ tokenAmount: Decimal,
1209
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1210
+ farmState?: FarmState
1211
+ ): Promise<DepositIxs> {
1212
+ return this.buildShareEntryIxs('buy', user, vault, tokenAmount, vaultReservesMap, farmState);
1213
+ }
1214
+
1215
+ private async buildShareEntryIxs(
1216
+ mode: 'deposit' | 'buy',
1217
+ user: TransactionSigner,
1218
+ vault: KaminoVault,
1219
+ tokenAmount: Decimal,
1220
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1221
+ farmState?: FarmState
1195
1222
  ): Promise<DepositIxs> {
1196
1223
  const vaultState = await vault.getState();
1197
1224
 
@@ -1226,49 +1253,66 @@ export class KaminoVaultClient {
1226
1253
  createAtasIxs.push(createSharesAtaIxs);
1227
1254
 
1228
1255
  const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1229
- const buyAccounts: BuyAccounts = {
1230
- user: user,
1231
- vaultState: vault.address,
1232
- tokenVault: vaultState.tokenVault,
1233
- tokenMint: vaultState.tokenMint,
1234
- baseVaultAuthority: vaultState.baseVaultAuthority,
1235
- sharesMint: vaultState.sharesMint,
1236
- userTokenAta: userTokenAta,
1237
- userSharesAta: userSharesAta,
1238
- tokenProgram: tokenProgramID,
1239
- klendProgram: this._kaminoLendProgramId,
1240
- sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1241
- eventAuthority: eventAuthority,
1242
- program: this._kaminoVaultProgramId,
1243
- };
1244
-
1245
- const buyArgs: BuyArgs = {
1246
- maxAmount: new BN(
1247
- numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor().toString()
1248
- ),
1249
- };
1250
-
1251
- let buyIx = buy(buyArgs, buyAccounts, undefined, this._kaminoVaultProgramId);
1256
+ const tokenAmountLamports = numberToLamportsDecimal(tokenAmount, vaultState.tokenMintDecimals.toNumber()).floor();
1257
+ let entryIx: Instruction;
1258
+ if (mode === 'deposit') {
1259
+ const depositAccounts: DepositAccounts = {
1260
+ user,
1261
+ vaultState: vault.address,
1262
+ tokenVault: vaultState.tokenVault,
1263
+ tokenMint: vaultState.tokenMint,
1264
+ baseVaultAuthority: vaultState.baseVaultAuthority,
1265
+ sharesMint: vaultState.sharesMint,
1266
+ userTokenAta,
1267
+ userSharesAta,
1268
+ tokenProgram: tokenProgramID,
1269
+ klendProgram: this._kaminoLendProgramId,
1270
+ sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1271
+ eventAuthority,
1272
+ program: this._kaminoVaultProgramId,
1273
+ };
1274
+ const depositArgs: DepositArgs = {
1275
+ maxAmount: new BN(tokenAmountLamports.toString()),
1276
+ };
1277
+ entryIx = deposit(depositArgs, depositAccounts, undefined, this._kaminoVaultProgramId);
1278
+ } else {
1279
+ const buyAccounts: BuyAccounts = {
1280
+ user,
1281
+ vaultState: vault.address,
1282
+ tokenVault: vaultState.tokenVault,
1283
+ tokenMint: vaultState.tokenMint,
1284
+ baseVaultAuthority: vaultState.baseVaultAuthority,
1285
+ sharesMint: vaultState.sharesMint,
1286
+ userTokenAta,
1287
+ userSharesAta,
1288
+ tokenProgram: tokenProgramID,
1289
+ klendProgram: this._kaminoLendProgramId,
1290
+ sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1291
+ eventAuthority,
1292
+ program: this._kaminoVaultProgramId,
1293
+ };
1294
+ const buyArgs: BuyArgs = {
1295
+ maxAmount: new BN(tokenAmountLamports.toString()),
1296
+ };
1297
+ entryIx = buy(buyArgs, buyAccounts, undefined, this._kaminoVaultProgramId);
1298
+ }
1252
1299
 
1253
1300
  const vaultReserves = this.getVaultReserves(vaultState);
1254
-
1255
1301
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1256
- buyIx = this.appendRemainingAccountsForVaultReserves(buyIx, vaultReserves, vaultReservesState);
1302
+ entryIx = this.appendRemainingAccountsForVaultReserves(entryIx, vaultReserves, vaultReservesState);
1257
1303
 
1258
- const depositIxs: DepositIxs = {
1259
- depositIxs: [...createAtasIxs, buyIx, ...closeAtasIxs],
1304
+ const result: DepositIxs = {
1305
+ depositIxs: [...createAtasIxs, entryIx, ...closeAtasIxs],
1260
1306
  stakeInFarmIfNeededIxs: [],
1261
1307
  };
1262
1308
 
1263
- // if there is no farm, we can return the deposit instructions, otherwise include the stake ix in the response
1264
1309
  if (!(await vault.hasFarm())) {
1265
- return depositIxs;
1310
+ return result;
1266
1311
  }
1267
1312
 
1268
- // if there is a farm, stake the shares
1269
1313
  const stakeSharesIxs = await this.stakeSharesIxs(user, vault, undefined, farmState);
1270
- depositIxs.stakeInFarmIfNeededIxs = stakeSharesIxs;
1271
- return depositIxs;
1314
+ result.stakeInFarmIfNeededIxs = stakeSharesIxs;
1315
+ return result;
1272
1316
  }
1273
1317
 
1274
1318
  /**
@@ -1318,6 +1362,39 @@ export class KaminoVaultClient {
1318
1362
  slot: Slot,
1319
1363
  vaultReservesMap?: Map<Address, KaminoReserve>,
1320
1364
  farmState?: FarmState
1365
+ ): Promise<WithdrawIxs> {
1366
+ return this.buildShareExitIxs('withdraw', user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState);
1367
+ }
1368
+
1369
+ /**
1370
+ * This function will return the missing ATA creation instructions, as well as one or multiple withdraw instructions, based on how many reserves it's needed to withdraw from. This might have to be split in multiple transactions
1371
+ * @param user - user to sell shares for vault tokens
1372
+ * @param vault - vault to sell shares from
1373
+ * @param shareAmount - share amount to sell (in tokens, not lamports), in order to withdraw everything, any value > user share amount
1374
+ * @param slot - current slot, used to estimate the interest earned in the different reserves with allocation from the vault
1375
+ * @param [vaultReservesMap] - optional parameter; a hashmap from each reserve pubkey to the reserve state. If provided the function will be significantly faster as it will not have to fetch the reserves
1376
+ * @param [farmState] - the state of the vault farm, if the vault has a farm. Optional. If not provided, it will be fetched
1377
+ * @returns an array of instructions to create missing ATAs if needed and the withdraw instructions
1378
+ */
1379
+ async sellSharesIxs(
1380
+ user: TransactionSigner,
1381
+ vault: KaminoVault,
1382
+ shareAmountToWithdraw: Decimal,
1383
+ slot: Slot,
1384
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1385
+ farmState?: FarmState
1386
+ ): Promise<WithdrawIxs> {
1387
+ return this.buildShareExitIxs('sell', user, vault, shareAmountToWithdraw, slot, vaultReservesMap, farmState);
1388
+ }
1389
+
1390
+ private async buildShareExitIxs(
1391
+ mode: 'withdraw' | 'sell',
1392
+ user: TransactionSigner,
1393
+ vault: KaminoVault,
1394
+ shareAmountToWithdraw: Decimal,
1395
+ slot: Slot,
1396
+ vaultReservesMap?: Map<Address, KaminoReserve>,
1397
+ farmState?: FarmState
1321
1398
  ): Promise<WithdrawIxs> {
1322
1399
  const vaultState = await vault.getState();
1323
1400
  const hasFarm = await vault.hasFarm();
@@ -1389,20 +1466,47 @@ export class KaminoVaultClient {
1389
1466
  withdrawIxs.unstakeFromFarmIfNeededIxs.push(unstakeAndWithdrawFromFarmIxs.withdrawIx);
1390
1467
  }
1391
1468
 
1392
- // if the vault has allocations withdraw otherwise wtihdraw from available ix
1393
- const vaultAllocation = vaultState.vaultAllocationStrategy.find(
1469
+ const hasAllocatedReserves = vaultState.vaultAllocationStrategy.some(
1394
1470
  (allocation) => allocation.reserve !== DEFAULT_PUBLIC_KEY
1395
1471
  );
1396
1472
 
1397
- if (vaultAllocation) {
1398
- const withdrawFromVaultIxs = await this.withdrawWithReserveIxs(
1473
+ if (hasAllocatedReserves) {
1474
+ const reserveExitBuilder: ReserveExitInstructionBuilder =
1475
+ mode === 'withdraw'
1476
+ ? (params) =>
1477
+ this.withdrawIx(
1478
+ params.user,
1479
+ params.vault,
1480
+ params.vaultState,
1481
+ params.marketAddress,
1482
+ params.reserve,
1483
+ params.userSharesAta,
1484
+ params.userTokenAta,
1485
+ params.shareAmountLamports,
1486
+ params.vaultReservesState
1487
+ )
1488
+ : (params) =>
1489
+ this.sellIx(
1490
+ params.user,
1491
+ params.vault,
1492
+ params.vaultState,
1493
+ params.marketAddress,
1494
+ params.reserve,
1495
+ params.userSharesAta,
1496
+ params.userTokenAta,
1497
+ params.shareAmountLamports,
1498
+ params.vaultReservesState
1499
+ );
1500
+ const withdrawFromVaultIxs = await this.buildReserveExitIxs({
1399
1501
  user,
1400
1502
  vault,
1401
- sharesToWithdraw,
1402
- totalUserShares,
1503
+ vaultState,
1504
+ shareAmount: sharesToWithdraw,
1505
+ allUserShares: totalUserShares,
1403
1506
  slot,
1404
- vaultReservesMap
1405
- );
1507
+ vaultReservesMap,
1508
+ builder: reserveExitBuilder,
1509
+ });
1406
1510
  withdrawIxs.withdrawIxs = withdrawFromVaultIxs;
1407
1511
  } else {
1408
1512
  const withdrawFromVaultIxs = await this.withdrawFromAvailableIxs(user, vault, sharesToWithdraw);
@@ -1468,16 +1572,16 @@ export class KaminoVaultClient {
1468
1572
  return [createAtaIx, withdrawFromAvailableIxn];
1469
1573
  }
1470
1574
 
1471
- private async withdrawWithReserveIxs(
1472
- user: TransactionSigner,
1473
- vault: KaminoVault,
1474
- shareAmount: Decimal,
1475
- allUserShares: Decimal,
1476
- slot: Slot,
1477
- vaultReservesMap?: Map<Address, KaminoReserve>
1478
- ): Promise<Instruction[]> {
1479
- const vaultState = await vault.getState();
1480
-
1575
+ private async buildReserveExitIxs({
1576
+ user,
1577
+ vault,
1578
+ vaultState,
1579
+ shareAmount,
1580
+ allUserShares,
1581
+ slot,
1582
+ vaultReservesMap,
1583
+ builder,
1584
+ }: BuildReserveExitIxsParams): Promise<Instruction[]> {
1481
1585
  const vaultReservesState = vaultReservesMap ? vaultReservesMap : await this.loadVaultReserves(vaultState);
1482
1586
  const userSharesAta = await getAssociatedTokenAddress(vaultState.sharesMint, user.address);
1483
1587
  const [{ ata: userTokenAta, createAtaIx }] = await createAtasIdempotent(user, [
@@ -1506,32 +1610,32 @@ export class KaminoVaultClient {
1506
1610
  let isFirstWithdraw = true;
1507
1611
 
1508
1612
  if (tokenLeftToWithdraw.lte(0)) {
1509
- // Availabe enough to withdraw all - using the first existent reserve
1510
1613
  const firstReserve = vaultState.vaultAllocationStrategy.find((reserve) => reserve.reserve !== DEFAULT_PUBLIC_KEY);
1614
+ if (!firstReserve) {
1615
+ throw new Error('No reserve available to satisfy withdraw request');
1616
+ }
1511
1617
  if (withdrawAllShares) {
1512
1618
  reserveWithSharesAmountToWithdraw.push({
1513
- reserve: firstReserve!.reserve,
1619
+ reserve: firstReserve.reserve,
1514
1620
  shares: new Decimal(U64_MAX.toString()),
1515
1621
  });
1516
1622
  } else {
1517
1623
  reserveWithSharesAmountToWithdraw.push({
1518
- reserve: firstReserve!.reserve,
1624
+ reserve: firstReserve.reserve,
1519
1625
  shares: shareLamportsToWithdraw,
1520
1626
  });
1521
1627
  }
1522
1628
  } else {
1523
- // Get decreasing order sorted available liquidity to withdraw from each reserve allocated to
1524
1629
  const reserveAllocationAvailableLiquidityToWithdraw = await this.getReserveAllocationAvailableLiquidityToWithdraw(
1525
1630
  vault,
1526
1631
  slot,
1527
1632
  vaultReservesState
1528
1633
  );
1529
- // sort
1530
1634
  const reserveAllocationAvailableLiquidityToWithdrawSorted = [
1531
1635
  ...reserveAllocationAvailableLiquidityToWithdraw.entries(),
1532
1636
  ].sort((a, b) => b[1].sub(a[1]).toNumber());
1533
1637
 
1534
- reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw], _) => {
1638
+ reserveAllocationAvailableLiquidityToWithdrawSorted.forEach(([key, availableLiquidityToWithdraw]) => {
1535
1639
  if (tokenLeftToWithdraw.gt(0)) {
1536
1640
  let tokensToWithdrawFromReserve = Decimal.min(tokenLeftToWithdraw, availableLiquidityToWithdraw);
1537
1641
  if (isFirstWithdraw) {
@@ -1541,7 +1645,6 @@ export class KaminoVaultClient {
1541
1645
  if (withdrawAllShares) {
1542
1646
  reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: new Decimal(U64_MAX.toString()) });
1543
1647
  } else {
1544
- // round up to the nearest integer the shares to withdraw
1545
1648
  const sharesToWithdrawFromReserve = tokensToWithdrawFromReserve.mul(sharesPerToken).floor();
1546
1649
  reserveWithSharesAmountToWithdraw.push({ reserve: key, shares: sharesToWithdrawFromReserve });
1547
1650
  }
@@ -1553,26 +1656,25 @@ export class KaminoVaultClient {
1553
1656
 
1554
1657
  const withdrawIxs: Instruction[] = [];
1555
1658
  withdrawIxs.push(createAtaIx);
1556
- for (let reserveIndex = 0; reserveIndex < reserveWithSharesAmountToWithdraw.length; reserveIndex++) {
1557
- const reserveWithTokens = reserveWithSharesAmountToWithdraw[reserveIndex];
1659
+ for (const reserveWithTokens of reserveWithSharesAmountToWithdraw) {
1558
1660
  const reserveState = vaultReservesState.get(reserveWithTokens.reserve);
1559
1661
  if (reserveState === undefined) {
1560
1662
  throw new Error(`Reserve ${reserveWithTokens.reserve} not found in vault reserves map`);
1561
1663
  }
1562
1664
  const marketAddress = reserveState.state.lendingMarket;
1563
1665
 
1564
- const sellIx = await this.sellIx(
1666
+ const exitIx = await builder({
1565
1667
  user,
1566
1668
  vault,
1567
1669
  vaultState,
1568
1670
  marketAddress,
1569
- { address: reserveWithTokens.reserve, state: reserveState.state },
1671
+ reserve: { address: reserveWithTokens.reserve, state: reserveState.state },
1570
1672
  userSharesAta,
1571
1673
  userTokenAta,
1572
- reserveWithTokens.shares,
1573
- vaultReservesState
1574
- );
1575
- withdrawIxs.push(sellIx);
1674
+ shareAmountLamports: reserveWithTokens.shares,
1675
+ vaultReservesState,
1676
+ });
1677
+ withdrawIxs.push(exitIx);
1576
1678
  }
1577
1679
 
1578
1680
  return withdrawIxs;
@@ -1848,6 +1950,65 @@ export class KaminoVaultClient {
1848
1950
  return sellIxn;
1849
1951
  }
1850
1952
 
1953
+ private async withdrawIx(
1954
+ user: TransactionSigner,
1955
+ vault: KaminoVault,
1956
+ vaultState: VaultState,
1957
+ marketAddress: Address,
1958
+ reserve: ReserveWithAddress,
1959
+ userSharesAta: Address,
1960
+ userTokenAta: Address,
1961
+ shareAmountLamports: Decimal,
1962
+ vaultReservesState: Map<Address, KaminoReserve>
1963
+ ): Promise<Instruction> {
1964
+ const [lendingMarketAuth] = await lendingMarketAuthPda(marketAddress, this._kaminoLendProgramId);
1965
+
1966
+ const globalConfig = await getKvaultGlobalConfigPda(this._kaminoVaultProgramId);
1967
+ const eventAuthority = await getEventAuthorityPda(this._kaminoVaultProgramId);
1968
+ const withdrawAccounts: WithdrawAccounts = {
1969
+ withdrawFromAvailable: {
1970
+ user,
1971
+ vaultState: vault.address,
1972
+ globalConfig: globalConfig,
1973
+ tokenVault: vaultState.tokenVault,
1974
+ baseVaultAuthority: vaultState.baseVaultAuthority,
1975
+ userTokenAta: userTokenAta,
1976
+ tokenMint: vaultState.tokenMint,
1977
+ userSharesAta: userSharesAta,
1978
+ sharesMint: vaultState.sharesMint,
1979
+ tokenProgram: vaultState.tokenProgram,
1980
+ sharesTokenProgram: TOKEN_PROGRAM_ADDRESS,
1981
+ klendProgram: this._kaminoLendProgramId,
1982
+ eventAuthority: eventAuthority,
1983
+ program: this._kaminoVaultProgramId,
1984
+ },
1985
+ withdrawFromReserveAccounts: {
1986
+ vaultState: vault.address,
1987
+ reserve: reserve.address,
1988
+ ctokenVault: await getCTokenVaultPda(vault.address, reserve.address, this._kaminoVaultProgramId),
1989
+ lendingMarket: marketAddress,
1990
+ lendingMarketAuthority: lendingMarketAuth,
1991
+ reserveLiquiditySupply: reserve.state.liquidity.supplyVault,
1992
+ reserveCollateralMint: reserve.state.collateral.mintPubkey,
1993
+ reserveCollateralTokenProgram: TOKEN_PROGRAM_ADDRESS,
1994
+ instructionSysvarAccount: SYSVAR_INSTRUCTIONS_ADDRESS,
1995
+ },
1996
+ eventAuthority: eventAuthority,
1997
+ program: this._kaminoVaultProgramId,
1998
+ };
1999
+
2000
+ const withdrawArgs: WithdrawArgs = {
2001
+ sharesAmount: new BN(shareAmountLamports.floor().toString()),
2002
+ };
2003
+
2004
+ let withdrawIxn = withdraw(withdrawArgs, withdrawAccounts, undefined, this._kaminoVaultProgramId);
2005
+
2006
+ const vaultReserves = this.getVaultReserves(vaultState);
2007
+ withdrawIxn = this.appendRemainingAccountsForVaultReserves(withdrawIxn, vaultReserves, vaultReservesState);
2008
+
2009
+ return withdrawIxn;
2010
+ }
2011
+
1851
2012
  private async withdrawFromAvailableIx(
1852
2013
  user: TransactionSigner,
1853
2014
  vault: KaminoVault,
@@ -4437,3 +4598,28 @@ export type PendingRewardsForUserInVault = {
4437
4598
  pendingRewardsInVaultReservesFarms: Map<Address, Decimal>;
4438
4599
  totalPendingRewards: Map<Address, Decimal>;
4439
4600
  };
4601
+
4602
+ type ReserveExitBuilderParams = {
4603
+ user: TransactionSigner;
4604
+ vault: KaminoVault;
4605
+ vaultState: VaultState;
4606
+ marketAddress: Address;
4607
+ reserve: ReserveWithAddress;
4608
+ userSharesAta: Address;
4609
+ userTokenAta: Address;
4610
+ shareAmountLamports: Decimal;
4611
+ vaultReservesState: Map<Address, KaminoReserve>;
4612
+ };
4613
+
4614
+ type ReserveExitInstructionBuilder = (params: ReserveExitBuilderParams) => Promise<Instruction>;
4615
+
4616
+ type BuildReserveExitIxsParams = {
4617
+ user: TransactionSigner;
4618
+ vault: KaminoVault;
4619
+ vaultState: VaultState;
4620
+ shareAmount: Decimal;
4621
+ allUserShares: Decimal;
4622
+ slot: Slot;
4623
+ vaultReservesMap?: Map<Address, KaminoReserve>;
4624
+ builder: ReserveExitInstructionBuilder;
4625
+ };