@kamino-finance/klend-sdk 5.11.2 → 5.11.3-beta.0

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.
Files changed (82) hide show
  1. package/dist/classes/action.d.ts +23 -23
  2. package/dist/classes/action.d.ts.map +1 -1
  3. package/dist/classes/action.js +69 -149
  4. package/dist/classes/action.js.map +1 -1
  5. package/dist/classes/manager.d.ts +1 -1
  6. package/dist/classes/manager.js +1 -1
  7. package/dist/classes/market.d.ts +3 -3
  8. package/dist/classes/market.d.ts.map +1 -1
  9. package/dist/classes/market.js +30 -16
  10. package/dist/classes/market.js.map +1 -1
  11. package/dist/classes/obligation.d.ts +2 -0
  12. package/dist/classes/obligation.d.ts.map +1 -1
  13. package/dist/classes/obligation.js +5 -0
  14. package/dist/classes/obligation.js.map +1 -1
  15. package/dist/classes/vault.js +14 -14
  16. package/dist/classes/vault.js.map +1 -1
  17. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts +3 -3
  18. package/dist/idl_codegen_kamino_vault/accounts/VaultState.d.ts.map +1 -1
  19. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js +6 -6
  20. package/dist/idl_codegen_kamino_vault/accounts/VaultState.js.map +1 -1
  21. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts +1 -1
  22. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.d.ts.map +1 -1
  23. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js +1 -1
  24. package/dist/idl_codegen_kamino_vault/instructions/giveUpPendingFees.js.map +1 -1
  25. package/dist/idl_codegen_kamino_vault/instructions/initializeSharesMetadata.d.ts +1 -1
  26. package/dist/idl_codegen_kamino_vault/instructions/initializeSharesMetadata.d.ts.map +1 -1
  27. package/dist/idl_codegen_kamino_vault/instructions/initializeSharesMetadata.js +1 -1
  28. package/dist/idl_codegen_kamino_vault/instructions/initializeSharesMetadata.js.map +1 -1
  29. package/dist/idl_codegen_kamino_vault/instructions/updateSharesMetadata.d.ts +1 -1
  30. package/dist/idl_codegen_kamino_vault/instructions/updateSharesMetadata.d.ts.map +1 -1
  31. package/dist/idl_codegen_kamino_vault/instructions/updateSharesMetadata.js +1 -1
  32. package/dist/idl_codegen_kamino_vault/instructions/updateSharesMetadata.js.map +1 -1
  33. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts +1 -1
  34. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.d.ts.map +1 -1
  35. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js +1 -1
  36. package/dist/idl_codegen_kamino_vault/instructions/updateVaultConfig.js.map +1 -1
  37. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts +1 -1
  38. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.d.ts.map +1 -1
  39. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js +1 -1
  40. package/dist/idl_codegen_kamino_vault/instructions/withdrawPendingFees.js.map +1 -1
  41. package/dist/lending_operations/repay_with_collateral_operations.d.ts +4 -4
  42. package/dist/lending_operations/repay_with_collateral_operations.d.ts.map +1 -1
  43. package/dist/lending_operations/repay_with_collateral_operations.js +10 -8
  44. package/dist/lending_operations/repay_with_collateral_operations.js.map +1 -1
  45. package/dist/lending_operations/swap_collateral_operations.d.ts +2 -2
  46. package/dist/lending_operations/swap_collateral_operations.d.ts.map +1 -1
  47. package/dist/lending_operations/swap_collateral_operations.js +11 -6
  48. package/dist/lending_operations/swap_collateral_operations.js.map +1 -1
  49. package/dist/leverage/operations.d.ts +9 -7
  50. package/dist/leverage/operations.d.ts.map +1 -1
  51. package/dist/leverage/operations.js +78 -66
  52. package/dist/leverage/operations.js.map +1 -1
  53. package/dist/leverage/types.d.ts +4 -4
  54. package/dist/leverage/types.d.ts.map +1 -1
  55. package/dist/utils/ObligationType.d.ts +1 -1
  56. package/dist/utils/ObligationType.d.ts.map +1 -1
  57. package/dist/utils/managerTypes.d.ts.map +1 -1
  58. package/dist/utils/managerTypes.js +7 -52
  59. package/dist/utils/managerTypes.js.map +1 -1
  60. package/dist/utils/oracle.d.ts +3 -3
  61. package/dist/utils/oracle.d.ts.map +1 -1
  62. package/package.json +2 -2
  63. package/src/classes/action.ts +75 -162
  64. package/src/classes/manager.ts +1 -1
  65. package/src/classes/market.ts +34 -25
  66. package/src/classes/obligation.ts +6 -0
  67. package/src/classes/vault.ts +14 -14
  68. package/src/client.ts +8 -3
  69. package/src/idl_codegen_kamino_vault/accounts/VaultState.ts +8 -8
  70. package/src/idl_codegen_kamino_vault/instructions/giveUpPendingFees.ts +2 -2
  71. package/src/idl_codegen_kamino_vault/instructions/initializeSharesMetadata.ts +2 -2
  72. package/src/idl_codegen_kamino_vault/instructions/updateSharesMetadata.ts +2 -2
  73. package/src/idl_codegen_kamino_vault/instructions/updateVaultConfig.ts +2 -2
  74. package/src/idl_codegen_kamino_vault/instructions/withdrawPendingFees.ts +2 -2
  75. package/src/idl_kamino_vault.json +7 -7
  76. package/src/lending_operations/repay_with_collateral_operations.ts +15 -11
  77. package/src/lending_operations/swap_collateral_operations.ts +19 -7
  78. package/src/leverage/operations.ts +114 -66
  79. package/src/leverage/types.ts +4 -4
  80. package/src/utils/ObligationType.ts +1 -1
  81. package/src/utils/managerTypes.ts +10 -52
  82. package/src/utils/oracle.ts +2 -2
@@ -61,11 +61,11 @@ import {
61
61
  isNotNullPubkey,
62
62
  PublicKeySet,
63
63
  getAssociatedTokenAddress,
64
- ScopeRefresh,
64
+ ScopePriceRefreshConfig,
65
65
  createAtasIdempotent,
66
66
  obligationFarmStatePda,
67
67
  } from '../utils';
68
- import { KaminoMarket } from './market';
68
+ import { getTokenIdsForScopeRefresh, KaminoMarket } from './market';
69
69
  import { KaminoObligation } from './obligation';
70
70
  import { KaminoReserve } from './reserve';
71
71
  import { ReserveFarmKind } from '../idl_codegen/types';
@@ -73,7 +73,7 @@ import { farmsId } from '@kamino-finance/farms-sdk';
73
73
  import { Reserve } from '../idl_codegen/accounts';
74
74
  import { VanillaObligation } from '../utils/ObligationType';
75
75
  import { PROGRAM_ID } from '../lib';
76
- import { U16_MAX } from '@kamino-finance/scope-sdk';
76
+ import { Scope } from '@kamino-finance/scope-sdk';
77
77
 
78
78
  const SOL_PADDING_FOR_INTEREST = new BN('1000000');
79
79
 
@@ -411,6 +411,7 @@ export class KaminoAction {
411
411
  owner: PublicKey,
412
412
  obligation: KaminoObligation | ObligationType,
413
413
  useV2Ixs: boolean,
414
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
414
415
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
415
416
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
416
417
  requestElevationGroup: boolean = false, // to be requested *before* the deposit
@@ -418,7 +419,6 @@ export class KaminoAction {
418
419
  createLookupTable: boolean = true,
419
420
  referrer: PublicKey = PublicKey.default,
420
421
  currentSlot: number = 0,
421
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' },
422
422
  overrideElevationGroupRequest: number | undefined = undefined // if set, when an elevationgroup request is made, it will use this value
423
423
  ) {
424
424
  const axn = await KaminoAction.initialize(
@@ -437,16 +437,6 @@ export class KaminoAction {
437
437
  axn.addComputeBudgetIxn(extraComputeBudget);
438
438
  }
439
439
 
440
- const allReserves = new PublicKeySet<PublicKey>([
441
- ...axn.depositReserves,
442
- ...axn.borrowReserves,
443
- axn.reserve.address,
444
- ]).toArray();
445
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
446
-
447
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
448
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
449
- }
450
440
  await axn.addSupportIxs(
451
441
  'deposit',
452
442
  includeAtaIxns,
@@ -454,6 +444,7 @@ export class KaminoAction {
454
444
  includeUserMetadata,
455
445
  addInitObligationForFarm,
456
446
  useV2Ixs,
447
+ scopeRefreshConfig,
457
448
  createLookupTable,
458
449
  undefined,
459
450
  overrideElevationGroupRequest
@@ -468,36 +459,10 @@ export class KaminoAction {
468
459
  return axn;
469
460
  }
470
461
 
471
- getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves: PublicKey[]): number[] {
472
- const tokenIds: number[] = [];
473
-
474
- for (const reserveAddress of reserves) {
475
- const reserve = kaminoMarket.getReserveByAddress(reserveAddress);
476
- if (!reserve) {
477
- throw new Error(`Reserve not found for reserve ${reserveAddress.toBase58()}`);
478
- }
479
-
480
- if (!reserve.state.config.tokenInfo.scopeConfiguration.priceFeed.equals(PublicKey.default)) {
481
- reserve.state.config.tokenInfo.scopeConfiguration.priceChain.map((x) => {
482
- if (x !== U16_MAX) {
483
- tokenIds.push(x);
484
- }
485
- });
486
- reserve.state.config.tokenInfo.scopeConfiguration.twapChain.map((x) => {
487
- if (x !== U16_MAX) {
488
- tokenIds.push(x);
489
- }
490
- });
491
- }
492
- }
493
-
494
- return tokenIds;
495
- }
496
-
497
- async addScopeRefreshIxs(tokens: number[], feed: string = 'hubble') {
498
- this.preTxnIxsLabels.unshift(`refreshScopePrices`);
499
- this.preTxnIxs.unshift(
500
- await this.kaminoMarket.scope.refreshPriceListIx(
462
+ async addScopeRefreshIxs(scope: Scope, tokens: number[], feed: string = 'hubble') {
463
+ this.setupIxsLabels.unshift(`refreshScopePrices`);
464
+ this.setupIxs.unshift(
465
+ await scope.refreshPriceListIx(
501
466
  {
502
467
  feed: feed,
503
468
  },
@@ -513,6 +478,7 @@ export class KaminoAction {
513
478
  owner: PublicKey,
514
479
  obligation: KaminoObligation | ObligationType,
515
480
  useV2Ixs: boolean,
481
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
516
482
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
517
483
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
518
484
  requestElevationGroup: boolean = false,
@@ -520,7 +486,6 @@ export class KaminoAction {
520
486
  createLookupTable: boolean = true,
521
487
  referrer: PublicKey = PublicKey.default,
522
488
  currentSlot: number = 0,
523
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' },
524
489
  overrideElevationGroupRequest: number | undefined = undefined // if set, when an elevationgroup request is made, it will use this value
525
490
  ) {
526
491
  const axn = await KaminoAction.initialize(
@@ -538,17 +503,6 @@ export class KaminoAction {
538
503
  axn.addComputeBudgetIxn(extraComputeBudget);
539
504
  }
540
505
 
541
- const allReserves = new PublicKeySet<PublicKey>([
542
- ...axn.depositReserves,
543
- ...axn.borrowReserves,
544
- axn.reserve.address,
545
- ]).toArray();
546
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
547
-
548
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
549
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
550
- }
551
-
552
506
  await axn.addSupportIxs(
553
507
  'borrow',
554
508
  includeAtaIxns,
@@ -556,6 +510,7 @@ export class KaminoAction {
556
510
  includeUserMetadata,
557
511
  addInitObligationForFarm,
558
512
  useV2Ixs,
513
+ scopeRefreshConfig,
559
514
  createLookupTable,
560
515
  undefined,
561
516
  overrideElevationGroupRequest
@@ -576,12 +531,12 @@ export class KaminoAction {
576
531
  mint: PublicKey,
577
532
  owner: PublicKey,
578
533
  obligation: KaminoObligation | ObligationType,
534
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
579
535
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
580
536
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas
581
537
  requestElevationGroup: boolean = false,
582
538
  referrer: PublicKey = PublicKey.default,
583
- currentSlot: number = 0,
584
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
539
+ currentSlot: number = 0
585
540
  ) {
586
541
  const axn = await KaminoAction.initialize(
587
542
  'mint',
@@ -599,17 +554,6 @@ export class KaminoAction {
599
554
  axn.addComputeBudgetIxn(extraComputeBudget);
600
555
  }
601
556
 
602
- const allReserves = new PublicKeySet<PublicKey>([
603
- ...axn.depositReserves,
604
- ...axn.borrowReserves,
605
- axn.reserve.address,
606
- ]).toArray();
607
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
608
-
609
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
610
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
611
- }
612
-
613
557
  await axn.addSupportIxs(
614
558
  'mint',
615
559
  includeAtaIxns,
@@ -617,6 +561,7 @@ export class KaminoAction {
617
561
  false,
618
562
  addInitObligationForFarm,
619
563
  false,
564
+ scopeRefreshConfig,
620
565
  false
621
566
  );
622
567
  axn.addDepositReserveLiquidityIx();
@@ -630,12 +575,12 @@ export class KaminoAction {
630
575
  mint: PublicKey,
631
576
  owner: PublicKey,
632
577
  obligation: KaminoObligation | ObligationType,
578
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
633
579
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
634
580
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas
635
581
  requestElevationGroup: boolean = false,
636
582
  referrer: PublicKey = PublicKey.default,
637
- currentSlot: number = 0,
638
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
583
+ currentSlot: number = 0
639
584
  ) {
640
585
  const axn = await KaminoAction.initialize(
641
586
  'redeem',
@@ -653,17 +598,6 @@ export class KaminoAction {
653
598
  axn.addComputeBudgetIxn(extraComputeBudget);
654
599
  }
655
600
 
656
- const allReserves = new PublicKeySet<PublicKey>([
657
- ...axn.depositReserves,
658
- ...axn.borrowReserves,
659
- axn.reserve.address,
660
- ]).toArray();
661
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
662
-
663
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
664
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
665
- }
666
-
667
601
  await axn.addSupportIxs(
668
602
  'redeem',
669
603
  includeAtaIxns,
@@ -671,6 +605,7 @@ export class KaminoAction {
671
605
  false,
672
606
  addInitObligationForFarm,
673
607
  false,
608
+ scopeRefreshConfig,
674
609
  false
675
610
  );
676
611
  axn.addRedeemReserveCollateralIx();
@@ -685,14 +620,14 @@ export class KaminoAction {
685
620
  owner: PublicKey,
686
621
  obligation: KaminoObligation | ObligationType,
687
622
  useV2Ixs: boolean,
623
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
688
624
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
689
625
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas
690
626
  requestElevationGroup: boolean = false,
691
627
  includeUserMetadata: boolean = true, // if true it includes user metadata
692
628
  createLookupTable: boolean = true,
693
629
  referrer: PublicKey = PublicKey.default,
694
- currentSlot: number = 0,
695
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
630
+ currentSlot: number = 0
696
631
  ) {
697
632
  const axn = await KaminoAction.initialize(
698
633
  'depositCollateral',
@@ -710,17 +645,6 @@ export class KaminoAction {
710
645
  axn.addComputeBudgetIxn(extraComputeBudget);
711
646
  }
712
647
 
713
- const allReserves = new PublicKeySet<PublicKey>([
714
- ...axn.depositReserves,
715
- ...axn.borrowReserves,
716
- axn.reserve.address,
717
- ]).toArray();
718
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
719
-
720
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
721
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
722
- }
723
-
724
648
  await axn.addSupportIxs(
725
649
  'depositCollateral',
726
650
  includeAtaIxns,
@@ -728,6 +652,7 @@ export class KaminoAction {
728
652
  includeUserMetadata,
729
653
  addInitObligationForFarm,
730
654
  useV2Ixs,
655
+ scopeRefreshConfig,
731
656
  createLookupTable
732
657
  );
733
658
  if (useV2Ixs) {
@@ -748,14 +673,14 @@ export class KaminoAction {
748
673
  payer: PublicKey,
749
674
  obligation: KaminoObligation | ObligationType,
750
675
  useV2Ixs: boolean,
676
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
751
677
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
752
678
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
753
679
  requestElevationGroup: boolean = false,
754
680
  includeUserMetadata: boolean = true, // if true it includes user metadata,
755
681
  createLookupTable: boolean = true,
756
682
  referrer: PublicKey = PublicKey.default,
757
- currentSlot: number = 0,
758
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
683
+ currentSlot: number = 0
759
684
  ) {
760
685
  const axn = await KaminoAction.initializeMultiTokenAction(
761
686
  kaminoMarket,
@@ -778,18 +703,6 @@ export class KaminoAction {
778
703
  axn.addComputeBudgetIxn(extraComputeBudget);
779
704
  }
780
705
 
781
- const allReserves = new PublicKeySet<PublicKey>([
782
- ...axn.depositReserves,
783
- ...axn.borrowReserves,
784
- axn.reserve.address,
785
- axn.outflowReserve!.address,
786
- ]).toArray();
787
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
788
-
789
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
790
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
791
- }
792
-
793
706
  await axn.addSupportIxs(
794
707
  'deposit',
795
708
  includeAtaIxns,
@@ -797,6 +710,7 @@ export class KaminoAction {
797
710
  includeUserMetadata,
798
711
  addInitObligationForFarmForDeposit,
799
712
  useV2Ixs,
713
+ undefined,
800
714
  createLookupTable,
801
715
  twoTokenAction
802
716
  );
@@ -814,6 +728,20 @@ export class KaminoAction {
814
728
  useV2Ixs
815
729
  );
816
730
  axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs();
731
+
732
+ // Create the scope refresh ixn in here to ensure it's the first ixn in the txn
733
+ const allReserves = new PublicKeySet<PublicKey>([
734
+ ...axn.depositReserves,
735
+ ...axn.borrowReserves,
736
+ axn.reserve.address,
737
+ ...(axn.outflowReserve ? [axn.outflowReserve.address] : []),
738
+ ...(axn.preLoadedDepositReservesSameTx ? axn.preLoadedDepositReservesSameTx : []),
739
+ ]).toArray();
740
+ const tokenIds = getTokenIdsForScopeRefresh(axn.kaminoMarket, allReserves);
741
+
742
+ if (tokenIds.length > 0 && scopeRefreshConfig) {
743
+ await axn.addScopeRefreshIxs(scopeRefreshConfig.scope, tokenIds, scopeRefreshConfig.scopeFeed);
744
+ }
817
745
  return axn;
818
746
  }
819
747
 
@@ -827,13 +755,13 @@ export class KaminoAction {
827
755
  currentSlot: number,
828
756
  obligation: KaminoObligation | ObligationType,
829
757
  useV2Ixs: boolean,
758
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
830
759
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
831
760
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
832
761
  requestElevationGroup: boolean = false,
833
762
  includeUserMetadata: boolean = true, // if true it includes user metadata,
834
763
  createLookupTable: boolean = true,
835
- referrer: PublicKey = PublicKey.default,
836
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
764
+ referrer: PublicKey = PublicKey.default
837
765
  ) {
838
766
  const axn = await KaminoAction.initializeMultiTokenAction(
839
767
  kaminoMarket,
@@ -855,18 +783,6 @@ export class KaminoAction {
855
783
  axn.addComputeBudgetIxn(extraComputeBudget);
856
784
  }
857
785
 
858
- const allReserves = new PublicKeySet<PublicKey>([
859
- ...axn.depositReserves,
860
- ...axn.borrowReserves,
861
- axn.reserve.address,
862
- axn.outflowReserve!.address,
863
- ]).toArray();
864
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
865
-
866
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
867
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
868
- }
869
-
870
786
  await axn.addSupportIxs(
871
787
  'repay',
872
788
  includeAtaIxns,
@@ -874,6 +790,7 @@ export class KaminoAction {
874
790
  includeUserMetadata,
875
791
  addInitObligationForFarmForRepay,
876
792
  useV2Ixs,
793
+ undefined,
877
794
  createLookupTable,
878
795
  twoTokenAction
879
796
  );
@@ -893,6 +810,19 @@ export class KaminoAction {
893
810
  useV2Ixs
894
811
  );
895
812
  axn.addRefreshFarmsCleanupTxnIxsToCleanupIxs();
813
+ // Create the scope refresh ixn in here to ensure it's the first ixn in the txn
814
+ const allReserves = new PublicKeySet<PublicKey>([
815
+ ...axn.depositReserves,
816
+ ...axn.borrowReserves,
817
+ axn.reserve.address,
818
+ ...(axn.outflowReserve ? [axn.outflowReserve.address] : []),
819
+ ...(axn.preLoadedDepositReservesSameTx ? axn.preLoadedDepositReservesSameTx : []),
820
+ ]).toArray();
821
+ const tokenIds = getTokenIdsForScopeRefresh(axn.kaminoMarket, allReserves);
822
+
823
+ if (tokenIds.length > 0 && scopeRefreshConfig) {
824
+ await axn.addScopeRefreshIxs(scopeRefreshConfig.scope, tokenIds, scopeRefreshConfig.scopeFeed);
825
+ }
896
826
  return axn;
897
827
  }
898
828
 
@@ -903,6 +833,7 @@ export class KaminoAction {
903
833
  owner: PublicKey,
904
834
  obligation: KaminoObligation | ObligationType,
905
835
  useV2Ixs: boolean,
836
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
906
837
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
907
838
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas,
908
839
  requestElevationGroup: boolean = false, // to be requested *after* the withdraw
@@ -910,7 +841,6 @@ export class KaminoAction {
910
841
  createLookupTable: boolean = true,
911
842
  referrer: PublicKey = PublicKey.default,
912
843
  currentSlot: number = 0,
913
- scopeRefresh: ScopeRefresh | undefined = undefined,
914
844
  overrideElevationGroupRequest?: number,
915
845
  // Optional customizations which may be needed if the obligation was mutated by some previous ixn.
916
846
  obligationCustomizations?: {
@@ -936,17 +866,6 @@ export class KaminoAction {
936
866
 
937
867
  axn.depositReserves.push(...(obligationCustomizations?.addedDepositReserves || []));
938
868
 
939
- const allReserves = new PublicKeySet<PublicKey>([
940
- ...axn.depositReserves,
941
- ...axn.borrowReserves,
942
- axn.reserve.address,
943
- ]).toArray();
944
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
945
-
946
- if (tokenIds.length > 0 && scopeRefresh && scopeRefresh.includeScopeRefresh) {
947
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
948
- }
949
-
950
869
  await axn.addSupportIxs(
951
870
  'withdraw',
952
871
  includeAtaIxns,
@@ -954,6 +873,7 @@ export class KaminoAction {
954
873
  includeUserMetadata,
955
874
  addInitObligationForFarm,
956
875
  useV2Ixs,
876
+ scopeRefreshConfig,
957
877
  createLookupTable,
958
878
  false,
959
879
  overrideElevationGroupRequest
@@ -993,6 +913,7 @@ export class KaminoAction {
993
913
  owner: PublicKey,
994
914
  obligation: KaminoObligation | ObligationType,
995
915
  useV2Ixs: boolean,
916
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
996
917
  currentSlot: number,
997
918
  payer: PublicKey | undefined = undefined,
998
919
  extraComputeBudget: number = 1_000_000,
@@ -1000,8 +921,7 @@ export class KaminoAction {
1000
921
  requestElevationGroup: boolean = false,
1001
922
  includeUserMetadata: boolean = true,
1002
923
  createLookupTable: boolean = true,
1003
- referrer: PublicKey = PublicKey.default,
1004
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
924
+ referrer: PublicKey = PublicKey.default
1005
925
  ) {
1006
926
  const axn = await KaminoAction.initialize(
1007
927
  'repay',
@@ -1020,17 +940,6 @@ export class KaminoAction {
1020
940
  axn.addComputeBudgetIxn(extraComputeBudget);
1021
941
  }
1022
942
 
1023
- const allReserves = new PublicKeySet<PublicKey>([
1024
- ...axn.depositReserves,
1025
- ...axn.borrowReserves,
1026
- axn.reserve.address,
1027
- ]).toArray();
1028
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
1029
-
1030
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
1031
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
1032
- }
1033
-
1034
943
  await axn.addSupportIxs(
1035
944
  'repay',
1036
945
  includeAtaIxns,
@@ -1038,6 +947,7 @@ export class KaminoAction {
1038
947
  includeUserMetadata,
1039
948
  addInitObligationForFarm,
1040
949
  useV2Ixs,
950
+ scopeRefreshConfig,
1041
951
  createLookupTable
1042
952
  );
1043
953
  if (useV2Ixs) {
@@ -1060,6 +970,7 @@ export class KaminoAction {
1060
970
  obligationOwner: PublicKey,
1061
971
  obligation: KaminoObligation | ObligationType,
1062
972
  useV2Ixs: boolean,
973
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
1063
974
  extraComputeBudget: number = 1_000_000, // if > 0 then adds the ixn
1064
975
  includeAtaIxns: boolean = true, // if true it includes create and close wsol and token atas, and creates all other token atas if they don't exist
1065
976
  requestElevationGroup: boolean = false,
@@ -1067,8 +978,7 @@ export class KaminoAction {
1067
978
  createLookupTable: boolean = true,
1068
979
  referrer: PublicKey = PublicKey.default,
1069
980
  maxAllowedLtvOverridePercent: number = 0,
1070
- currentSlot: number = 0,
1071
- scopeRefresh: ScopeRefresh = { includeScopeRefresh: false, scopeFeed: 'hubble' }
981
+ currentSlot: number = 0
1072
982
  ) {
1073
983
  const axn = await KaminoAction.initializeMultiTokenAction(
1074
984
  kaminoMarket,
@@ -1089,18 +999,6 @@ export class KaminoAction {
1089
999
  axn.addComputeBudgetIxn(extraComputeBudget);
1090
1000
  }
1091
1001
 
1092
- const allReserves = new PublicKeySet<PublicKey>([
1093
- ...axn.depositReserves,
1094
- ...axn.borrowReserves,
1095
- axn.reserve.address,
1096
- axn.outflowReserve!.address,
1097
- ]).toArray();
1098
- const tokenIds = axn.getTokenIdsForScopeRefresh(kaminoMarket, allReserves);
1099
-
1100
- if (tokenIds.length > 0 && scopeRefresh.includeScopeRefresh) {
1101
- await axn.addScopeRefreshIxs(tokenIds, scopeRefresh.scopeFeed);
1102
- }
1103
-
1104
1002
  await axn.addSupportIxs(
1105
1003
  'liquidate',
1106
1004
  includeAtaIxns,
@@ -1108,6 +1006,7 @@ export class KaminoAction {
1108
1006
  includeUserMetadata,
1109
1007
  addInitObligationForFarm,
1110
1008
  useV2Ixs,
1009
+ scopeRefreshConfig,
1111
1010
  createLookupTable
1112
1011
  );
1113
1012
  if (useV2Ixs) {
@@ -2500,6 +2399,7 @@ export class KaminoAction {
2500
2399
  includeUserMetadata: boolean,
2501
2400
  addInitObligationForFarm: boolean,
2502
2401
  useV2Ixs: boolean,
2402
+ scopeRefreshConfig: ScopePriceRefreshConfig | undefined,
2503
2403
  createLookupTable: boolean,
2504
2404
  twoTokenAction: boolean = false,
2505
2405
  overrideElevationGroupRequest?: number
@@ -2540,6 +2440,19 @@ export class KaminoAction {
2540
2440
  twoTokenAction,
2541
2441
  overrideElevationGroupRequest
2542
2442
  );
2443
+
2444
+ const allReserves = new PublicKeySet<PublicKey>([
2445
+ ...this.depositReserves,
2446
+ ...this.borrowReserves,
2447
+ this.reserve.address,
2448
+ ...(this.outflowReserve ? [this.outflowReserve.address] : []),
2449
+ ...(this.preLoadedDepositReservesSameTx ? this.preLoadedDepositReservesSameTx : []),
2450
+ ]).toArray();
2451
+ const tokenIds = getTokenIdsForScopeRefresh(this.kaminoMarket, allReserves);
2452
+
2453
+ if (tokenIds.length > 0 && scopeRefreshConfig) {
2454
+ await this.addScopeRefreshIxs(scopeRefreshConfig.scope, tokenIds, scopeRefreshConfig.scopeFeed);
2455
+ }
2543
2456
  }
2544
2457
 
2545
2458
  private static optionalAccount(pubkey: PublicKey, programId: PublicKey = PROGRAM_ID): PublicKey {
@@ -949,7 +949,7 @@ export class KaminoManager {
949
949
  }
950
950
 
951
951
  /**
952
- * This retruns an array of scope oracle configs to be used to set the scope price and twap oracles for a reserve
952
+ * This returns an array of scope oracle configs to be used to set the scope price and twap oracles for a reserve
953
953
  * @param feed - scope feed to fetch prices from
954
954
  * @param cluster - cluster to fetch from, this should be left unchanged unless working on devnet or locally
955
955
  * @returns - an array of scope oracle configs
@@ -29,7 +29,7 @@ import Decimal from 'decimal.js';
29
29
  import { FarmState } from '@kamino-finance/farms-sdk';
30
30
  import { PROGRAM_ID } from '../idl_codegen/programId';
31
31
  import bs58 from 'bs58';
32
- import { OraclePrices, Scope } from '@kamino-finance/scope-sdk';
32
+ import { OraclePrices, Scope, U16_MAX } from '@kamino-finance/scope-sdk';
33
33
  import { Fraction } from './fraction';
34
34
  import { chunks, KaminoPrices, MintToPriceMap } from '@kamino-finance/kliquidity-sdk';
35
35
  import { parseTokenSymbol, parseZeroPaddedUtf8 } from './utils';
@@ -59,8 +59,6 @@ export class KaminoMarket {
59
59
 
60
60
  readonly programId: PublicKey;
61
61
 
62
- scope: Scope;
63
-
64
62
  private readonly recentSlotDurationMs: number;
65
63
 
66
64
  private constructor(
@@ -68,7 +66,6 @@ export class KaminoMarket {
68
66
  state: LendingMarket,
69
67
  marketAddress: string,
70
68
  reserves: Map<PublicKey, KaminoReserve>,
71
- scope: Scope,
72
69
  recentSlotDurationMs: number,
73
70
  programId: PublicKey = PROGRAM_ID
74
71
  ) {
@@ -78,7 +75,6 @@ export class KaminoMarket {
78
75
  this.reserves = reserves;
79
76
  this.reservesActive = getReservesActive(this.reserves);
80
77
  this.programId = programId;
81
- this.scope = scope;
82
78
  this.recentSlotDurationMs = recentSlotDurationMs;
83
79
  }
84
80
 
@@ -97,7 +93,6 @@ export class KaminoMarket {
97
93
  marketAddress: PublicKey,
98
94
  recentSlotDurationMs: number,
99
95
  programId: PublicKey = PROGRAM_ID,
100
- setupLocalTest: boolean = false,
101
96
  withReserves: boolean = true
102
97
  ) {
103
98
  const market = await LendingMarket.fetch(connection, marketAddress, programId);
@@ -105,26 +100,12 @@ export class KaminoMarket {
105
100
  if (market === null) {
106
101
  return null;
107
102
  }
108
- let scope: Scope;
109
- if (!setupLocalTest) {
110
- scope = new Scope('mainnet-beta', connection);
111
- } else {
112
- scope = new Scope('localnet', connection);
113
- }
114
103
 
115
104
  const reserves = withReserves
116
105
  ? await getReservesForMarket(marketAddress, connection, programId, recentSlotDurationMs)
117
106
  : new Map<PublicKey, KaminoReserve>();
118
107
 
119
- return new KaminoMarket(
120
- connection,
121
- market,
122
- marketAddress.toString(),
123
- reserves,
124
- scope,
125
- recentSlotDurationMs,
126
- programId
127
- );
108
+ return new KaminoMarket(connection, market, marketAddress.toString(), reserves, recentSlotDurationMs, programId);
128
109
  }
129
110
 
130
111
  async reload(): Promise<void> {
@@ -1186,9 +1167,9 @@ export class KaminoMarket {
1186
1167
  /**
1187
1168
  * Get all Scope prices used by all the market reserves
1188
1169
  */
1189
- async getAllScopePrices(oraclePrices?: OraclePrices): Promise<KaminoPrices> {
1170
+ async getAllScopePrices(scope: Scope, oraclePrices?: OraclePrices): Promise<KaminoPrices> {
1190
1171
  if (!oraclePrices) {
1191
- oraclePrices = await this.scope.getOraclePrices();
1172
+ oraclePrices = await scope.getOraclePrices();
1192
1173
  }
1193
1174
  const spot: MintToPriceMap = {};
1194
1175
  const twaps: MintToPriceMap = {};
@@ -1199,11 +1180,11 @@ export class KaminoMarket {
1199
1180
  const chain = reserve.state.config.tokenInfo.scopeConfiguration.priceChain;
1200
1181
  const twapChain = reserve.state.config.tokenInfo.scopeConfiguration.twapChain.filter((x) => x > 0);
1201
1182
  if (oracle && isNotNullPubkey(oracle) && chain && Scope.isScopeChainValid(chain)) {
1202
- const spotPrice = await this.scope.getPriceFromChain(chain, oraclePrices);
1183
+ const spotPrice = await scope.getPriceFromChain(chain, oraclePrices);
1203
1184
  spot[tokenMint] = { price: spotPrice.price, name: tokenName };
1204
1185
  }
1205
1186
  if (oracle && isNotNullPubkey(oracle) && twapChain && Scope.isScopeChainValid(twapChain)) {
1206
- const twap = await this.scope.getPriceFromChain(twapChain, oraclePrices);
1187
+ const twap = await scope.getPriceFromChain(twapChain, oraclePrices);
1207
1188
  twaps[tokenMint] = { price: twap.price, name: tokenName };
1208
1189
  }
1209
1190
  }
@@ -1527,6 +1508,34 @@ export function getReservesActive(reserves: Map<PublicKey, KaminoReserve>): Map<
1527
1508
  return reservesActive;
1528
1509
  }
1529
1510
 
1511
+ export function getTokenIdsForScopeRefresh(kaminoMarket: KaminoMarket, reserves: PublicKey[]): number[] {
1512
+ const tokenIds: number[] = [];
1513
+
1514
+ for (const reserveAddress of reserves) {
1515
+ const reserve = kaminoMarket.getReserveByAddress(reserveAddress);
1516
+ if (!reserve) {
1517
+ throw new Error(`Reserve not found for reserve ${reserveAddress.toBase58()}`);
1518
+ }
1519
+
1520
+ if (!reserve.state.config.tokenInfo.scopeConfiguration.priceFeed.equals(PublicKey.default)) {
1521
+ let x = 0;
1522
+
1523
+ while (reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x] !== U16_MAX) {
1524
+ tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.priceChain[x]);
1525
+ x++;
1526
+ }
1527
+
1528
+ x = 0;
1529
+ while (reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x] !== U16_MAX) {
1530
+ tokenIds.push(reserve.state.config.tokenInfo.scopeConfiguration.twapChain[x]);
1531
+ x++;
1532
+ }
1533
+ }
1534
+ }
1535
+
1536
+ return tokenIds;
1537
+ }
1538
+
1530
1539
  export async function getReserveFromMintAndMarket(
1531
1540
  connection: Connection,
1532
1541
  market: KaminoMarket,
@@ -17,6 +17,7 @@ import {
17
17
  getObligationPdaWithArgs,
18
18
  getObligationType,
19
19
  isNotNullPubkey,
20
+ ObligationType,
20
21
  PubkeyHashMap,
21
22
  TOTAL_NUMBER_OF_IDS_TO_CHECK,
22
23
  U64_MAX,
@@ -1593,3 +1594,8 @@ export class KaminoObligation {
1593
1594
  }
1594
1595
  }
1595
1596
  }
1597
+
1598
+ // Create a function that checks if an obligation is of type obligation or obligationType
1599
+ export function isKaminoObligation(obligation: KaminoObligation | ObligationType): obligation is KaminoObligation {
1600
+ return 'obligationAddress' in obligation;
1601
+ }