@glamsystems/glam-sdk 0.1.28 → 0.1.30

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/index.cjs.js CHANGED
@@ -34,7 +34,7 @@ var borsh__namespace = /*#__PURE__*/_interopNamespaceDefault(borsh);
34
34
  var address = "GLAMbTqav9N9witRjswJ8enwp9vv5G8bsSJ2kPJ4rcyc";
35
35
  var metadata = {
36
36
  name: "glam_protocol",
37
- version: "0.4.32",
37
+ version: "0.4.35",
38
38
  spec: "0.1.0",
39
39
  description: "Glam Protocol"
40
40
  };
@@ -6900,7 +6900,9 @@ var instructions = [
6900
6900
  docs: [
6901
6901
  "Extra accounts for pricing N vault depositors:",
6902
6902
  "- (vault_depositor, drift_vault, drift_user) x N",
6903
- "- markets and oracles used by all drift users (no specific order)"
6903
+ "- spot_market used by drift users of vaults (no specific order)",
6904
+ "- perp markets used by drift users of vaults (no specific order)",
6905
+ "- oracles of spot markets and perp markets (no specific order)"
6904
6906
  ],
6905
6907
  discriminator: [
6906
6908
  234,
@@ -7021,6 +7023,14 @@ var instructions = [
7021
7023
  {
7022
7024
  name: "num_vault_depositors",
7023
7025
  type: "u8"
7026
+ },
7027
+ {
7028
+ name: "num_spot_markets",
7029
+ type: "u8"
7030
+ },
7031
+ {
7032
+ name: "num_perp_markets",
7033
+ type: "u8"
7024
7034
  }
7025
7035
  ]
7026
7036
  },
@@ -7166,6 +7176,16 @@ var instructions = [
7166
7176
  },
7167
7177
  {
7168
7178
  name: "price_kamino_vault_shares",
7179
+ docs: [
7180
+ "Price Kamino vault shares.",
7181
+ "- `num_vaults` Number of kamino vaults to price.",
7182
+ "",
7183
+ "Extra accounts for pricing N kamino vault shares:",
7184
+ "- (kvault_share_ata, kvault_share_mint, kvault_state, kvault_deposit_token_oracle) x N",
7185
+ "- reserve x M",
7186
+ "- M = number of reserves used by all kvaults' allocations",
7187
+ "- reserve pubkeys must follow the same order of reserves used by each allocation"
7188
+ ],
7169
7189
  discriminator: [
7170
7190
  112,
7171
7191
  92,
@@ -7207,10 +7227,6 @@ var instructions = [
7207
7227
  writable: true,
7208
7228
  signer: true
7209
7229
  },
7210
- {
7211
- name: "kamino_lending_program",
7212
- address: "KLend2g3cP87fffoy8q1mQqGKjrxjC8boSyAYavgmjD"
7213
- },
7214
7230
  {
7215
7231
  name: "sol_oracle"
7216
7232
  },
@@ -7275,22 +7291,6 @@ var instructions = [
7275
7291
  ]
7276
7292
  }
7277
7293
  }
7278
- },
7279
- {
7280
- name: "pyth_oracle",
7281
- optional: true
7282
- },
7283
- {
7284
- name: "switchboard_price_oracle",
7285
- optional: true
7286
- },
7287
- {
7288
- name: "switchboard_twap_oracle",
7289
- optional: true
7290
- },
7291
- {
7292
- name: "scope_prices",
7293
- optional: true
7294
7294
  }
7295
7295
  ],
7296
7296
  args: [
@@ -7301,6 +7301,10 @@ var instructions = [
7301
7301
  name: "PriceDenom"
7302
7302
  }
7303
7303
  }
7304
+ },
7305
+ {
7306
+ name: "num_vaults",
7307
+ type: "u8"
7304
7308
  }
7305
7309
  ]
7306
7310
  },
@@ -7545,16 +7549,22 @@ var instructions = [
7545
7549
  ]
7546
7550
  },
7547
7551
  {
7548
- name: "price_vault",
7552
+ name: "price_vault_tokens",
7553
+ docs: [
7554
+ "Price vault SOL balance and tokens it holds.",
7555
+ "",
7556
+ "Extra accounts for pricing N tokens:",
7557
+ "- (ata, mint, oracle) x N"
7558
+ ],
7549
7559
  discriminator: [
7550
- 47,
7551
- 213,
7552
- 36,
7553
- 17,
7560
+ 54,
7561
+ 42,
7562
+ 16,
7563
+ 199,
7564
+ 20,
7554
7565
  183,
7555
- 5,
7556
- 141,
7557
- 45
7566
+ 50,
7567
+ 137
7558
7568
  ],
7559
7569
  accounts: [
7560
7570
  {
@@ -9593,6 +9603,10 @@ var instructions = [
9593
9603
  {
9594
9604
  name: "stake_history",
9595
9605
  address: "SysvarStakeHistory1111111111111111111111111"
9606
+ },
9607
+ {
9608
+ name: "system_program",
9609
+ address: "11111111111111111111111111111111"
9596
9610
  }
9597
9611
  ],
9598
9612
  args: [
@@ -14561,6 +14575,7 @@ const KAMINO_LENDING_PROGRAM = new web3_js.PublicKey("KLend2g3cP87fffoy8q1mQqGKj
14561
14575
  const KAMINO_VAULTS_PROGRAM = new web3_js.PublicKey("KvauGMspG5k6rtzrqqn7WNn3oZdyKqLKwK2XWQ8FLjd");
14562
14576
  const KAMINO_FARM_PROGRAM = new web3_js.PublicKey("FarmsPZpWu9i7Kky8tPN37rs2TpmMrAZrC7S7vJa91Hr");
14563
14577
  const MEMO_PROGRAM = new web3_js.PublicKey("MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr");
14578
+ const ALT_PROGRAM_ID = new web3_js.PublicKey("AddressLookupTab1e1111111111111111111111111");
14564
14579
  /**
14565
14580
  * Stake pools
14566
14581
  */ const JITO_STAKE_POOL = new web3_js.PublicKey("Jito4APyf642JPZPx3hGc6WWJ8zPKtRbRs4P815Awbb");
@@ -14969,12 +14984,6 @@ var ClusterNetwork = /*#__PURE__*/ function(ClusterNetwork) {
14969
14984
  return ClusterNetwork;
14970
14985
  }({});
14971
14986
 
14972
- const LOOKUP_TABLES_MAP = new Map([
14973
- [
14974
- "3tfbxaHBDjczQo3eyNJGGG64ChZ9nG4V3Gywa4k59d5a",
14975
- new web3_js.PublicKey("8HUXT9abWS2z3z92QyDzg51nMcc18LyWFvaEQZJMPixu")
14976
- ]
14977
- ]);
14978
14987
  const findStakeAccounts = async (connection, withdrawAuthority)=>{
14979
14988
  // stake authority offset: 12
14980
14989
  // withdraw authority offset: 44
@@ -15079,45 +15088,6 @@ const parseMeteoraPosition = async (connection, position)=>{
15079
15088
  binArrayUpper
15080
15089
  };
15081
15090
  };
15082
- async function fetchLookupTables(connection, authority, firstEntry) {
15083
- const ALT_PROGRAM_ID = new web3_js.PublicKey("AddressLookupTab1e1111111111111111111111111");
15084
- const tablePubkey = LOOKUP_TABLES_MAP.get(firstEntry.toBase58());
15085
- if (tablePubkey) {
15086
- const accountInfo = await connection.getAccountInfo(tablePubkey);
15087
- if (accountInfo) {
15088
- return [
15089
- new web3_js.AddressLookupTableAccount({
15090
- key: tablePubkey,
15091
- state: web3_js.AddressLookupTableAccount.deserialize(accountInfo.data)
15092
- })
15093
- ];
15094
- }
15095
- } else {
15096
- // Fetch all accounts owned by the ALT program
15097
- // This is currently disabled due to RPC error "Request deprioritized due to number of accounts requested. Slow down requests or add filters to narrow down results"
15098
- const accounts = await connection.getProgramAccounts(ALT_PROGRAM_ID, {
15099
- filters: [
15100
- {
15101
- memcmp: {
15102
- offset: 22,
15103
- bytes: authority.toBase58()
15104
- }
15105
- },
15106
- {
15107
- memcmp: {
15108
- offset: 56,
15109
- bytes: firstEntry.toBase58()
15110
- }
15111
- }
15112
- ]
15113
- });
15114
- return accounts.map(({ pubkey, account })=>new web3_js.AddressLookupTableAccount({
15115
- key: pubkey,
15116
- state: web3_js.AddressLookupTableAccount.deserialize(account.data)
15117
- }));
15118
- }
15119
- return [];
15120
- }
15121
15091
  /**
15122
15092
  * Parses program logs to extract error message
15123
15093
  */ function parseProgramLogs(logs) {
@@ -15227,6 +15197,15 @@ const STAKE_POOLS = sanctumLstList.LstList.filter((lst)=>!lst.name.includes("San
15227
15197
  poolState: new web3_js.PublicKey(poolState)
15228
15198
  };
15229
15199
  });
15200
+ STAKE_POOLS.push({
15201
+ name: "Phantom Staked SOL",
15202
+ symbol: "PSOL",
15203
+ mint: "pSo1f9nQXWgXibFtKf7NWYxb5enAM4qfP6UJSiXRQfL",
15204
+ decimals: 9,
15205
+ logoURI: "https://assets.phantom.app/assets/metadata/PSOL-512.png",
15206
+ tokenProgram: splToken.TOKEN_PROGRAM_ID,
15207
+ poolState: new web3_js.PublicKey("pSPcvR8GmG9aKDUbn9nbKYjkxt9hxMS7kF1qqKJaPqJ")
15208
+ });
15230
15209
  const STAKE_POOLS_MAP = new Map(STAKE_POOLS.map((p)=>[
15231
15210
  p.mint,
15232
15211
  p
@@ -15527,6 +15506,12 @@ const LOOKUP_TABLES = [
15527
15506
  new web3_js.PublicKey("D9cnvzswDikQDf53k4HpQ3KJ9y1Fv3HGGDFYMXnK5T6c"),
15528
15507
  new web3_js.PublicKey("EiWSskK5HXnBTptiS5DH6gpAJRVNQ3cAhTKBGaiaysAb")
15529
15508
  ];
15509
+ const STATES_LOOKUP_TABLES_MAP = new Map([
15510
+ [
15511
+ "3tfbxaHBDjczQo3eyNJGGG64ChZ9nG4V3Gywa4k59d5a",
15512
+ new web3_js.PublicKey("8HUXT9abWS2z3z92QyDzg51nMcc18LyWFvaEQZJMPixu")
15513
+ ]
15514
+ ]);
15530
15515
  const isBrowser = process.env.ANCHOR_BROWSER || typeof window !== "undefined" && !window.process?.hasOwnProperty("type");
15531
15516
  class BaseClient {
15532
15517
  get detectedCluster() {
@@ -15608,6 +15593,59 @@ class BaseClient {
15608
15593
  })
15609
15594
  ];
15610
15595
  }
15596
+ /**
15597
+ * Fetches lookup tables for the current GLAM instance
15598
+ */ async findLookupTables() {
15599
+ const glamApi = process.env.NEXT_PUBLIC_GLAM_API || process.env.GLAM_API;
15600
+ if (glamApi) {
15601
+ const response = await fetch(`${glamApi}/v0/lut/glam/?state=${this.statePda}`);
15602
+ const data = await response.json();
15603
+ const { t: lookupTables } = data;
15604
+ const pubkeys = Object.keys(lookupTables);
15605
+ if (pubkeys.length > 0) {
15606
+ return await this.fetchAddressLookupTableAccounts(pubkeys);
15607
+ }
15608
+ }
15609
+ const tablePubkey = STATES_LOOKUP_TABLES_MAP.get(this.statePda.toBase58());
15610
+ if (tablePubkey) {
15611
+ return await this.fetchAddressLookupTableAccounts([
15612
+ tablePubkey
15613
+ ]);
15614
+ }
15615
+ // Fetch all accounts owned by the ALT program
15616
+ // This is very likely to hit the RPC error "Request deprioritized due to number of accounts requested. Slow down requests or add filters to narrow down results"
15617
+ const accounts = await this.provider.connection.getProgramAccounts(ALT_PROGRAM_ID, {
15618
+ filters: [
15619
+ {
15620
+ memcmp: {
15621
+ offset: 0,
15622
+ bytes: bytes.bs58.encode([
15623
+ 1,
15624
+ 0,
15625
+ 0,
15626
+ 0
15627
+ ])
15628
+ }
15629
+ },
15630
+ {
15631
+ memcmp: {
15632
+ offset: 56,
15633
+ bytes: this.statePda.toBase58()
15634
+ }
15635
+ },
15636
+ {
15637
+ memcmp: {
15638
+ offset: 88,
15639
+ bytes: this.vaultPda.toBase58()
15640
+ }
15641
+ }
15642
+ ]
15643
+ });
15644
+ return accounts.map(({ pubkey, account })=>new web3_js.AddressLookupTableAccount({
15645
+ key: pubkey,
15646
+ state: web3_js.AddressLookupTableAccount.deserialize(account.data)
15647
+ }));
15648
+ }
15611
15649
  async intoVersionedTransaction(tx, { lookupTables = [], signer, computeUnitLimit, getPriorityFeeMicroLamports, maxFeeLamports, useMaxFee = false, jitoTipLamports, simulate = false }) {
15612
15650
  signer = signer || this.getSigner();
15613
15651
  const instructions = tx.instructions;
@@ -15619,15 +15657,26 @@ class BaseClient {
15619
15657
  lamports: jitoTipLamports
15620
15658
  }));
15621
15659
  }
15622
- lookupTables.push(...await this.getAdressLookupTableAccounts(LOOKUP_TABLES));
15660
+ const lookupTableAccounts = [];
15661
+ if (lookupTables.every((t)=>t instanceof web3_js.AddressLookupTableAccount)) {
15662
+ const accounts = await this.fetchAddressLookupTableAccounts([
15663
+ ...LOOKUP_TABLES
15664
+ ]);
15665
+ lookupTableAccounts.push(...lookupTables, ...accounts);
15666
+ } else {
15667
+ const accounts = await this.fetchAddressLookupTableAccounts([
15668
+ ...lookupTables,
15669
+ ...LOOKUP_TABLES
15670
+ ]);
15671
+ lookupTableAccounts.push(...accounts);
15672
+ }
15623
15673
  const recentBlockhash = (await this.blockhashWithCache.get()).blockhash;
15624
- const { unitsConsumed, error, serializedTx } = await getSimulationResult(this.provider.connection, instructions, signer, lookupTables);
15674
+ const { unitsConsumed, error, serializedTx } = await getSimulationResult(this.provider.connection, instructions, signer, lookupTableAccounts);
15625
15675
  computeUnitLimit = unitsConsumed;
15626
15676
  // by default, a simulation error doesn't prevent the tx from being sent
15627
15677
  // - gui: wallet apps usually do the simulation themselves, we should ignore the simulation error here by default
15628
15678
  // - cli: we should set simulate=true
15629
15679
  if (error && simulate) {
15630
- console.log("Lookup tables:", lookupTables.map((lt)=>lt.key.toString()));
15631
15680
  console.log("Tx (base64):", serializedTx);
15632
15681
  console.error("Simulation failed:", error.message);
15633
15682
  console.error("If error message is too obscure, inspect and simulate the tx in explorer: https://explorer.solana.com/tx/inspector");
@@ -15638,7 +15687,7 @@ class BaseClient {
15638
15687
  payerKey: signer,
15639
15688
  recentBlockhash,
15640
15689
  instructions
15641
- }).compileToV0Message(lookupTables));
15690
+ }).compileToV0Message(lookupTableAccounts));
15642
15691
  const cuIxs = await this.getComputeBudgetIxs(vTx, computeUnitLimit, getPriorityFeeMicroLamports, maxFeeLamports, useMaxFee);
15643
15692
  instructions.unshift(...cuIxs);
15644
15693
  }
@@ -15646,7 +15695,7 @@ class BaseClient {
15646
15695
  payerKey: signer,
15647
15696
  recentBlockhash,
15648
15697
  instructions
15649
- }).compileToV0Message(lookupTables));
15698
+ }).compileToV0Message(lookupTableAccounts));
15650
15699
  }
15651
15700
  async sendAndConfirm(tx, additionalSigners = []) {
15652
15701
  const connection = this.provider.connection;
@@ -15694,13 +15743,21 @@ class BaseClient {
15694
15743
  }
15695
15744
  return txSig;
15696
15745
  }
15697
- async getAdressLookupTableAccounts(keys) {
15698
- if (!keys) {
15746
+ /**
15747
+ * Fetches multiple address lookup table accounts.
15748
+ *
15749
+ * @param pubkeys Array of lookup table public keys.
15750
+ * @returns
15751
+ */ async fetchAddressLookupTableAccounts(pubkeys) {
15752
+ if (!pubkeys) {
15699
15753
  throw new Error("addressLookupTableAddresses is undefined");
15700
15754
  }
15701
- const addressLookupTableAccountInfos = await this.provider.connection.getMultipleAccountsInfo(keys.map((key)=>new web3_js.PublicKey(key)));
15755
+ if (pubkeys.length === 0) {
15756
+ return [];
15757
+ }
15758
+ const addressLookupTableAccountInfos = await this.provider.connection.getMultipleAccountsInfo(pubkeys.map((key)=>new web3_js.PublicKey(key)));
15702
15759
  return addressLookupTableAccountInfos.reduce((acc, accountInfo, index)=>{
15703
- const tableAddress = keys[index];
15760
+ const tableAddress = pubkeys[index];
15704
15761
  if (accountInfo) {
15705
15762
  const tableAccount = new web3_js.AddressLookupTableAccount({
15706
15763
  key: new web3_js.PublicKey(tableAddress),
@@ -17997,7 +18054,7 @@ class JupiterSwapClient {
17997
18054
  swapInstruction = swapInstructions.swapInstruction;
17998
18055
  addressLookupTableAddresses = swapInstructions.addressLookupTableAddresses;
17999
18056
  }
18000
- const lookupTables = await this.base.getAdressLookupTableAccounts(addressLookupTableAddresses);
18057
+ const lookupTables = addressLookupTableAddresses.map((pubkey)=>new web3_js.PublicKey(pubkey));
18001
18058
  const swapIx = this.toTransactionInstruction(swapInstruction, glamVault.toBase58());
18002
18059
  const [inputTokenProgram, outputTokenProgram] = (await this.base.fetchMintsAndTokenPrograms([
18003
18060
  inputMint,
@@ -19699,7 +19756,6 @@ const KVaultStateLayout = borsh.struct([
19699
19756
  borsh.array(borsh.u128(), 242, "padding3")
19700
19757
  ]);
19701
19758
 
19702
- const LOOKUP_TABLE = new web3_js.PublicKey("284iwGtA9X9aLy3KsyV8uT2pXLARhYbiSi5SiM2g47M2");
19703
19759
  const DEFAULT_OBLIGATION_ARGS = {
19704
19760
  tag: 0,
19705
19761
  id: 0
@@ -20344,13 +20400,7 @@ class KaminoLendingClient {
20344
20400
  reserveFarmState: depositReserve.farmCollateral,
20345
20401
  farmsProgram: KAMINO_FARM_PROGRAM
20346
20402
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
20347
- const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
20348
- LOOKUP_TABLE
20349
- ]);
20350
- const vTx = await this.base.intoVersionedTransaction(tx, {
20351
- ...txOptions,
20352
- lookupTables
20353
- });
20403
+ const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
20354
20404
  return vTx;
20355
20405
  }
20356
20406
  async withdrawTx(market, asset, amount, txOptions) {
@@ -20435,13 +20485,7 @@ class KaminoLendingClient {
20435
20485
  // - refreshObligationFarmsForReserve (if farm exists)
20436
20486
  const tx = new web3_js.Transaction();
20437
20487
  tx.add(...preInstructions, withdrawIx, ...postInstructions);
20438
- const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
20439
- LOOKUP_TABLE
20440
- ]);
20441
- const vTx = await this.base.intoVersionedTransaction(tx, {
20442
- ...txOptions,
20443
- lookupTables
20444
- });
20488
+ const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
20445
20489
  return vTx;
20446
20490
  }
20447
20491
  async borrowTx(market, asset, amount, txOptions) {
@@ -20523,13 +20567,7 @@ class KaminoLendingClient {
20523
20567
  // - borrowObligationLiquidityV2
20524
20568
  const tx = new web3_js.Transaction();
20525
20569
  tx.add(...preInstructions, borrowIx);
20526
- const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
20527
- LOOKUP_TABLE
20528
- ]);
20529
- const vTx = await this.base.intoVersionedTransaction(tx, {
20530
- ...txOptions,
20531
- lookupTables
20532
- });
20570
+ const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
20533
20571
  return vTx;
20534
20572
  }
20535
20573
  async repayTx(market, asset, amount, txOptions = {}) {
@@ -20596,13 +20634,7 @@ class KaminoLendingClient {
20596
20634
  // - repayObligationLiquidityV2
20597
20635
  const tx = new web3_js.Transaction();
20598
20636
  tx.add(...preInstructions, repayIx);
20599
- const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
20600
- LOOKUP_TABLE
20601
- ]);
20602
- const vTx = await this.base.intoVersionedTransaction(tx, {
20603
- ...txOptions,
20604
- lookupTables
20605
- });
20637
+ const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
20606
20638
  return vTx;
20607
20639
  }
20608
20640
  constructor(base){
@@ -20724,13 +20756,7 @@ class KaminoFarmClient {
20724
20756
  tx.add(createAtaIx, harvestIx);
20725
20757
  }
20726
20758
  }
20727
- const lookupTables = txOptions.lookupTables || await this.base.getAdressLookupTableAccounts([
20728
- LOOKUP_TABLE
20729
- ]);
20730
- const vTx = await this.base.intoVersionedTransaction(tx, {
20731
- ...txOptions,
20732
- lookupTables
20733
- });
20759
+ const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
20734
20760
  return vTx;
20735
20761
  }
20736
20762
  constructor(base){
@@ -20796,8 +20822,7 @@ class KaminoVaultsClient {
20796
20822
  this.shareMintToVaultPdaMap.set(vaultState.sharesMint.toBase58(), vault);
20797
20823
  return vaultState;
20798
20824
  }
20799
- async composeRemainingAccounts(allocationStrategies) {
20800
- // For each allocation get reserve and market pubkeys
20825
+ async composeRemainingAccounts(allocationStrategies, pricingMode = false) {
20801
20826
  const reserves = allocationStrategies.map((strategy)=>strategy.reserve);
20802
20827
  const parsedReserves = await this.kaminoLending.fetchAndParseReserves(reserves);
20803
20828
  const reserveMetas = reserves.map((pubkey)=>({
@@ -20810,15 +20835,21 @@ class KaminoVaultsClient {
20810
20835
  isSigner: false,
20811
20836
  isWritable: false
20812
20837
  }));
20838
+ if (pricingMode) {
20839
+ // (market, reserve) must be paired
20840
+ return marketMetas.reduce((acc, marketMeta, i)=>{
20841
+ acc.push(marketMeta, reserveMetas[i]);
20842
+ return acc;
20843
+ }, []);
20844
+ }
20813
20845
  return [
20814
20846
  ...reserveMetas,
20815
20847
  ...marketMetas
20816
- ];
20848
+ ]; // Non pricing mode
20817
20849
  }
20818
20850
  async depositTx(vault, amount, txOptions = {}) {
20819
20851
  const glamSigner = txOptions.signer || this.base.getSigner();
20820
20852
  const vaultState = await this.fetchAndParseVaultState(vault);
20821
- const amountBN = amount.mul(new anchor.BN(10).pow(vaultState.tokenMintDecimals));
20822
20853
  const { tokenProgram: sharesTokenProgram } = await this.base.fetchMintAndTokenProgram(vaultState.sharesMint);
20823
20854
  const userTokenAta = this.base.getVaultAta(vaultState.tokenMint, vaultState.tokenProgram);
20824
20855
  const userSharesAta = this.base.getVaultAta(vaultState.sharesMint, sharesTokenProgram);
@@ -20828,7 +20859,7 @@ class KaminoVaultsClient {
20828
20859
  ];
20829
20860
  // Remaining accounts, skip empty allocation strategies
20830
20861
  const remainingAccounts = await this.composeRemainingAccounts(vaultState.vaultAllocationStrategy.filter(({ reserve })=>!reserve.equals(web3_js.PublicKey.default)));
20831
- const tx = await this.base.program.methods.kaminoVaultsDeposit(amountBN).accounts({
20862
+ const tx = await this.base.program.methods.kaminoVaultsDeposit(amount).accounts({
20832
20863
  glamState: this.base.statePda,
20833
20864
  glamSigner,
20834
20865
  vaultState: vault,
@@ -20853,7 +20884,6 @@ class KaminoVaultsClient {
20853
20884
  const userTokenAta = this.base.getVaultAta(vaultState.tokenMint, vaultState.tokenProgram);
20854
20885
  const { tokenProgram: sharesTokenProgram } = await this.base.fetchMintAndTokenProgram(vaultState.sharesMint);
20855
20886
  const userSharesAta = this.base.getVaultAta(vaultState.sharesMint, sharesTokenProgram);
20856
- const amountBN = amount.mul(new anchor.BN(10).pow(vaultState.sharesMintDecimals));
20857
20887
  const reserves = vaultState.vaultAllocationStrategy.filter(({ reserve })=>!reserve.equals(web3_js.PublicKey.default));
20858
20888
  // Withdraw from the first reserve when kvault does not have enough liquidity
20859
20889
  const idx = 0;
@@ -20863,7 +20893,7 @@ class KaminoVaultsClient {
20863
20893
  const preInstructions = [
20864
20894
  splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, userTokenAta, this.base.vaultPda, vaultState.tokenMint, vaultState.tokenProgram)
20865
20895
  ];
20866
- const tx = await this.base.program.methods.kaminoVaultsWithdraw(amountBN).accounts({
20896
+ const tx = await this.base.program.methods.kaminoVaultsWithdraw(amount).accounts({
20867
20897
  glamState: this.base.statePda,
20868
20898
  glamSigner,
20869
20899
  withdrawFromAvailableVaultState: vault,
@@ -21384,36 +21414,63 @@ class PriceClient {
21384
21414
  const shareAtas = [];
21385
21415
  const shareMints = [];
21386
21416
  const kvaultStates = [];
21417
+ const oracles = []; // oracle of kvault deposit token
21387
21418
  possibleShareAtaAccountsInfo.forEach((info, i)=>{
21388
21419
  if (info !== null) {
21389
21420
  shareAtas.push(possibleShareAtas[i]);
21390
21421
  shareMints.push(allKvaultMints[i]);
21391
21422
  kvaultStates.push(allKvaultStates[i]);
21423
+ const { tokenMint } = allKvaultStates[i];
21424
+ const assetMeta = ASSETS_MAINNET.get(tokenMint.toBase58());
21425
+ oracles.push(assetMeta?.oracle);
21392
21426
  }
21393
21427
  });
21394
21428
  const kvaultPdas = await this.kvaults.getVaultPdasByShareMints(shareMints);
21395
- const remainingAccounts = (await Promise.all(kvaultStates.map((kvault)=>{
21396
- return this.kvaults.composeRemainingAccounts(kvault.vaultAllocationStrategy.filter((alloc)=>!alloc.reserve.equals(web3_js.PublicKey.default)));
21397
- }))).flat();
21398
- [
21399
- ...kvaultPdas,
21400
- ...shareAtas
21401
- ].map((pubkey)=>{
21402
- remainingAccounts.unshift({
21403
- pubkey: pubkey,
21404
- isSigner: false,
21405
- isWritable: false
21429
+ const remainingAccounts = [];
21430
+ // first 3N remaining accounts are N tuples of (kvault_shares_ata, kvault_shares_mint, kvault_state)
21431
+ for(let i = 0; i < shareAtas.length; i++){
21432
+ [
21433
+ shareAtas[i],
21434
+ shareMints[i],
21435
+ kvaultPdas[i],
21436
+ oracles[i]
21437
+ ].map((pubkey)=>{
21438
+ remainingAccounts.push({
21439
+ pubkey: pubkey,
21440
+ isSigner: false,
21441
+ isWritable: false
21442
+ });
21406
21443
  });
21407
- });
21408
- const priceIx = await this.base.program.methods.priceKaminoVaultShares(priceDenom).accounts({
21444
+ }
21445
+ const marketsAndReserves = (await Promise.all(kvaultStates.map((kvault)=>{
21446
+ return this.kvaults.composeRemainingAccounts(kvault.vaultAllocationStrategy.filter((alloc)=>!alloc.reserve.equals(web3_js.PublicKey.default)), true);
21447
+ }))).flat();
21448
+ const processed = new Set();
21449
+ const preInstructions = [];
21450
+ const chunkSize = 2;
21451
+ for(let i = 0; i < marketsAndReserves.length; i += chunkSize){
21452
+ const chunk = marketsAndReserves.slice(i, i + chunkSize);
21453
+ const market = chunk[0].pubkey;
21454
+ const reserve = chunk[1].pubkey;
21455
+ // reserve should always be added to remaining accounts
21456
+ remainingAccounts.push(chunk[1]);
21457
+ // each reserve should only be refreshed once
21458
+ if (!processed.has(reserve.toBase58())) {
21459
+ const ix = this.klend.refreshReserveIxs(market, [
21460
+ reserve
21461
+ ]);
21462
+ preInstructions.push(...ix);
21463
+ processed.add(reserve.toBase58());
21464
+ }
21465
+ }
21466
+ const priceIx = await this.base.program.methods.priceKaminoVaultShares(priceDenom, shareAtas.length).accounts({
21409
21467
  glamState: this.base.statePda,
21410
- solOracle: SOL_ORACLE,
21411
- pythOracle: null,
21412
- switchboardPriceOracle: null,
21413
- switchboardTwapOracle: null,
21414
- scopePrices: KAMINO_SCOPE_PRICES
21468
+ solOracle: SOL_ORACLE
21415
21469
  }).remainingAccounts(remainingAccounts).instruction();
21416
- return priceIx;
21470
+ return [
21471
+ ...preInstructions,
21472
+ priceIx
21473
+ ];
21417
21474
  }
21418
21475
  /**
21419
21476
  * Returns an instruction that prices all Drift users (aka sub-accounts) controlled by the GLAM vault.
@@ -21481,43 +21538,8 @@ class PriceClient {
21481
21538
  if (parsedVaultDepositors.length === 0) {
21482
21539
  return null;
21483
21540
  }
21484
- // For each vault deposit, we need the following pubkeys in remaining accounts:
21485
- // - depositor
21486
- // - drift vault
21487
- // - drift user of the vault
21488
- // - oracles
21489
- // - spot & perp markets
21490
- // There might be overlaps between markets and oracles so we use a set to avoid duplicates
21491
- const remainingAccounts = [];
21492
- const marketsAndOracles = new Set();
21493
- for (const depositor of parsedVaultDepositors){
21494
- const { user: driftUser } = await this.dvaults.parseDriftVault(depositor.driftVault);
21495
- remainingAccounts.push({
21496
- pubkey: depositor.address,
21497
- isSigner: false,
21498
- isWritable: false
21499
- });
21500
- remainingAccounts.push({
21501
- pubkey: depositor.driftVault,
21502
- isSigner: false,
21503
- isWritable: false
21504
- });
21505
- remainingAccounts.push({
21506
- pubkey: driftUser,
21507
- isSigner: false,
21508
- isWritable: false
21509
- });
21510
- const markets_and_oracles = (await this.dvaults.composeRemainingAccounts(driftUser)).map((a)=>a.pubkey.toBase58());
21511
- for (const k of markets_and_oracles){
21512
- marketsAndOracles.add(k);
21513
- }
21514
- }
21515
- Array.from(marketsAndOracles).forEach((k)=>remainingAccounts.push({
21516
- pubkey: new web3_js.PublicKey(k),
21517
- isSigner: false,
21518
- isWritable: false
21519
- }));
21520
- const priceIx = await this.base.program.methods.priceDriftVaultDepositors(priceDenom, parsedVaultDepositors.length).accounts({
21541
+ const { remainingAccounts, numSpotMarkets, numPerpMarkets } = await this.remainingAccountsForPricingDriftVaultDepositors(parsedVaultDepositors);
21542
+ const priceIx = await this.base.program.methods.priceDriftVaultDepositors(priceDenom, parsedVaultDepositors.length, numSpotMarkets, numPerpMarkets).accounts({
21521
21543
  glamState: this.base.statePda,
21522
21544
  solOracle: SOL_ORACLE
21523
21545
  }).remainingAccounts(remainingAccounts).instruction();
@@ -21527,7 +21549,7 @@ class PriceClient {
21527
21549
  * Returns an instruction that prices vault balance and tokens the vault holds
21528
21550
  */ async priceVaultIx(priceDenom) {
21529
21551
  const remainingAccounts = await this.remainingAccountsForPricingVaultAssets(priceDenom == PriceDenom.ASSET);
21530
- const priceVaultIx = await this.base.program.methods.priceVault(priceDenom).accounts({
21552
+ const priceVaultIx = await this.base.program.methods.priceVaultTokens(priceDenom).accounts({
21531
21553
  glamState: this.base.statePda,
21532
21554
  solOracle: SOL_ORACLE
21533
21555
  }).remainingAccounts(remainingAccounts).instruction();
@@ -21584,83 +21606,142 @@ class PriceClient {
21584
21606
  }
21585
21607
  const integrations = (stateModel.integrations || []).map((i)=>Object.keys(i)[0]);
21586
21608
  const integrationsToPricingFns = {
21587
- drift: this.priceDriftUsersIx.bind(this),
21588
- kaminoLending: this.priceKaminoObligationsIx.bind(this),
21589
- nativeStaking: this.priceStakesIx.bind(this),
21590
- meteoraDlmm: this.priceMeteoraPositionsIx.bind(this),
21591
- driftVaults: this.priceDriftVaultDepositorsIx.bind(this),
21592
- kaminoVaults: this.priceKaminoVaultSharesIx.bind(this)
21609
+ // drift: this.priceDriftUsersIx.bind(this),
21610
+ // kaminoLending: this.priceKaminoObligationsIx.bind(this),
21611
+ // nativeStaking: this.priceStakesIx.bind(this),
21612
+ // meteoraDlmm: this.priceMeteoraPositionsIx.bind(this),
21613
+ driftVaults: this.priceDriftVaultDepositorsIx.bind(this)
21593
21614
  };
21594
21615
  const pricingFns = integrations.map((integration)=>integrationsToPricingFns[integration]).filter(Boolean);
21595
- const pricingIxs = [];
21616
+ const pricingIxs = [
21617
+ priceVaultIx
21618
+ ];
21596
21619
  for (const fn of pricingFns){
21597
21620
  const ix = await fn(priceDenom);
21598
- pricingIxs.push(ix);
21621
+ if (Array.isArray(ix)) {
21622
+ pricingIxs.push(...ix);
21623
+ } else {
21624
+ pricingIxs.push(ix);
21625
+ }
21599
21626
  }
21600
21627
  return pricingIxs.filter(Boolean);
21601
21628
  }
21629
+ async remainingAccountsForPricingDriftVaultDepositors(parsedVaultDepositors) {
21630
+ // Extra accounts for pricing N vault depositors:
21631
+ // - (vault_depositor, drift_vault, drift_user) x N
21632
+ // - spot_market used by drift users of vaults (no specific order)
21633
+ // - perp markets used by drift users of vaults (no specific order)
21634
+ // - oracles of spot markets and perp markets (no specific order)
21635
+ const remainingAccounts = [];
21636
+ const spotMarketsSet = new Set();
21637
+ const perpMarketsSet = new Set();
21638
+ const oraclesSet = new Set();
21639
+ for (const { address: depositor, driftVault } of parsedVaultDepositors){
21640
+ const { user } = await this.dvaults.parseDriftVault(driftVault); // get drift user used by the vault
21641
+ [
21642
+ depositor,
21643
+ driftVault,
21644
+ user
21645
+ ].forEach((k)=>remainingAccounts.push({
21646
+ pubkey: k,
21647
+ isSigner: false,
21648
+ isWritable: false
21649
+ }));
21650
+ const { spotPositions, perpPositions } = await this.dvaults.fetchUserPositions(user);
21651
+ const spotMarketIndexes = spotPositions.map((p)=>p.marketIndex);
21652
+ const perpMarketIndexes = perpPositions.map((p)=>p.marketIndex);
21653
+ // If there are perp positions, add spot market 0 as it's used as quote market for perp
21654
+ if (perpMarketIndexes.length > 0 && !spotMarketIndexes.includes(0)) {
21655
+ spotMarketIndexes.push(0);
21656
+ }
21657
+ const spotMarkets = await this.drift.fetchAndParseSpotMarkets(spotMarketIndexes);
21658
+ const perpMarkets = await this.drift.fetchAndParsePerpMarkets(perpMarketIndexes);
21659
+ spotMarkets.forEach((m)=>{
21660
+ oraclesSet.add(m.oracle.toBase58());
21661
+ spotMarketsSet.add(m.marketPda.toBase58());
21662
+ });
21663
+ perpMarkets.forEach((m)=>{
21664
+ oraclesSet.add(m.oracle.toBase58());
21665
+ perpMarketsSet.add(m.marketPda.toBase58());
21666
+ });
21667
+ }
21668
+ [
21669
+ ...spotMarketsSet,
21670
+ ...perpMarketsSet,
21671
+ ...oraclesSet
21672
+ ].forEach((k)=>remainingAccounts.push({
21673
+ pubkey: new web3_js.PublicKey(k),
21674
+ isSigner: false,
21675
+ isWritable: false
21676
+ }));
21677
+ return {
21678
+ remainingAccounts,
21679
+ numSpotMarkets: spotMarketsSet.size,
21680
+ numPerpMarkets: perpMarketsSet.size
21681
+ };
21682
+ }
21683
+ async remainingAccountsForPricingMeteora() {
21684
+ const positions = await fetchMeteoraPositions(this.base.provider.connection, this.base.vaultPda);
21685
+ let chunks = await Promise.all(positions.map(async (pubkey)=>{
21686
+ const { lbPair, binArrayLower, binArrayUpper } = await parseMeteoraPosition(this.base.provider.connection, pubkey);
21687
+ return [
21688
+ pubkey,
21689
+ lbPair,
21690
+ binArrayLower,
21691
+ binArrayUpper,
21692
+ SOL_ORACLE,
21693
+ USDC_ORACLE
21694
+ ].map((k)=>({
21695
+ pubkey: k,
21696
+ isSigner: false,
21697
+ isWritable: false
21698
+ }));
21699
+ }));
21700
+ return chunks.flat();
21701
+ }
21702
+ async remainingAccountsForPricingVaultAssets(baseAssetOnly = false) {
21703
+ const stateModel = await this.base.fetchStateModel();
21704
+ if (baseAssetOnly) {
21705
+ if (!stateModel.baseAsset) {
21706
+ throw new Error("Base asset not configured for the vault");
21707
+ }
21708
+ // FIXME: support token 2022 base asset
21709
+ const ata = this.base.getVaultAta(stateModel.baseAsset);
21710
+ // Set oracle to default pubkey (aka system program) to indicate oracle isn't needed
21711
+ return [
21712
+ ata,
21713
+ stateModel.baseAsset,
21714
+ web3_js.PublicKey.default
21715
+ ].map((k)=>({
21716
+ pubkey: k,
21717
+ isSigner: false,
21718
+ isWritable: false
21719
+ }));
21720
+ }
21721
+ const assetsForPricing = (stateModel.borrowableAssets || []).concat(stateModel.assets || []);
21722
+ return assetsForPricing.map((mint)=>{
21723
+ const assetMeta = ASSETS_MAINNET.get(mint.toBase58());
21724
+ if (!assetMeta) {
21725
+ throw new Error(`Asset meta not found for ${mint}`);
21726
+ }
21727
+ const ata = this.base.getVaultAta(mint, assetMeta?.programId);
21728
+ return [
21729
+ ata,
21730
+ mint,
21731
+ assetMeta?.oracle
21732
+ ];
21733
+ }).flat().map((a)=>({
21734
+ pubkey: a,
21735
+ isSigner: false,
21736
+ isWritable: false
21737
+ }));
21738
+ }
21602
21739
  constructor(base, klend, kvaults, drift, dvaults){
21603
21740
  this.base = base;
21604
21741
  this.klend = klend;
21605
21742
  this.kvaults = kvaults;
21606
21743
  this.drift = drift;
21607
21744
  this.dvaults = dvaults;
21608
- this.remainingAccountsForPricingMeteora = async ()=>{
21609
- const positions = await fetchMeteoraPositions(this.base.provider.connection, this.base.vaultPda);
21610
- let chunks = await Promise.all(positions.map(async (pubkey)=>{
21611
- const { lbPair, binArrayLower, binArrayUpper } = await parseMeteoraPosition(this.base.provider.connection, pubkey);
21612
- return [
21613
- pubkey,
21614
- lbPair,
21615
- binArrayLower,
21616
- binArrayUpper,
21617
- SOL_ORACLE,
21618
- USDC_ORACLE
21619
- ].map((k)=>({
21620
- pubkey: k,
21621
- isSigner: false,
21622
- isWritable: false
21623
- }));
21624
- }));
21625
- return chunks.flat();
21626
- };
21627
- this.remainingAccountsForPricingVaultAssets = async (baseAssetOnly = false)=>{
21628
- const stateModel = await this.base.fetchStateModel();
21629
- if (baseAssetOnly) {
21630
- if (!stateModel.baseAsset) {
21631
- throw new Error("Base asset not configured for the vault");
21632
- }
21633
- // FIXME: support token 2022 base asset
21634
- const ata = this.base.getVaultAta(stateModel.baseAsset);
21635
- // Set oracle to default pubkey (aka system program) to indicate oracle isn't needed
21636
- return [
21637
- ata,
21638
- stateModel.baseAsset,
21639
- web3_js.PublicKey.default
21640
- ].map((k)=>({
21641
- pubkey: k,
21642
- isSigner: false,
21643
- isWritable: false
21644
- }));
21645
- }
21646
- const assetsForPricing = (stateModel.borrowableAssets || []).concat(stateModel.assets || []);
21647
- return assetsForPricing.map((mint)=>{
21648
- const assetMeta = ASSETS_MAINNET.get(mint.toBase58());
21649
- if (!assetMeta) {
21650
- throw new Error(`Asset meta not found for ${mint}`);
21651
- }
21652
- const ata = this.base.getVaultAta(mint, assetMeta?.programId);
21653
- return [
21654
- ata,
21655
- mint,
21656
- assetMeta?.oracle
21657
- ];
21658
- }).flat().map((a)=>({
21659
- pubkey: a,
21660
- isSigner: false,
21661
- isWritable: false
21662
- }));
21663
- };
21664
21745
  }
21665
21746
  }
21666
21747
 
@@ -21957,6 +22038,7 @@ function getMarketOrderParams(params) {
21957
22038
  return Object.assign({}, DefaultOrderParams, optionalOrderParams, overridingParams);
21958
22039
  }
21959
22040
 
22041
+ exports.ALT_PROGRAM_ID = ALT_PROGRAM_ID;
21960
22042
  exports.ASSETS_MAINNET = ASSETS_MAINNET;
21961
22043
  exports.ASSETS_TESTS = ASSETS_TESTS;
21962
22044
  exports.AssetTier = AssetTier;
@@ -22064,7 +22146,6 @@ exports.VoteAuthorize = VoteAuthorize;
22064
22146
  exports.WSOL = WSOL;
22065
22147
  exports.ZERO = ZERO;
22066
22148
  exports.decodeUser = decodeUser;
22067
- exports.fetchLookupTables = fetchLookupTables;
22068
22149
  exports.fetchMeteoraPositions = fetchMeteoraPositions;
22069
22150
  exports.fetchProgramLabels = fetchProgramLabels;
22070
22151
  exports.fetchTokenPrices = fetchTokenPrices;