@glamsystems/glam-sdk 0.1.18 → 0.1.20

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/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # GLAM SDK
2
2
 
3
+ ![NPM Version](https://img.shields.io/npm/v/%40glamsystems%2Fglam-sdk)
4
+
3
5
  A TypeScript SDK for interacting with the GLAM Protocol.
4
6
 
5
7
  ## Installation
@@ -11,43 +13,7 @@ npm i @glamsystems/glam-sdk
11
13
  ## Getting Started
12
14
 
13
15
  - [GLAM docs](https://docs.glam.systems)
14
- - [TypeScript API docs](#)
15
16
 
16
17
  ## Examples
17
18
 
18
- ### Set up a GLAM client and interact with a vault
19
-
20
- ```ts
21
- import * as anchor from "@coral-xyz/anchor";
22
- import { GlamClient, WSOL } from "@glamsystems/glam-sdk";
23
- import { PublicKey } from "@solana/web3.js";
24
-
25
- // Need to set ANCHOR_PROVIDER_URL and ANCHOR_WALLET env variables
26
- // ANCHOR_PROVIDER_URL=...
27
- // ANCHOR_WALLET=...
28
- const glamClient = new GlamClient();
29
- const statePda = new PublicKey("FMHLPaEeCbuivqsAfHrr28FpWJ9oKHTx3jzFbb3tYhq4");
30
-
31
- async function main() {
32
- const vaultPda = glamClient.getVaultPda(statePda);
33
-
34
- console.log("statePda:", statePda.toBase58());
35
- console.log("vaultPda:", vaultPda.toBase58());
36
-
37
- const vaultWsolBalance = await glamClient.getVaultTokenBalance(statePda, WSOL);
38
- console.log("vaultWsolBalance:", vaultWsolBalance.toString());
39
-
40
- // Wrap 0.1 SOL
41
- const txSig = await glamClient.wsol.wrap(statePda, new anchor.BN(100_000_000));
42
- console.log("txSig:", txSig);
43
-
44
- // wSOL balance after wrap should increase by 0.1 SOL
45
- const vaultWsolBalanceAfter = await glamClient.getVaultTokenBalance(statePda, WSOL);
46
- console.log("vaultWsolBalanceAfter:", vaultWsolBalanceAfter.toString());
47
- }
48
-
49
- main().catch((error) => {
50
- console.error("Error:", error);
51
- process.exit(1);
52
- });
53
- ```
19
+ See [examples](./examples).
package/index.cjs.js CHANGED
@@ -3251,6 +3251,7 @@ var instructions = [
3251
3251
  },
3252
3252
  {
3253
3253
  name: "glam_vault",
3254
+ writable: true,
3254
3255
  pda: {
3255
3256
  seeds: [
3256
3257
  {
@@ -12448,9 +12449,12 @@ const MEMO_PROGRAM = new web3_js.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgD
12448
12449
  * Stake pools
12449
12450
  */ const JITO_STAKE_POOL = new web3_js.PublicKey("Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb");
12450
12451
  const JUPSOL_STAKE_POOL = new web3_js.PublicKey("8VpRhuxa7sUUepdY3kQiTmX9rS5vx4WgaXiAnXq4KCtr");
12452
+ /**
12453
+ * Referrers
12454
+ */ const GLAM_REFERRER = new web3_js.PublicKey("GLAMrG37ZqioqvzBNQGCfCUueDz3tsr7MwMFyRk9PS89");
12451
12455
 
12452
12456
  const GlamIntegrations = GlamProtocolIdlJson?.types?.find((t)=>t.name === "Integration")?.type?.variants?.map((v)=>v.name) ?? [];
12453
- const GlamPermissions = GlamProtocolIdlJson?.types?.find((t)=>t.name === "Permission")?.type?.variants?.map((v)=>v.name) ?? [];
12457
+ const GlamPermissions = GlamProtocolIdlJson?.types?.find((t)=>t.name === "Permission")?.type?.variants?.map((v)=>v.name).filter((v)=>!v.startsWith("__")) ?? [];
12454
12458
  const GLAM_PROGRAM_ID_DEFAULT = new web3_js.PublicKey(GlamProtocolIdlJson.address);
12455
12459
  class StateIdlModel {
12456
12460
  constructor(data){
@@ -13660,6 +13664,12 @@ class DriftClient {
13660
13664
  sdk.getUserStatsAccountPublicKey(DRIFT_PROGRAM_ID, vault)
13661
13665
  ];
13662
13666
  }
13667
+ getGlamReferrer() {
13668
+ return [
13669
+ sdk.getUserAccountPublicKeySync(DRIFT_PROGRAM_ID, GLAM_REFERRER, 0),
13670
+ sdk.getUserStatsAccountPublicKey(DRIFT_PROGRAM_ID, GLAM_REFERRER)
13671
+ ];
13672
+ }
13663
13673
  async fetchMarketConfigs() {
13664
13674
  const response = await fetch("https://api.glam.systems/v0/drift/market_configs/");
13665
13675
  if (!response.ok) {
@@ -13753,15 +13763,18 @@ class DriftClient {
13753
13763
  const name = `GLAM *.+ ${subAccountId}`.split("").map((char)=>char.charCodeAt(0)).concat(Array(24).fill(0));
13754
13764
  const [user, userStats] = this.getUser(glamState, subAccountId);
13755
13765
  const state = await sdk.getDriftStateAccountPublicKey(DRIFT_PROGRAM_ID);
13766
+ const remainingAccounts = this.getGlamReferrer().map((p)=>({
13767
+ pubkey: p,
13768
+ isWritable: true,
13769
+ isSigner: false
13770
+ }));
13756
13771
  return await this.base.program.methods.driftInitializeUser(subAccountId, name).accounts({
13757
13772
  glamState,
13758
13773
  user,
13759
13774
  userStats,
13760
13775
  state,
13761
13776
  glamSigner
13762
- })// We should only try to add referrer if it is the first user (subAccountId == 0)
13763
- // .remainingAccounts([]) TODO: set glam referral account
13764
- .instruction();
13777
+ }).remainingAccounts(remainingAccounts).instruction();
13765
13778
  }
13766
13779
  async initializeTx(glamState, subAccountId = 0, txOptions = {}) {
13767
13780
  const glamSigner = txOptions.signer || this.base.getSigner();
@@ -13911,7 +13924,11 @@ class DriftClient {
13911
13924
  }
13912
13925
  async placeOrderTx(glamState, orderParams, subAccountId = 0, marketConfigs, txOptions = {}) {
13913
13926
  const { marketIndex, marketType } = orderParams;
13914
- const remainingAccounts = await this.composeRemainingAccounts(glamState, subAccountId, marketConfigs, marketType, marketIndex);
13927
+ const remainingAccounts = (await this.composeRemainingAccounts(glamState, subAccountId, marketConfigs, marketType, marketIndex)).concat(this.getGlamReferrer().map((p)=>({
13928
+ pubkey: p,
13929
+ isWritable: true,
13930
+ isSigner: false
13931
+ })));
13915
13932
  const glamSigner = txOptions.signer || this.base.getSigner();
13916
13933
  const [user] = this.getUser(glamState, subAccountId);
13917
13934
  const state = await sdk.getDriftStateAccountPublicKey(DRIFT_PROGRAM_ID);
@@ -15674,7 +15691,7 @@ const DEFAULT_OBLIGATION_ARGS = {
15674
15691
  id: 0
15675
15692
  };
15676
15693
  const SCOPE_PRICES = new web3_js.PublicKey("3NJYftD5sjVfxSnUdZ1wVML8f3aC6mp1CXCL6L7TnU8C");
15677
- function refreshObligation(accounts, programId) {
15694
+ function refreshObligation(accounts, programId = KAMINO_LENDING_PROGRAM) {
15678
15695
  const keys = [
15679
15696
  {
15680
15697
  pubkey: accounts.lendingMarket,
@@ -15712,7 +15729,7 @@ function refreshObligation(accounts, programId) {
15712
15729
  });
15713
15730
  return ix;
15714
15731
  }
15715
- function refreshReserve(accounts, programId) {
15732
+ function refreshReserve(accounts, programId = KAMINO_LENDING_PROGRAM) {
15716
15733
  const keys = [
15717
15734
  {
15718
15735
  pubkey: accounts.reserve,
@@ -15763,7 +15780,7 @@ function refreshReserve(accounts, programId) {
15763
15780
  });
15764
15781
  return ix;
15765
15782
  }
15766
- function refreshObligationFarmsForReserve(args, accounts, programId) {
15783
+ function refreshObligationFarmsForReserve(args, accounts, programId = KAMINO_LENDING_PROGRAM) {
15767
15784
  const keys = [
15768
15785
  {
15769
15786
  pubkey: accounts.crank,
@@ -15854,7 +15871,7 @@ class KaminoLendingClient {
15854
15871
  * @param txOptions
15855
15872
  * @returns
15856
15873
  */ async initUserMetadata(statePda, referrer, txOptions = {}) {
15857
- const tx = await this.initUserMetadataTx(new web3_js.PublicKey(statePda), referrer ? new web3_js.PublicKey(referrer) : web3_js.PublicKey.default, txOptions);
15874
+ const tx = await this.initUserMetadataTx(new web3_js.PublicKey(statePda), referrer ? new web3_js.PublicKey(referrer) : null, txOptions);
15858
15875
  return await this.base.sendAndConfirm(tx);
15859
15876
  }
15860
15877
  /**
@@ -15940,18 +15957,17 @@ class KaminoLendingClient {
15940
15957
  ], KAMINO_FARM_PROGRAM);
15941
15958
  return obligationFarm;
15942
15959
  }
15943
- async initUserMetadataTx(glamState, referrer, txOptions) {
15960
+ async initUserMetadataTx(glamState, referrer, txOptions = {}) {
15944
15961
  const glamSigner = txOptions.signer || this.base.getSigner();
15945
15962
  const vault = this.base.getVaultPda(glamState);
15946
15963
  const userMetadata = this.getUserMetadataPda(vault);
15947
15964
  const lookupTable = new web3_js.PublicKey(0); // FIXME: create lookup table
15948
- const referrerUserMetadata = referrer.equals(web3_js.PublicKey.default) ? KAMINO_LENDING_PROGRAM : referrer;
15949
15965
  // @ts-ignore
15950
15966
  const tx = await this.base.program.methods.kaminoLendingInitUserMetadata(lookupTable).accounts({
15951
15967
  glamState,
15952
15968
  glamSigner,
15953
15969
  userMetadata,
15954
- referrerUserMetadata
15970
+ referrerUserMetadata: referrer
15955
15971
  }).transaction();
15956
15972
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
15957
15973
  return vTx;
@@ -15964,7 +15980,7 @@ class KaminoLendingClient {
15964
15980
  switchboardPriceOracle: KAMINO_LENDING_PROGRAM,
15965
15981
  switchboardTwapOracle: KAMINO_LENDING_PROGRAM,
15966
15982
  scopePrices: SCOPE_PRICES
15967
- }, KAMINO_LENDING_PROGRAM));
15983
+ }));
15968
15984
  }
15969
15985
  refreshObligationFarmsForReserveIxs(obligation, lendingMarket, parsedReserves) {
15970
15986
  return parsedReserves.map((parsedReserve)=>{
@@ -15989,18 +16005,21 @@ class KaminoLendingClient {
15989
16005
  farmsProgram: KAMINO_FARM_PROGRAM,
15990
16006
  rent: web3_js.SYSVAR_RENT_PUBKEY,
15991
16007
  systemProgram: web3_js.SystemProgram.programId
15992
- }, KAMINO_LENDING_PROGRAM);
16008
+ });
15993
16009
  });
15994
16010
  }).flat();
15995
16011
  }
15996
- /**
15997
- * Returns an array of instructions for refreshing an existing obligation and reserves it depends on.
15998
- */ async getRefreshIxs(obligation, lendingMarket) {
15999
- // If obligation has deposits or borrows, we need the following refresh ixs:
16000
- // - refreshReserve x N_reserves
16001
- // - refreshObligation
16002
- // - refreshObligationFarmsForReserve x M_farms
16003
- const { deposits, borrows } = await this.fetchAndParseObligation(obligation);
16012
+ // If obligation has deposits or borrows, we need the following refresh ixs:
16013
+ // - refreshReserve x N_reserves
16014
+ // - refreshObligation
16015
+ // - refreshObligationFarmsForReserve x M_farms
16016
+ //
16017
+ // For pricing purpose, we don't need to refresh farm states
16018
+ async getRefreshIxs(obligation, refreshFarms = true) {
16019
+ const { lendingMarket, deposits, borrows } = await this.fetchAndParseObligation(obligation);
16020
+ if (!lendingMarket) {
16021
+ throw new Error("Lending market not found");
16022
+ }
16004
16023
  const reserves = deposits.concat(borrows).map((d)=>d.reserve);
16005
16024
  const parsedReserves = await this.fetchAndParseReserves(reserves);
16006
16025
  return [
@@ -16009,8 +16028,8 @@ class KaminoLendingClient {
16009
16028
  lendingMarket,
16010
16029
  obligation,
16011
16030
  reserves
16012
- }, KAMINO_LENDING_PROGRAM),
16013
- ...this.refreshObligationFarmsForReserveIxs(obligation, lendingMarket, parsedReserves)
16031
+ }),
16032
+ ...refreshFarms ? this.refreshObligationFarmsForReserveIxs(obligation, lendingMarket, parsedReserves) : []
16014
16033
  ];
16015
16034
  }
16016
16035
  getMarketAuthority(market) {
@@ -16061,11 +16080,14 @@ class KaminoLendingClient {
16061
16080
  const obligationAccount = await this.base.provider.connection.getAccountInfo(obligation);
16062
16081
  if (!obligationAccount) {
16063
16082
  return {
16083
+ address: obligation,
16084
+ lendingMarket: null,
16064
16085
  deposits: [],
16065
16086
  borrows: []
16066
16087
  };
16067
16088
  }
16068
16089
  const data = obligationAccount.data;
16090
+ const lendingMarket = new web3_js.PublicKey(data.subarray(32, 64));
16069
16091
  // read deposits
16070
16092
  let depositsOffset = 96;
16071
16093
  let depositSize = 136;
@@ -16075,8 +16097,9 @@ class KaminoLendingClient {
16075
16097
  length: numDeposits
16076
16098
  }, (_, i)=>{
16077
16099
  const depositData = depositsData.subarray(i * depositSize, (i + 1) * depositSize);
16100
+ const reserve = new web3_js.PublicKey(depositData.subarray(0, 32));
16078
16101
  return {
16079
- reserve: new web3_js.PublicKey(depositData.subarray(0, 32))
16102
+ reserve
16080
16103
  };
16081
16104
  }).filter((d)=>!d.reserve.equals(web3_js.PublicKey.default));
16082
16105
  // read borrows
@@ -16088,24 +16111,33 @@ class KaminoLendingClient {
16088
16111
  length: numBorrows
16089
16112
  }, (_, i)=>{
16090
16113
  const borrowData = borrowsData.subarray(i * borrowSize, (i + 1) * borrowSize);
16114
+ const reserve = new web3_js.PublicKey(borrowData.subarray(0, 32));
16091
16115
  return {
16092
- reserve: new web3_js.PublicKey(borrowData.subarray(0, 32))
16116
+ reserve
16093
16117
  };
16094
16118
  }).filter((d)=>!d.reserve.equals(web3_js.PublicKey.default));
16095
16119
  const parsedObligation = {
16120
+ address: obligation,
16121
+ lendingMarket,
16096
16122
  deposits,
16097
16123
  borrows
16098
16124
  };
16099
16125
  this.obligations.set(obligation, parsedObligation);
16100
16126
  return parsedObligation;
16101
16127
  }
16102
- /**
16103
- * We only need pubkeys that don't change over time. No need to fetch them every time.
16104
- *
16105
- * @param market
16106
- * @param asset
16107
- * @returns
16108
- */ async fetchAndParseReserves(reserves) {
16128
+ _parseReserveAccount(data) {
16129
+ const market = new web3_js.PublicKey(data.subarray(32, 64));
16130
+ const farmCollateral = new web3_js.PublicKey(data.subarray(64, 96));
16131
+ const farmDebt = new web3_js.PublicKey(data.subarray(96, 128));
16132
+ const liquidityMint = new web3_js.PublicKey(data.subarray(128, 160));
16133
+ return {
16134
+ farmCollateral: farmCollateral.equals(web3_js.PublicKey.default) ? null : farmCollateral,
16135
+ farmDebt: farmDebt.equals(web3_js.PublicKey.default) ? null : farmDebt,
16136
+ liquidityMint,
16137
+ ...this.reservePdas(market, liquidityMint)
16138
+ };
16139
+ }
16140
+ async fetchAndParseReserves(reserves) {
16109
16141
  if (this.pubkeyArraysEqual(reserves, Array.from(this.reserves.keys()))) {
16110
16142
  return Array.from(this.reserves.values());
16111
16143
  }
@@ -16114,20 +16146,12 @@ class KaminoLendingClient {
16114
16146
  throw new Error("Not all reserves can be found");
16115
16147
  }
16116
16148
  return reserveAccounts.filter((a)=>!!a).map((account, i)=>{
16117
- const data = account.data;
16118
- const market = new web3_js.PublicKey(data.subarray(32, 64));
16119
- const farmCollateral = new web3_js.PublicKey(data.subarray(64, 96));
16120
- const farmDebt = new web3_js.PublicKey(data.subarray(96, 128));
16121
- const liquidityMint = new web3_js.PublicKey(data.subarray(128, 160));
16122
- const parsed = {
16149
+ const parsedReserve = {
16123
16150
  address: reserves[i],
16124
- farmCollateral: farmCollateral.equals(web3_js.PublicKey.default) ? undefined : farmCollateral,
16125
- farmDebt: farmDebt.equals(web3_js.PublicKey.default) ? undefined : farmDebt,
16126
- liquidityMint,
16127
- ...this.reservePdas(market, liquidityMint)
16151
+ ...this._parseReserveAccount(account.data)
16128
16152
  };
16129
- this.reserves.set(reserves[i], parsed);
16130
- return parsed;
16153
+ this.reserves.set(reserves[i], parsedReserve);
16154
+ return parsedReserve;
16131
16155
  });
16132
16156
  }
16133
16157
  async findAndParseReserve(market, asset) {
@@ -16154,18 +16178,12 @@ class KaminoLendingClient {
16154
16178
  throw new Error("Reserve not found");
16155
16179
  }
16156
16180
  const account = accounts[0];
16157
- const data = account.account.data;
16158
- const farmCollateral = new web3_js.PublicKey(data.subarray(64, 96));
16159
- const farmDebt = new web3_js.PublicKey(data.subarray(96, 128));
16160
- const parsed = {
16181
+ const parsedReserve = {
16161
16182
  address: account.pubkey,
16162
- farmCollateral: farmCollateral.equals(web3_js.PublicKey.default) ? undefined : farmCollateral,
16163
- farmDebt: farmDebt.equals(web3_js.PublicKey.default) ? undefined : farmDebt,
16164
- liquidityMint: asset,
16165
- ...this.reservePdas(market, asset)
16183
+ ...this._parseReserveAccount(account.account.data)
16166
16184
  };
16167
- this.reserves.set(account.pubkey, parsed);
16168
- return parsed;
16185
+ this.reserves.set(account.pubkey, parsedReserve);
16186
+ return parsedReserve;
16169
16187
  }
16170
16188
  async depositTx(glamState, market, asset, amount, txOptions) {
16171
16189
  const glamSigner = txOptions.signer || this.base.getSigner();
@@ -16223,7 +16241,7 @@ class KaminoLendingClient {
16223
16241
  lendingMarket: market,
16224
16242
  obligation,
16225
16243
  reserves: reservesInUse
16226
- }, KAMINO_LENDING_PROGRAM));
16244
+ }));
16227
16245
  if (depositReserve.farmCollateral) {
16228
16246
  const ixs = this.refreshObligationFarmsForReserveIxs(obligation, market, [
16229
16247
  depositReserve
@@ -16265,7 +16283,6 @@ class KaminoLendingClient {
16265
16283
  liquidityTokenProgram: splToken.TOKEN_PROGRAM_ID,
16266
16284
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
16267
16285
  obligationFarmUserState: obligationFarm,
16268
- // @ts-ignore
16269
16286
  reserveFarmState: depositReserve.farmCollateral,
16270
16287
  farmsProgram: KAMINO_FARM_PROGRAM
16271
16288
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
@@ -16320,7 +16337,7 @@ class KaminoLendingClient {
16320
16337
  lendingMarket: market,
16321
16338
  obligation,
16322
16339
  reserves: reservesInUse
16323
- }, KAMINO_LENDING_PROGRAM));
16340
+ }));
16324
16341
  if (withdrawReserve.farmCollateral) {
16325
16342
  const ixs = this.refreshObligationFarmsForReserveIxs(obligation, market, [
16326
16343
  withdrawReserve
@@ -16349,7 +16366,6 @@ class KaminoLendingClient {
16349
16366
  liquidityTokenProgram: splToken.TOKEN_PROGRAM_ID,
16350
16367
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
16351
16368
  obligationFarmUserState: obligationFarm,
16352
- // @ts-ignore
16353
16369
  reserveFarmState: withdrawReserve.farmCollateral,
16354
16370
  farmsProgram: KAMINO_FARM_PROGRAM
16355
16371
  }).instruction();
@@ -16411,7 +16427,7 @@ class KaminoLendingClient {
16411
16427
  lendingMarket: market,
16412
16428
  obligation,
16413
16429
  reserves: reservesInUse
16414
- }, KAMINO_LENDING_PROGRAM));
16430
+ }));
16415
16431
  // Don't need to refresh debt farm for borrow
16416
16432
  /*
16417
16433
  if (borrowReserve.farmDebt) {
@@ -16440,7 +16456,6 @@ class KaminoLendingClient {
16440
16456
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
16441
16457
  tokenProgram: splToken.TOKEN_PROGRAM_ID,
16442
16458
  obligationFarmUserState: obligationFarm,
16443
- // @ts-ignore
16444
16459
  reserveFarmState: borrowReserve.farmDebt,
16445
16460
  farmsProgram: KAMINO_FARM_PROGRAM
16446
16461
  }).instruction();
@@ -16500,7 +16515,7 @@ class KaminoLendingClient {
16500
16515
  lendingMarket: market,
16501
16516
  obligation,
16502
16517
  reserves: reservesInUse
16503
- }, KAMINO_LENDING_PROGRAM));
16518
+ }));
16504
16519
  const repayIx = await this.base.program.methods.kaminoLendingRepayObligationLiquidityV2(amount).accounts({
16505
16520
  glamState,
16506
16521
  glamSigner,
@@ -16514,7 +16529,6 @@ class KaminoLendingClient {
16514
16529
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
16515
16530
  tokenProgram: splToken.TOKEN_PROGRAM_ID,
16516
16531
  obligationFarmUserState: obligationFarm,
16517
- // @ts-ignore
16518
16532
  reserveFarmState: repayReserve.farmDebt,
16519
16533
  farmsProgram: KAMINO_FARM_PROGRAM
16520
16534
  }).instruction();
@@ -16545,6 +16559,135 @@ class KaminoLendingClient {
16545
16559
  };
16546
16560
  }
16547
16561
  }
16562
+ class KaminoFarmClient {
16563
+ async findAndParseFarmStates(owner) {
16564
+ const accounts = await this.base.provider.connection.getProgramAccounts(KAMINO_FARM_PROGRAM, {
16565
+ filters: [
16566
+ {
16567
+ dataSize: 920
16568
+ },
16569
+ {
16570
+ memcmp: {
16571
+ offset: 48,
16572
+ bytes: owner.toBase58()
16573
+ }
16574
+ }
16575
+ ]
16576
+ });
16577
+ return accounts.map((account)=>{
16578
+ const data = account.account.data;
16579
+ const farmState = new web3_js.PublicKey(data.subarray(16, 48));
16580
+ return {
16581
+ userFarmState: account.pubkey,
16582
+ farmState
16583
+ };
16584
+ });
16585
+ }
16586
+ async parseFarm(data) {
16587
+ const globalConfig = new web3_js.PublicKey(data.subarray(40, 72));
16588
+ const rewardsOffset = 192;
16589
+ const numRewards = 10;
16590
+ const rewardSize = 704;
16591
+ const rewardsData = data.subarray(rewardsOffset, rewardsOffset + numRewards * rewardSize);
16592
+ const rewards = Array.from({
16593
+ length: numRewards
16594
+ }, (_, i)=>{
16595
+ const rewardData = rewardsData.subarray(i * rewardSize, (i + 1) * rewardSize);
16596
+ const mint = new web3_js.PublicKey(rewardData.subarray(0, 32));
16597
+ const tokenProgram = new web3_js.PublicKey(rewardData.subarray(40, 72));
16598
+ const rewardsVault = new web3_js.PublicKey(rewardData.subarray(120, 152));
16599
+ const minClaimDurationSeconds = new anchor.BN(rewardData.subarray(480, 488), "le");
16600
+ return {
16601
+ index: i,
16602
+ mint,
16603
+ minClaimDurationSeconds,
16604
+ tokenProgram,
16605
+ rewardsVault
16606
+ };
16607
+ }).filter((r)=>{
16608
+ if (r.mint.equals(web3_js.PublicKey.default)) {
16609
+ return false;
16610
+ }
16611
+ // Filter out rewards with minClaimDurationSeconds > 1 year, they are considered disabled
16612
+ if (r.minClaimDurationSeconds.div(new anchor.BN(365 * 24 * 60 * 60)).gt(new anchor.BN(1))) {
16613
+ return false;
16614
+ }
16615
+ return true;
16616
+ });
16617
+ return {
16618
+ globalConfig,
16619
+ rewards
16620
+ };
16621
+ }
16622
+ async fetchAndParseFarms(farms) {
16623
+ const farmAccounts = await this.base.provider.connection.getMultipleAccountsInfo(farms);
16624
+ const map = new Map();
16625
+ for(let i = 0; i < farmAccounts.length; i++){
16626
+ const account = farmAccounts[i];
16627
+ if (!account) {
16628
+ continue;
16629
+ }
16630
+ const data = account.data;
16631
+ const parsedFarm = await this.parseFarm(data);
16632
+ map.set(farms[i].toBase58(), parsedFarm);
16633
+ }
16634
+ return map;
16635
+ }
16636
+ async harvest(statePda, txOptions = {}) {
16637
+ const tx = await this.harvestTx(new web3_js.PublicKey(statePda), txOptions);
16638
+ return await this.base.sendAndConfirm(tx);
16639
+ }
16640
+ async harvestTx(glamState, txOptions = {}) {
16641
+ const glamSigner = txOptions.signer || this.base.getSigner();
16642
+ const vault = this.base.getVaultPda(glamState);
16643
+ const farmStates = await this.findAndParseFarmStates(vault);
16644
+ const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f)=>f.farmState));
16645
+ const tx = new web3_js.Transaction();
16646
+ for (const { userFarmState, farmState } of farmStates){
16647
+ const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
16648
+ for (const { index, mint, tokenProgram, rewardsVault } of rewards){
16649
+ console.log("Reward token:", mint.toBase58());
16650
+ const vaultAta = this.base.getVaultAta(glamState, mint, tokenProgram);
16651
+ const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, vault, mint, tokenProgram);
16652
+ const harvestIx = await this.base.program.methods.kaminoFarmHarvestReward(new anchor.BN(index)).accounts({
16653
+ glamState,
16654
+ glamSigner,
16655
+ userState: userFarmState,
16656
+ farmState,
16657
+ globalConfig,
16658
+ rewardMint: mint,
16659
+ userRewardAta: vaultAta,
16660
+ rewardsVault,
16661
+ rewardsTreasuryVault: this.rewardsTreasuryVault(globalConfig, mint),
16662
+ farmVaultsAuthority: this.farmVaultsAuthority(farmState),
16663
+ scopePrices: null,
16664
+ tokenProgram
16665
+ }).instruction();
16666
+ tx.add(createAtaIx, harvestIx);
16667
+ }
16668
+ }
16669
+ const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
16670
+ LOOKUP_TABLE
16671
+ ]);
16672
+ const vTx = await this.base.intoVersionedTransaction(tx, {
16673
+ ...txOptions,
16674
+ lookupTables
16675
+ });
16676
+ return vTx;
16677
+ }
16678
+ constructor(base){
16679
+ this.base = base;
16680
+ this.farmVaultsAuthority = (farm)=>web3_js.PublicKey.findProgramAddressSync([
16681
+ Buffer.from("authority"),
16682
+ farm.toBuffer()
16683
+ ], KAMINO_FARM_PROGRAM)[0];
16684
+ this.rewardsTreasuryVault = (globalConfig, mint)=>web3_js.PublicKey.findProgramAddressSync([
16685
+ Buffer.from("tvault"),
16686
+ globalConfig.toBuffer(),
16687
+ mint.toBuffer()
16688
+ ], KAMINO_FARM_PROGRAM)[0];
16689
+ }
16690
+ }
16548
16691
 
16549
16692
  // Pubkey::find_program_address(&[b"__event_authority"], &dlmm_interface::ID)
16550
16693
  const EVENT_AUTHORITY = new web3_js.PublicKey("D1ZN9Wj1fRSUQfCjhvnu1hqDMT7hzjzBBpi12nVniYD6");
@@ -17021,6 +17164,28 @@ class PriceClient {
17021
17164
  });
17022
17165
  return pricedAssets.reduce((sum, p)=>new anchor.BN(p.amount).add(sum), new anchor.BN(0));
17023
17166
  }
17167
+ async priceKaminoIxs(glamState, priceDenom) {
17168
+ const glamVault = this.base.getVaultPda(glamState);
17169
+ const obligations = await fetchKaminoObligations(this.base.provider.connection, glamVault);
17170
+ const refreshIxs = [];
17171
+ for (const obligation of obligations){
17172
+ const ixs = await this.klend.getRefreshIxs(obligation, false); // skip farms
17173
+ refreshIxs.push(...ixs);
17174
+ }
17175
+ // @ts-ignore
17176
+ const priceIx = await this.base.program.methods.priceKaminoObligations(priceDenom).accounts({
17177
+ glamState,
17178
+ solOracle: SOL_ORACLE
17179
+ }).remainingAccounts(obligations.map((o)=>({
17180
+ pubkey: o,
17181
+ isSigner: false,
17182
+ isWritable: false
17183
+ }))).instruction();
17184
+ return [
17185
+ ...refreshIxs,
17186
+ priceIx
17187
+ ];
17188
+ }
17024
17189
  async priceVaultIxs(glamState, priceDenom) {
17025
17190
  const vault = this.base.getVaultPda(glamState);
17026
17191
  const tickets = await fetchMarinadeTicketAccounts(this.base.provider.connection, vault);
@@ -17050,29 +17215,18 @@ class PriceClient {
17050
17215
  glamState,
17051
17216
  solOracle: SOL_ORACLE
17052
17217
  }).remainingAccounts(await this.remainingAccountsForPricingMeteora(glamState)).instruction();
17053
- const priceKaminoIx = await this.base.program.methods.priceKaminoObligations(priceDenom).accounts({
17054
- glamState,
17055
- solOracle: SOL_ORACLE
17056
- }).remainingAccounts(await this.remainingAccountsForPricingKamino(glamState)).instruction();
17218
+ const priceKaminoIxs = await this.priceKaminoIxs(glamState, priceDenom);
17057
17219
  return [
17058
17220
  priceTicketsIx,
17059
17221
  priceStakesIx,
17060
17222
  priceVaultIx,
17061
17223
  priceMeteoraIx,
17062
- priceKaminoIx
17224
+ ...priceKaminoIxs
17063
17225
  ];
17064
17226
  }
17065
- constructor(base){
17227
+ constructor(base, klend){
17066
17228
  this.base = base;
17067
- this.remainingAccountsForPricingKamino = async (glamState)=>{
17068
- const glamVault = this.base.getVaultPda(glamState);
17069
- const obligationAccounts = await fetchKaminoObligations(this.base.provider.connection, glamVault);
17070
- return obligationAccounts.map((a)=>({
17071
- pubkey: a,
17072
- isSigner: false,
17073
- isWritable: false
17074
- }));
17075
- };
17229
+ this.klend = klend;
17076
17230
  this.remainingAccountsForPricingMeteora = async (glamState)=>{
17077
17231
  const glamVault = this.base.getVaultPda(glamState);
17078
17232
  const positions = await fetchMeteoraPositions(this.base.provider.connection, glamVault);
@@ -17158,7 +17312,7 @@ class PriceClient {
17158
17312
  }
17159
17313
  get price() {
17160
17314
  if (!this._price) {
17161
- this._price = new PriceClient(this);
17315
+ this._price = new PriceClient(this, this.kaminoLending);
17162
17316
  }
17163
17317
  return this._price;
17164
17318
  }
@@ -17180,6 +17334,12 @@ class PriceClient {
17180
17334
  }
17181
17335
  return this._kaminoLending;
17182
17336
  }
17337
+ get kaminoFarm() {
17338
+ if (!this._kaminoFarm) {
17339
+ this._kaminoFarm = new KaminoFarmClient(this);
17340
+ }
17341
+ return this._kaminoFarm;
17342
+ }
17183
17343
  get meteoraDlmm() {
17184
17344
  if (!this._meteoraDlmm) {
17185
17345
  this._meteoraDlmm = new MeteoraDlmmClient(this);
@@ -17195,7 +17355,7 @@ const getPriorityFeeEstimate = async (heliusApiKey, tx, accountKeys, priorityLev
17195
17355
  if (!tx && !accountKeys) {
17196
17356
  throw new Error("Either tx or accountKeys must be provided");
17197
17357
  }
17198
- const options = priorityLevel ? {
17358
+ const options = priorityLevel && priorityLevel !== "Recommended" ? {
17199
17359
  priorityLevel
17200
17360
  } : {
17201
17361
  recommended: true
@@ -17237,6 +17397,7 @@ exports.DRIFT_PROGRAM_ID = DRIFT_PROGRAM_ID;
17237
17397
  exports.DelegateAcl = DelegateAcl;
17238
17398
  exports.DriftClient = DriftClient;
17239
17399
  exports.FundOpenfundsModel = FundOpenfundsModel;
17400
+ exports.GLAM_REFERRER = GLAM_REFERRER;
17240
17401
  exports.GOVERNANCE_PROGRAM_ID = GOVERNANCE_PROGRAM_ID;
17241
17402
  exports.GlamClient = GlamClient;
17242
17403
  exports.GlamError = GlamError;