@glamsystems/glam-sdk 0.1.33 → 0.1.35

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
@@ -14783,12 +14783,13 @@ class StateModel extends StateIdlModel {
14783
14783
  // @ts-ignore
14784
14784
  const value = Object.values(param.value)[0].val;
14785
14785
  // Ledger is a mint param but we store it on the state model
14786
- if (Object.keys(stateAccount.accountType)[0] === "fund") {
14787
- if (name === "ledger") {
14788
- stateModel["ledger"] = value;
14789
- }
14786
+ if (name === "ledger") {
14787
+ stateModel["ledger"] = value;
14788
+ } else if (name === "redemptionNotifyAndSettle") {
14789
+ mintIdlModel["notifyAndSettle"] = value;
14790
+ } else {
14791
+ mintIdlModel[name] = value;
14790
14792
  }
14791
- mintIdlModel[name] = value;
14792
14793
  });
14793
14794
  if (openfundsMetadataAccount) {
14794
14795
  const mintOpenfundsFields = {};
@@ -15028,6 +15029,82 @@ var ClusterNetwork = /*#__PURE__*/ function(ClusterNetwork) {
15028
15029
  return ClusterNetwork;
15029
15030
  }({});
15030
15031
 
15032
+ // Example response from Helius:
15033
+ // {
15034
+ // "jsonrpc": "2.0",
15035
+ // "id": "1",
15036
+ // "result": {
15037
+ // "accounts": [
15038
+ // {
15039
+ // "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY",
15040
+ // "account": {
15041
+ // "lamports": 15298080,
15042
+ // "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
15043
+ // "data": [
15044
+ // "2R9jLfiAQ9bgdcw6h8s44439",
15045
+ // "base64"
15046
+ // ],
15047
+ // "executable": false,
15048
+ // "rentEpoch": 28,
15049
+ // "space": 165
15050
+ // }
15051
+ // }
15052
+ // ],
15053
+ // "paginationKey": "8WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
15054
+ // "totalResults": 25000
15055
+ // }
15056
+ // }
15057
+ async function getProgramAccountsV2(programId, limit = 100, filters) {
15058
+ const heliusApiKey = process.env.NEXT_PUBLIC_HELIUS_API_KEY || process.env.HELIUS_API_KEY;
15059
+ let allAccounts = [];
15060
+ let paginationKey = null;
15061
+ let encoding = "base64";
15062
+ do {
15063
+ const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`, {
15064
+ method: "POST",
15065
+ headers: {
15066
+ "Content-Type": "application/json"
15067
+ },
15068
+ body: JSON.stringify({
15069
+ jsonrpc: "2.0",
15070
+ id: "1",
15071
+ method: "getProgramAccountsV2",
15072
+ params: [
15073
+ programId.toBase58(),
15074
+ {
15075
+ encoding,
15076
+ filters,
15077
+ limit,
15078
+ ...paginationKey && {
15079
+ paginationKey
15080
+ }
15081
+ }
15082
+ ]
15083
+ })
15084
+ });
15085
+ const data = await response.json();
15086
+ data.result.accounts.forEach(({ pubkey, account })=>{
15087
+ const [acountData, encoding] = account.data;
15088
+ let decodedData;
15089
+ if (encoding === "base64") {
15090
+ decodedData = Buffer.from(acountData, "base64");
15091
+ }
15092
+ if (!decodedData) {
15093
+ throw new Error("Failed to decode base64 account data");
15094
+ }
15095
+ allAccounts.push({
15096
+ pubkey: new web3_js.PublicKey(pubkey),
15097
+ account: {
15098
+ ...account,
15099
+ owner: new web3_js.PublicKey(account.owner),
15100
+ data: decodedData
15101
+ }
15102
+ });
15103
+ });
15104
+ paginationKey = data.result.paginationKey;
15105
+ }while (paginationKey)
15106
+ return allAccounts;
15107
+ }
15031
15108
  /**
15032
15109
  * Fetches all the token accounts owned by the specified pubkey.
15033
15110
  */ async function getTokenAccountsByOwner(connection, owner) {
@@ -19857,7 +19934,6 @@ class MintClient {
19857
19934
  });
19858
19935
  }
19859
19936
  async update(mintModel, txOptions = {}) {
19860
- // @ts-ignore
19861
19937
  const tx = await this.base.program.methods.updateMint(0, new MintIdlModel(mintModel)).accounts({
19862
19938
  glamState: this.base.statePda,
19863
19939
  glamMint: this.base.mintPda
@@ -19881,6 +19957,19 @@ class MintClient {
19881
19957
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
19882
19958
  return await this.base.sendAndConfirm(vTx);
19883
19959
  }
19960
+ async setPermissionlessFulfill(enabled, txOptions = {}) {
19961
+ const stateModel = await this.base.fetchStateModel();
19962
+ const notifyAndSettle = stateModel.mints?.[0]?.notifyAndSettle;
19963
+ if (!notifyAndSettle) {
19964
+ throw new Error("Mint does not have notifyAndSettle configured.");
19965
+ }
19966
+ return await this.update({
19967
+ notifyAndSettle: {
19968
+ ...notifyAndSettle,
19969
+ permissionlessFulfillment: enabled
19970
+ }
19971
+ }, txOptions);
19972
+ }
19884
19973
  async closeMintIx() {
19885
19974
  return await this.base.program.methods.closeMint(0).accounts({
19886
19975
  glamState: this.base.statePda,
@@ -20711,7 +20800,6 @@ class KaminoLendingClient {
20711
20800
  const vault = this.base.vaultPda;
20712
20801
  const userMetadata = this.getUserMetadataPda(vault);
20713
20802
  const lookupTable = new web3_js.PublicKey(0); // FIXME: create lookup table
20714
- // @ts-ignore
20715
20803
  const tx = await this.base.program.methods.kaminoLendingInitUserMetadata(lookupTable).accounts({
20716
20804
  glamState: this.base.statePda,
20717
20805
  glamSigner,
@@ -20758,7 +20846,7 @@ class KaminoLendingClient {
20758
20846
  obligationFarm = this.getObligationFarmState(obligation, depositReserve.farmCollateral);
20759
20847
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20760
20848
  if (!obligationFarmAccount) {
20761
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
20849
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
20762
20850
  .accounts({
20763
20851
  glamState: this.base.statePda,
20764
20852
  glamSigner,
@@ -20796,7 +20884,8 @@ class KaminoLendingClient {
20796
20884
  postInstructions.push(...ixs); // farms must be refreshed after deposit
20797
20885
  }
20798
20886
  // If deposit asset is WSOL, wrap SOL first in case vault doesn't have enough wSOL
20799
- const userSourceLiquidity = this.base.getVaultAta(asset);
20887
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
20888
+ const userSourceLiquidity = this.base.getVaultAta(asset, tokenProgram);
20800
20889
  if (asset.equals(WSOL)) {
20801
20890
  const wrapSolIxs = await this.base.maybeWrapSol(amount);
20802
20891
  preInstructions.unshift(...wrapSolIxs);
@@ -20811,7 +20900,6 @@ class KaminoLendingClient {
20811
20900
  postInstructions.push(closeIx);
20812
20901
  }
20813
20902
  }
20814
- // @ts-ignore
20815
20903
  const tx = await this.base.program.methods.kaminoLendingDepositReserveLiquidityAndObligationCollateralV2(amount).accounts({
20816
20904
  glamState: this.base.statePda,
20817
20905
  glamSigner,
@@ -20826,7 +20914,7 @@ class KaminoLendingClient {
20826
20914
  userSourceLiquidity,
20827
20915
  placeholderUserDestinationCollateral: KAMINO_LENDING_PROGRAM,
20828
20916
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
20829
- liquidityTokenProgram: splToken.TOKEN_PROGRAM_ID,
20917
+ liquidityTokenProgram: tokenProgram,
20830
20918
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
20831
20919
  obligationFarmUserState: obligationFarm,
20832
20920
  reserveFarmState: depositReserve.farmCollateral,
@@ -20848,7 +20936,7 @@ class KaminoLendingClient {
20848
20936
  obligationFarm = this.getObligationFarmState(obligation, withdrawReserve.farmCollateral);
20849
20937
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20850
20938
  if (!obligationFarmAccount) {
20851
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
20939
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
20852
20940
  .accounts({
20853
20941
  glamState: this.base.statePda,
20854
20942
  glamSigner,
@@ -20886,8 +20974,9 @@ class KaminoLendingClient {
20886
20974
  postInstructions.push(...ixs); // farms must be refreshed after withdraw
20887
20975
  }
20888
20976
  // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
20889
- const userDestinationLiquidity = this.base.getVaultAta(asset);
20890
- const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset);
20977
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
20978
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
20979
+ const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
20891
20980
  preInstructions.unshift(createAtaIx);
20892
20981
  const withdrawIx = await this.base.program.methods.kaminoLendingWithdrawObligationCollateralAndRedeemReserveCollateralV2(amount).accounts({
20893
20982
  glamState: this.base.statePda,
@@ -20903,12 +20992,18 @@ class KaminoLendingClient {
20903
20992
  userDestinationLiquidity,
20904
20993
  placeholderUserDestinationCollateral: null,
20905
20994
  collateralTokenProgram: splToken.TOKEN_PROGRAM_ID,
20906
- liquidityTokenProgram: splToken.TOKEN_PROGRAM_ID,
20995
+ liquidityTokenProgram: tokenProgram,
20907
20996
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
20908
20997
  obligationFarmUserState: obligationFarm,
20909
20998
  reserveFarmState: withdrawReserve.farmCollateral,
20910
20999
  farmsProgram: KAMINO_FARM_PROGRAM
20911
- }).instruction();
21000
+ }).remainingAccounts([
21001
+ {
21002
+ pubkey: web3_js.SystemProgram.programId,
21003
+ isSigner: false,
21004
+ isWritable: false
21005
+ }
21006
+ ]).instruction();
20912
21007
  // The final instructions in the tx:
20913
21008
  // - refreshReserve * N
20914
21009
  // - refreshObligation
@@ -20932,7 +21027,7 @@ class KaminoLendingClient {
20932
21027
  obligationFarm = this.getObligationFarmState(obligation, borrowReserve.farmDebt);
20933
21028
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20934
21029
  if (!obligationFarmAccount) {
20935
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
21030
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
20936
21031
  .accounts({
20937
21032
  glamState: this.base.statePda,
20938
21033
  glamSigner,
@@ -20972,8 +21067,9 @@ class KaminoLendingClient {
20972
21067
  postInstructions.push(...ixs); // farms must be refreshed after deposit
20973
21068
  }
20974
21069
  */ // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
20975
- const userDestinationLiquidity = this.base.getVaultAta(asset);
20976
- const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset);
21070
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
21071
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
21072
+ const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
20977
21073
  preInstructions.unshift(createAtaIx);
20978
21074
  const borrowIx = await this.base.program.methods.kaminoLendingBorrowObligationLiquidityV2(amount).accounts({
20979
21075
  glamState: this.base.statePda,
@@ -20988,7 +21084,7 @@ class KaminoLendingClient {
20988
21084
  userDestinationLiquidity,
20989
21085
  referrerTokenState: null,
20990
21086
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
20991
- tokenProgram: splToken.TOKEN_PROGRAM_ID,
21087
+ tokenProgram,
20992
21088
  obligationFarmUserState: obligationFarm,
20993
21089
  reserveFarmState: borrowReserve.farmDebt,
20994
21090
  farmsProgram: KAMINO_FARM_PROGRAM
@@ -21014,7 +21110,7 @@ class KaminoLendingClient {
21014
21110
  obligationFarm = this.getObligationFarmState(obligation, repayReserve.farmDebt);
21015
21111
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
21016
21112
  if (!obligationFarmAccount) {
21017
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
21113
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
21018
21114
  .accounts({
21019
21115
  glamState: this.base.statePda,
21020
21116
  glamSigner,
@@ -21044,6 +21140,7 @@ class KaminoLendingClient {
21044
21140
  obligation,
21045
21141
  reserves: reservesInUse
21046
21142
  }));
21143
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
21047
21144
  const repayIx = await this.base.program.methods.kaminoLendingRepayObligationLiquidityV2(amount).accounts({
21048
21145
  glamState: this.base.statePda,
21049
21146
  glamSigner,
@@ -21053,9 +21150,9 @@ class KaminoLendingClient {
21053
21150
  repayReserve: repayReserve.address,
21054
21151
  reserveLiquidityMint: asset,
21055
21152
  reserveDestinationLiquidity: repayReserve.liquiditySupplyVault,
21056
- userSourceLiquidity: this.base.getVaultAta(asset),
21153
+ userSourceLiquidity: this.base.getVaultAta(asset, tokenProgram),
21057
21154
  instructionSysvarAccount: web3_js.SYSVAR_INSTRUCTIONS_PUBKEY,
21058
- tokenProgram: splToken.TOKEN_PROGRAM_ID,
21155
+ tokenProgram,
21059
21156
  obligationFarmUserState: obligationFarm,
21060
21157
  reserveFarmState: repayReserve.farmDebt,
21061
21158
  farmsProgram: KAMINO_FARM_PROGRAM
@@ -21082,26 +21179,39 @@ class KaminoLendingClient {
21082
21179
  }
21083
21180
  }
21084
21181
  class KaminoFarmClient {
21085
- async findAndParseFarmStates(owner) {
21086
- const accounts = await this.base.provider.connection.getProgramAccounts(KAMINO_FARM_PROGRAM, {
21087
- filters: [
21088
- {
21089
- dataSize: 920
21090
- },
21091
- {
21092
- memcmp: {
21093
- offset: 48,
21094
- bytes: owner.toBase58()
21095
- }
21182
+ async findAndParseUserStates(owner) {
21183
+ const accounts = await getProgramAccountsV2(KAMINO_FARM_PROGRAM, 10, [
21184
+ {
21185
+ dataSize: 920
21186
+ },
21187
+ {
21188
+ memcmp: {
21189
+ offset: 48,
21190
+ bytes: owner.toBase58()
21096
21191
  }
21097
- ]
21098
- });
21099
- return accounts.map((account)=>{
21100
- const data = account.account.data;
21101
- const farmState = new web3_js.PublicKey(data.subarray(16, 48));
21192
+ }
21193
+ ]);
21194
+ return accounts.map(({ pubkey, account })=>{
21195
+ // farmState: [16, 48]
21196
+ // owner: [48, 80]
21197
+ // isFarmDelegated + padding: [80, 88]
21198
+ // rewardsTallyScaled: [88, 248]
21199
+ // unclaimedRewards[0..10]: [248, 328]
21200
+ const farmState = new web3_js.PublicKey(account.data.subarray(16, 48));
21201
+ const rewardsOffset = 248;
21202
+ const numRewards = 10;
21203
+ const rewardSize = 8;
21204
+ const rewardsData = account.data.subarray(rewardsOffset, rewardsOffset + numRewards * rewardSize);
21205
+ const unclaimedRewards = Array.from({
21206
+ length: numRewards
21207
+ }, (_, i)=>{
21208
+ const rewardData = rewardsData.subarray(i * rewardSize, (i + 1) * rewardSize);
21209
+ return new anchor.BN(rewardData, "le");
21210
+ });
21102
21211
  return {
21103
- userFarmState: account.pubkey,
21104
- farmState
21212
+ userState: pubkey,
21213
+ farmState,
21214
+ unclaimedRewards
21105
21215
  };
21106
21216
  });
21107
21217
  }
@@ -21161,20 +21271,23 @@ class KaminoFarmClient {
21161
21271
  }
21162
21272
  async harvestTx(txOptions = {}) {
21163
21273
  const glamSigner = txOptions.signer || this.base.getSigner();
21164
- const vault = this.base.vaultPda;
21165
- const farmStates = await this.findAndParseFarmStates(vault);
21274
+ const farmStates = await this.findAndParseUserStates(this.base.vaultPda);
21166
21275
  const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f)=>f.farmState));
21167
21276
  const tx = new web3_js.Transaction();
21168
- for (const { userFarmState, farmState } of farmStates){
21277
+ console.log("Building transaction to harvest the following rewards:");
21278
+ for (const { userState, farmState, unclaimedRewards } of farmStates){
21169
21279
  const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
21170
21280
  for (const { index, mint, tokenProgram, rewardsVault } of rewards){
21171
- console.log("Reward token:", mint.toBase58());
21281
+ if (unclaimedRewards[index].eq(new anchor.BN(0))) {
21282
+ continue;
21283
+ }
21284
+ console.log(`userState: ${userState}, farmState: ${farmState}, unclaimedReward: ${unclaimedRewards[index]}, token: ${mint}`);
21172
21285
  const vaultAta = this.base.getVaultAta(mint, tokenProgram);
21173
- const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, vault, mint, tokenProgram);
21286
+ const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, this.base.vaultPda, mint, tokenProgram);
21174
21287
  const harvestIx = await this.base.program.methods.kaminoFarmHarvestReward(new anchor.BN(index)).accounts({
21175
21288
  glamState: this.base.statePda,
21176
21289
  glamSigner,
21177
- userState: userFarmState,
21290
+ userState,
21178
21291
  farmState,
21179
21292
  globalConfig,
21180
21293
  rewardMint: mint,
@@ -21188,6 +21301,9 @@ class KaminoFarmClient {
21188
21301
  tx.add(createAtaIx, harvestIx);
21189
21302
  }
21190
21303
  }
21304
+ if (tx.instructions.length === 0) {
21305
+ throw new Error("No rewards to harvest");
21306
+ }
21191
21307
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
21192
21308
  return vTx;
21193
21309
  }
@@ -21597,14 +21713,43 @@ class MeteoraDlmmClient {
21597
21713
  }
21598
21714
 
21599
21715
  class InvestClient {
21600
- async subscribe(asset, amount, mintId = 0, queued = false, txOptions = {}) {
21716
+ /**
21717
+ * Subscribe to a tokenized vault
21718
+ *
21719
+ * @param asset Deposit asset
21720
+ * @param amount
21721
+ * @param mintId
21722
+ * @param queued by default false, set to true to subscribe in queued mode
21723
+ * @param txOptions
21724
+ * @returns
21725
+ */ async subscribe(asset, amount, mintId = 0, queued = false, txOptions = {}) {
21601
21726
  const tx = await (queued ? this.queuedSubscribeTx(asset, amount, mintId, txOptions) : this.subscribeTx(asset, amount, mintId, txOptions));
21602
21727
  return await this.base.sendAndConfirm(tx);
21603
21728
  }
21604
- async queuedRedeem(amount, mintId = 0, txOptions = {}) {
21729
+ /**
21730
+ * Request to redeem share tokens of a tokenized vault in queued mode
21731
+ *
21732
+ * @param amount
21733
+ * @param mintId
21734
+ * @param txOptions
21735
+ * @returns
21736
+ */ async queuedRedeem(amount, mintId = 0, txOptions = {}) {
21605
21737
  const tx = await this.queuedRedeemTx(amount, mintId, txOptions);
21606
21738
  return await this.base.sendAndConfirm(tx);
21607
21739
  }
21740
+ /**
21741
+ * Redeem share tokens of a tokenized vault instantly. Preconditions:
21742
+ * 1. The vault must allow permissionless fulfillment
21743
+ * 2. The vault must have sufficient liquidity
21744
+ *
21745
+ * @param amount
21746
+ * @param mintId
21747
+ * @param txOptions
21748
+ * @returns
21749
+ */ async instantRedeem(amount, mintId = 0, txOptions = {}) {
21750
+ const tx = await this.instantRedeemTx(amount, mintId, txOptions);
21751
+ return await this.base.sendAndConfirm(tx);
21752
+ }
21608
21753
  async fulfill(mintId = 0, txOptions = {}) {
21609
21754
  const tx = await this.fulfillTx(mintId, txOptions);
21610
21755
  return await this.base.sendAndConfirm(tx);
@@ -21652,7 +21797,6 @@ class InvestClient {
21652
21797
  signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21653
21798
  console.log(`signerPolicy: ${signerPolicy} for signer ${signer} and token account ${mintTo}`);
21654
21799
  }
21655
- // @ts-ignore
21656
21800
  const tx = await this.base.program.methods.subscribe(0, amount).accounts({
21657
21801
  glamState: this.base.statePda,
21658
21802
  glamMint,
@@ -21694,6 +21838,58 @@ class InvestClient {
21694
21838
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
21695
21839
  return await this.base.intoVersionedTransaction(tx, txOptions);
21696
21840
  }
21841
+ async instantRedeemTx(amount, mintId = 0, txOptions = {}) {
21842
+ if (mintId !== 0) {
21843
+ throw new Error("mintId must be 0");
21844
+ }
21845
+ // Instant redemption flow is realized by enqueueing a redemption, fulfilling it, and then claiming the tokens in a single transaction.
21846
+ const preInstructions = txOptions.preInstructions || [];
21847
+ const signer = txOptions.signer || this.base.getSigner();
21848
+ const glamMint = this.base.mintPda;
21849
+ const stateModel = await this.base.fetchStateModel();
21850
+ const baseAsset = stateModel.baseAsset;
21851
+ const signerAta = this.base.getAta(baseAsset, signer);
21852
+ const fulfillIx = await this.base.program.methods.fulfill(mintId).accounts({
21853
+ glamState: this.base.statePda,
21854
+ glamMint,
21855
+ signer,
21856
+ asset: baseAsset,
21857
+ depositTokenProgram: splToken.TOKEN_PROGRAM_ID
21858
+ }).instruction();
21859
+ preInstructions.push(splToken.createAssociatedTokenAccountIdempotentInstruction(signer, signerAta, signer, baseAsset));
21860
+ const claimIx = await this.base.program.methods.claim(0).accounts({
21861
+ glamState: this.base.statePda,
21862
+ signer,
21863
+ tokenMint: baseAsset,
21864
+ claimTokenProgram: splToken.TOKEN_PROGRAM_ID
21865
+ }).instruction();
21866
+ const remainingAccounts = [];
21867
+ if (await this.base.isLockupEnabled()) {
21868
+ const extraMetasAccount = this.base.extraMetasPda;
21869
+ const signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21870
+ const escrow = this.base.escrowPda;
21871
+ const escrowPolicy = getAccountPolicyPda(this.base.getMintAta(escrow));
21872
+ remainingAccounts.push(...[
21873
+ extraMetasAccount,
21874
+ signerPolicy,
21875
+ escrowPolicy,
21876
+ TRANSFER_HOOK_PROGRAM
21877
+ ]);
21878
+ }
21879
+ const tx = await this.base.program.methods.queuedRedeem(0, amount).accounts({
21880
+ glamState: this.base.statePda,
21881
+ glamMint,
21882
+ signer
21883
+ }).remainingAccounts(remainingAccounts.map((pubkey)=>({
21884
+ pubkey,
21885
+ isSigner: false,
21886
+ isWritable: false
21887
+ }))).preInstructions(preInstructions).postInstructions([
21888
+ fulfillIx,
21889
+ claimIx
21890
+ ]).transaction();
21891
+ return await this.base.intoVersionedTransaction(tx, txOptions);
21892
+ }
21697
21893
  async queuedRedeemTx(amount, mintId = 0, txOptions = {}) {
21698
21894
  if (mintId !== 0) {
21699
21895
  throw new Error("mintId must be 0");
@@ -22641,6 +22837,7 @@ exports.getMintPda = getMintPda;
22641
22837
  exports.getOpenfundsPda = getOpenfundsPda;
22642
22838
  exports.getOrderParams = getOrderParams;
22643
22839
  exports.getPriorityFeeEstimate = getPriorityFeeEstimate;
22840
+ exports.getProgramAccountsV2 = getProgramAccountsV2;
22644
22841
  exports.getSimulationResult = getSimulationResult;
22645
22842
  exports.getSolAndTokenBalances = getSolAndTokenBalances;
22646
22843
  exports.getStakeAccountsWithStates = getStakeAccountsWithStates;
package/index.esm.js CHANGED
@@ -14763,12 +14763,13 @@ class StateModel extends StateIdlModel {
14763
14763
  // @ts-ignore
14764
14764
  const value = Object.values(param.value)[0].val;
14765
14765
  // Ledger is a mint param but we store it on the state model
14766
- if (Object.keys(stateAccount.accountType)[0] === "fund") {
14767
- if (name === "ledger") {
14768
- stateModel["ledger"] = value;
14769
- }
14766
+ if (name === "ledger") {
14767
+ stateModel["ledger"] = value;
14768
+ } else if (name === "redemptionNotifyAndSettle") {
14769
+ mintIdlModel["notifyAndSettle"] = value;
14770
+ } else {
14771
+ mintIdlModel[name] = value;
14770
14772
  }
14771
- mintIdlModel[name] = value;
14772
14773
  });
14773
14774
  if (openfundsMetadataAccount) {
14774
14775
  const mintOpenfundsFields = {};
@@ -15008,6 +15009,82 @@ var ClusterNetwork = /*#__PURE__*/ function(ClusterNetwork) {
15008
15009
  return ClusterNetwork;
15009
15010
  }({});
15010
15011
 
15012
+ // Example response from Helius:
15013
+ // {
15014
+ // "jsonrpc": "2.0",
15015
+ // "id": "1",
15016
+ // "result": {
15017
+ // "accounts": [
15018
+ // {
15019
+ // "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY",
15020
+ // "account": {
15021
+ // "lamports": 15298080,
15022
+ // "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
15023
+ // "data": [
15024
+ // "2R9jLfiAQ9bgdcw6h8s44439",
15025
+ // "base64"
15026
+ // ],
15027
+ // "executable": false,
15028
+ // "rentEpoch": 28,
15029
+ // "space": 165
15030
+ // }
15031
+ // }
15032
+ // ],
15033
+ // "paginationKey": "8WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
15034
+ // "totalResults": 25000
15035
+ // }
15036
+ // }
15037
+ async function getProgramAccountsV2(programId, limit = 100, filters) {
15038
+ const heliusApiKey = process.env.NEXT_PUBLIC_HELIUS_API_KEY || process.env.HELIUS_API_KEY;
15039
+ let allAccounts = [];
15040
+ let paginationKey = null;
15041
+ let encoding = "base64";
15042
+ do {
15043
+ const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`, {
15044
+ method: "POST",
15045
+ headers: {
15046
+ "Content-Type": "application/json"
15047
+ },
15048
+ body: JSON.stringify({
15049
+ jsonrpc: "2.0",
15050
+ id: "1",
15051
+ method: "getProgramAccountsV2",
15052
+ params: [
15053
+ programId.toBase58(),
15054
+ {
15055
+ encoding,
15056
+ filters,
15057
+ limit,
15058
+ ...paginationKey && {
15059
+ paginationKey
15060
+ }
15061
+ }
15062
+ ]
15063
+ })
15064
+ });
15065
+ const data = await response.json();
15066
+ data.result.accounts.forEach(({ pubkey, account })=>{
15067
+ const [acountData, encoding] = account.data;
15068
+ let decodedData;
15069
+ if (encoding === "base64") {
15070
+ decodedData = Buffer.from(acountData, "base64");
15071
+ }
15072
+ if (!decodedData) {
15073
+ throw new Error("Failed to decode base64 account data");
15074
+ }
15075
+ allAccounts.push({
15076
+ pubkey: new PublicKey(pubkey),
15077
+ account: {
15078
+ ...account,
15079
+ owner: new PublicKey(account.owner),
15080
+ data: decodedData
15081
+ }
15082
+ });
15083
+ });
15084
+ paginationKey = data.result.paginationKey;
15085
+ }while (paginationKey)
15086
+ return allAccounts;
15087
+ }
15011
15088
  /**
15012
15089
  * Fetches all the token accounts owned by the specified pubkey.
15013
15090
  */ async function getTokenAccountsByOwner(connection, owner) {
@@ -19837,7 +19914,6 @@ class MintClient {
19837
19914
  });
19838
19915
  }
19839
19916
  async update(mintModel, txOptions = {}) {
19840
- // @ts-ignore
19841
19917
  const tx = await this.base.program.methods.updateMint(0, new MintIdlModel(mintModel)).accounts({
19842
19918
  glamState: this.base.statePda,
19843
19919
  glamMint: this.base.mintPda
@@ -19861,6 +19937,19 @@ class MintClient {
19861
19937
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
19862
19938
  return await this.base.sendAndConfirm(vTx);
19863
19939
  }
19940
+ async setPermissionlessFulfill(enabled, txOptions = {}) {
19941
+ const stateModel = await this.base.fetchStateModel();
19942
+ const notifyAndSettle = stateModel.mints?.[0]?.notifyAndSettle;
19943
+ if (!notifyAndSettle) {
19944
+ throw new Error("Mint does not have notifyAndSettle configured.");
19945
+ }
19946
+ return await this.update({
19947
+ notifyAndSettle: {
19948
+ ...notifyAndSettle,
19949
+ permissionlessFulfillment: enabled
19950
+ }
19951
+ }, txOptions);
19952
+ }
19864
19953
  async closeMintIx() {
19865
19954
  return await this.base.program.methods.closeMint(0).accounts({
19866
19955
  glamState: this.base.statePda,
@@ -20691,7 +20780,6 @@ class KaminoLendingClient {
20691
20780
  const vault = this.base.vaultPda;
20692
20781
  const userMetadata = this.getUserMetadataPda(vault);
20693
20782
  const lookupTable = new PublicKey(0); // FIXME: create lookup table
20694
- // @ts-ignore
20695
20783
  const tx = await this.base.program.methods.kaminoLendingInitUserMetadata(lookupTable).accounts({
20696
20784
  glamState: this.base.statePda,
20697
20785
  glamSigner,
@@ -20738,7 +20826,7 @@ class KaminoLendingClient {
20738
20826
  obligationFarm = this.getObligationFarmState(obligation, depositReserve.farmCollateral);
20739
20827
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20740
20828
  if (!obligationFarmAccount) {
20741
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
20829
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
20742
20830
  .accounts({
20743
20831
  glamState: this.base.statePda,
20744
20832
  glamSigner,
@@ -20776,7 +20864,8 @@ class KaminoLendingClient {
20776
20864
  postInstructions.push(...ixs); // farms must be refreshed after deposit
20777
20865
  }
20778
20866
  // If deposit asset is WSOL, wrap SOL first in case vault doesn't have enough wSOL
20779
- const userSourceLiquidity = this.base.getVaultAta(asset);
20867
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
20868
+ const userSourceLiquidity = this.base.getVaultAta(asset, tokenProgram);
20780
20869
  if (asset.equals(WSOL)) {
20781
20870
  const wrapSolIxs = await this.base.maybeWrapSol(amount);
20782
20871
  preInstructions.unshift(...wrapSolIxs);
@@ -20791,7 +20880,6 @@ class KaminoLendingClient {
20791
20880
  postInstructions.push(closeIx);
20792
20881
  }
20793
20882
  }
20794
- // @ts-ignore
20795
20883
  const tx = await this.base.program.methods.kaminoLendingDepositReserveLiquidityAndObligationCollateralV2(amount).accounts({
20796
20884
  glamState: this.base.statePda,
20797
20885
  glamSigner,
@@ -20806,7 +20894,7 @@ class KaminoLendingClient {
20806
20894
  userSourceLiquidity,
20807
20895
  placeholderUserDestinationCollateral: KAMINO_LENDING_PROGRAM,
20808
20896
  collateralTokenProgram: TOKEN_PROGRAM_ID,
20809
- liquidityTokenProgram: TOKEN_PROGRAM_ID,
20897
+ liquidityTokenProgram: tokenProgram,
20810
20898
  instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
20811
20899
  obligationFarmUserState: obligationFarm,
20812
20900
  reserveFarmState: depositReserve.farmCollateral,
@@ -20828,7 +20916,7 @@ class KaminoLendingClient {
20828
20916
  obligationFarm = this.getObligationFarmState(obligation, withdrawReserve.farmCollateral);
20829
20917
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20830
20918
  if (!obligationFarmAccount) {
20831
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
20919
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
20832
20920
  .accounts({
20833
20921
  glamState: this.base.statePda,
20834
20922
  glamSigner,
@@ -20866,8 +20954,9 @@ class KaminoLendingClient {
20866
20954
  postInstructions.push(...ixs); // farms must be refreshed after withdraw
20867
20955
  }
20868
20956
  // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
20869
- const userDestinationLiquidity = this.base.getVaultAta(asset);
20870
- const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset);
20957
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
20958
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
20959
+ const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
20871
20960
  preInstructions.unshift(createAtaIx);
20872
20961
  const withdrawIx = await this.base.program.methods.kaminoLendingWithdrawObligationCollateralAndRedeemReserveCollateralV2(amount).accounts({
20873
20962
  glamState: this.base.statePda,
@@ -20883,12 +20972,18 @@ class KaminoLendingClient {
20883
20972
  userDestinationLiquidity,
20884
20973
  placeholderUserDestinationCollateral: null,
20885
20974
  collateralTokenProgram: TOKEN_PROGRAM_ID,
20886
- liquidityTokenProgram: TOKEN_PROGRAM_ID,
20975
+ liquidityTokenProgram: tokenProgram,
20887
20976
  instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
20888
20977
  obligationFarmUserState: obligationFarm,
20889
20978
  reserveFarmState: withdrawReserve.farmCollateral,
20890
20979
  farmsProgram: KAMINO_FARM_PROGRAM
20891
- }).instruction();
20980
+ }).remainingAccounts([
20981
+ {
20982
+ pubkey: SystemProgram.programId,
20983
+ isSigner: false,
20984
+ isWritable: false
20985
+ }
20986
+ ]).instruction();
20892
20987
  // The final instructions in the tx:
20893
20988
  // - refreshReserve * N
20894
20989
  // - refreshObligation
@@ -20912,7 +21007,7 @@ class KaminoLendingClient {
20912
21007
  obligationFarm = this.getObligationFarmState(obligation, borrowReserve.farmDebt);
20913
21008
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20914
21009
  if (!obligationFarmAccount) {
20915
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
21010
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
20916
21011
  .accounts({
20917
21012
  glamState: this.base.statePda,
20918
21013
  glamSigner,
@@ -20952,8 +21047,9 @@ class KaminoLendingClient {
20952
21047
  postInstructions.push(...ixs); // farms must be refreshed after deposit
20953
21048
  }
20954
21049
  */ // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
20955
- const userDestinationLiquidity = this.base.getVaultAta(asset);
20956
- const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset);
21050
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
21051
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
21052
+ const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
20957
21053
  preInstructions.unshift(createAtaIx);
20958
21054
  const borrowIx = await this.base.program.methods.kaminoLendingBorrowObligationLiquidityV2(amount).accounts({
20959
21055
  glamState: this.base.statePda,
@@ -20968,7 +21064,7 @@ class KaminoLendingClient {
20968
21064
  userDestinationLiquidity,
20969
21065
  referrerTokenState: null,
20970
21066
  instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
20971
- tokenProgram: TOKEN_PROGRAM_ID,
21067
+ tokenProgram,
20972
21068
  obligationFarmUserState: obligationFarm,
20973
21069
  reserveFarmState: borrowReserve.farmDebt,
20974
21070
  farmsProgram: KAMINO_FARM_PROGRAM
@@ -20994,7 +21090,7 @@ class KaminoLendingClient {
20994
21090
  obligationFarm = this.getObligationFarmState(obligation, repayReserve.farmDebt);
20995
21091
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
20996
21092
  if (!obligationFarmAccount) {
20997
- preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
21093
+ preInstructions.push(await this.base.program.methods.kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
20998
21094
  .accounts({
20999
21095
  glamState: this.base.statePda,
21000
21096
  glamSigner,
@@ -21024,6 +21120,7 @@ class KaminoLendingClient {
21024
21120
  obligation,
21025
21121
  reserves: reservesInUse
21026
21122
  }));
21123
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
21027
21124
  const repayIx = await this.base.program.methods.kaminoLendingRepayObligationLiquidityV2(amount).accounts({
21028
21125
  glamState: this.base.statePda,
21029
21126
  glamSigner,
@@ -21033,9 +21130,9 @@ class KaminoLendingClient {
21033
21130
  repayReserve: repayReserve.address,
21034
21131
  reserveLiquidityMint: asset,
21035
21132
  reserveDestinationLiquidity: repayReserve.liquiditySupplyVault,
21036
- userSourceLiquidity: this.base.getVaultAta(asset),
21133
+ userSourceLiquidity: this.base.getVaultAta(asset, tokenProgram),
21037
21134
  instructionSysvarAccount: SYSVAR_INSTRUCTIONS_PUBKEY,
21038
- tokenProgram: TOKEN_PROGRAM_ID,
21135
+ tokenProgram,
21039
21136
  obligationFarmUserState: obligationFarm,
21040
21137
  reserveFarmState: repayReserve.farmDebt,
21041
21138
  farmsProgram: KAMINO_FARM_PROGRAM
@@ -21062,26 +21159,39 @@ class KaminoLendingClient {
21062
21159
  }
21063
21160
  }
21064
21161
  class KaminoFarmClient {
21065
- async findAndParseFarmStates(owner) {
21066
- const accounts = await this.base.provider.connection.getProgramAccounts(KAMINO_FARM_PROGRAM, {
21067
- filters: [
21068
- {
21069
- dataSize: 920
21070
- },
21071
- {
21072
- memcmp: {
21073
- offset: 48,
21074
- bytes: owner.toBase58()
21075
- }
21162
+ async findAndParseUserStates(owner) {
21163
+ const accounts = await getProgramAccountsV2(KAMINO_FARM_PROGRAM, 10, [
21164
+ {
21165
+ dataSize: 920
21166
+ },
21167
+ {
21168
+ memcmp: {
21169
+ offset: 48,
21170
+ bytes: owner.toBase58()
21076
21171
  }
21077
- ]
21078
- });
21079
- return accounts.map((account)=>{
21080
- const data = account.account.data;
21081
- const farmState = new PublicKey(data.subarray(16, 48));
21172
+ }
21173
+ ]);
21174
+ return accounts.map(({ pubkey, account })=>{
21175
+ // farmState: [16, 48]
21176
+ // owner: [48, 80]
21177
+ // isFarmDelegated + padding: [80, 88]
21178
+ // rewardsTallyScaled: [88, 248]
21179
+ // unclaimedRewards[0..10]: [248, 328]
21180
+ const farmState = new PublicKey(account.data.subarray(16, 48));
21181
+ const rewardsOffset = 248;
21182
+ const numRewards = 10;
21183
+ const rewardSize = 8;
21184
+ const rewardsData = account.data.subarray(rewardsOffset, rewardsOffset + numRewards * rewardSize);
21185
+ const unclaimedRewards = Array.from({
21186
+ length: numRewards
21187
+ }, (_, i)=>{
21188
+ const rewardData = rewardsData.subarray(i * rewardSize, (i + 1) * rewardSize);
21189
+ return new BN(rewardData, "le");
21190
+ });
21082
21191
  return {
21083
- userFarmState: account.pubkey,
21084
- farmState
21192
+ userState: pubkey,
21193
+ farmState,
21194
+ unclaimedRewards
21085
21195
  };
21086
21196
  });
21087
21197
  }
@@ -21141,20 +21251,23 @@ class KaminoFarmClient {
21141
21251
  }
21142
21252
  async harvestTx(txOptions = {}) {
21143
21253
  const glamSigner = txOptions.signer || this.base.getSigner();
21144
- const vault = this.base.vaultPda;
21145
- const farmStates = await this.findAndParseFarmStates(vault);
21254
+ const farmStates = await this.findAndParseUserStates(this.base.vaultPda);
21146
21255
  const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f)=>f.farmState));
21147
21256
  const tx = new Transaction();
21148
- for (const { userFarmState, farmState } of farmStates){
21257
+ console.log("Building transaction to harvest the following rewards:");
21258
+ for (const { userState, farmState, unclaimedRewards } of farmStates){
21149
21259
  const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
21150
21260
  for (const { index, mint, tokenProgram, rewardsVault } of rewards){
21151
- console.log("Reward token:", mint.toBase58());
21261
+ if (unclaimedRewards[index].eq(new BN(0))) {
21262
+ continue;
21263
+ }
21264
+ console.log(`userState: ${userState}, farmState: ${farmState}, unclaimedReward: ${unclaimedRewards[index]}, token: ${mint}`);
21152
21265
  const vaultAta = this.base.getVaultAta(mint, tokenProgram);
21153
- const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, vault, mint, tokenProgram);
21266
+ const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, this.base.vaultPda, mint, tokenProgram);
21154
21267
  const harvestIx = await this.base.program.methods.kaminoFarmHarvestReward(new BN(index)).accounts({
21155
21268
  glamState: this.base.statePda,
21156
21269
  glamSigner,
21157
- userState: userFarmState,
21270
+ userState,
21158
21271
  farmState,
21159
21272
  globalConfig,
21160
21273
  rewardMint: mint,
@@ -21168,6 +21281,9 @@ class KaminoFarmClient {
21168
21281
  tx.add(createAtaIx, harvestIx);
21169
21282
  }
21170
21283
  }
21284
+ if (tx.instructions.length === 0) {
21285
+ throw new Error("No rewards to harvest");
21286
+ }
21171
21287
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
21172
21288
  return vTx;
21173
21289
  }
@@ -21577,14 +21693,43 @@ class MeteoraDlmmClient {
21577
21693
  }
21578
21694
 
21579
21695
  class InvestClient {
21580
- async subscribe(asset, amount, mintId = 0, queued = false, txOptions = {}) {
21696
+ /**
21697
+ * Subscribe to a tokenized vault
21698
+ *
21699
+ * @param asset Deposit asset
21700
+ * @param amount
21701
+ * @param mintId
21702
+ * @param queued by default false, set to true to subscribe in queued mode
21703
+ * @param txOptions
21704
+ * @returns
21705
+ */ async subscribe(asset, amount, mintId = 0, queued = false, txOptions = {}) {
21581
21706
  const tx = await (queued ? this.queuedSubscribeTx(asset, amount, mintId, txOptions) : this.subscribeTx(asset, amount, mintId, txOptions));
21582
21707
  return await this.base.sendAndConfirm(tx);
21583
21708
  }
21584
- async queuedRedeem(amount, mintId = 0, txOptions = {}) {
21709
+ /**
21710
+ * Request to redeem share tokens of a tokenized vault in queued mode
21711
+ *
21712
+ * @param amount
21713
+ * @param mintId
21714
+ * @param txOptions
21715
+ * @returns
21716
+ */ async queuedRedeem(amount, mintId = 0, txOptions = {}) {
21585
21717
  const tx = await this.queuedRedeemTx(amount, mintId, txOptions);
21586
21718
  return await this.base.sendAndConfirm(tx);
21587
21719
  }
21720
+ /**
21721
+ * Redeem share tokens of a tokenized vault instantly. Preconditions:
21722
+ * 1. The vault must allow permissionless fulfillment
21723
+ * 2. The vault must have sufficient liquidity
21724
+ *
21725
+ * @param amount
21726
+ * @param mintId
21727
+ * @param txOptions
21728
+ * @returns
21729
+ */ async instantRedeem(amount, mintId = 0, txOptions = {}) {
21730
+ const tx = await this.instantRedeemTx(amount, mintId, txOptions);
21731
+ return await this.base.sendAndConfirm(tx);
21732
+ }
21588
21733
  async fulfill(mintId = 0, txOptions = {}) {
21589
21734
  const tx = await this.fulfillTx(mintId, txOptions);
21590
21735
  return await this.base.sendAndConfirm(tx);
@@ -21632,7 +21777,6 @@ class InvestClient {
21632
21777
  signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21633
21778
  console.log(`signerPolicy: ${signerPolicy} for signer ${signer} and token account ${mintTo}`);
21634
21779
  }
21635
- // @ts-ignore
21636
21780
  const tx = await this.base.program.methods.subscribe(0, amount).accounts({
21637
21781
  glamState: this.base.statePda,
21638
21782
  glamMint,
@@ -21674,6 +21818,58 @@ class InvestClient {
21674
21818
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
21675
21819
  return await this.base.intoVersionedTransaction(tx, txOptions);
21676
21820
  }
21821
+ async instantRedeemTx(amount, mintId = 0, txOptions = {}) {
21822
+ if (mintId !== 0) {
21823
+ throw new Error("mintId must be 0");
21824
+ }
21825
+ // Instant redemption flow is realized by enqueueing a redemption, fulfilling it, and then claiming the tokens in a single transaction.
21826
+ const preInstructions = txOptions.preInstructions || [];
21827
+ const signer = txOptions.signer || this.base.getSigner();
21828
+ const glamMint = this.base.mintPda;
21829
+ const stateModel = await this.base.fetchStateModel();
21830
+ const baseAsset = stateModel.baseAsset;
21831
+ const signerAta = this.base.getAta(baseAsset, signer);
21832
+ const fulfillIx = await this.base.program.methods.fulfill(mintId).accounts({
21833
+ glamState: this.base.statePda,
21834
+ glamMint,
21835
+ signer,
21836
+ asset: baseAsset,
21837
+ depositTokenProgram: TOKEN_PROGRAM_ID
21838
+ }).instruction();
21839
+ preInstructions.push(createAssociatedTokenAccountIdempotentInstruction(signer, signerAta, signer, baseAsset));
21840
+ const claimIx = await this.base.program.methods.claim(0).accounts({
21841
+ glamState: this.base.statePda,
21842
+ signer,
21843
+ tokenMint: baseAsset,
21844
+ claimTokenProgram: TOKEN_PROGRAM_ID
21845
+ }).instruction();
21846
+ const remainingAccounts = [];
21847
+ if (await this.base.isLockupEnabled()) {
21848
+ const extraMetasAccount = this.base.extraMetasPda;
21849
+ const signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21850
+ const escrow = this.base.escrowPda;
21851
+ const escrowPolicy = getAccountPolicyPda(this.base.getMintAta(escrow));
21852
+ remainingAccounts.push(...[
21853
+ extraMetasAccount,
21854
+ signerPolicy,
21855
+ escrowPolicy,
21856
+ TRANSFER_HOOK_PROGRAM
21857
+ ]);
21858
+ }
21859
+ const tx = await this.base.program.methods.queuedRedeem(0, amount).accounts({
21860
+ glamState: this.base.statePda,
21861
+ glamMint,
21862
+ signer
21863
+ }).remainingAccounts(remainingAccounts.map((pubkey)=>({
21864
+ pubkey,
21865
+ isSigner: false,
21866
+ isWritable: false
21867
+ }))).preInstructions(preInstructions).postInstructions([
21868
+ fulfillIx,
21869
+ claimIx
21870
+ ]).transaction();
21871
+ return await this.base.intoVersionedTransaction(tx, txOptions);
21872
+ }
21677
21873
  async queuedRedeemTx(amount, mintId = 0, txOptions = {}) {
21678
21874
  if (mintId !== 0) {
21679
21875
  throw new Error("mintId must be 0");
@@ -22497,4 +22693,4 @@ function getMarketOrderParams(params) {
22497
22693
  return Object.assign({}, DefaultOrderParams, optionalOrderParams, overridingParams);
22498
22694
  }
22499
22695
 
22500
- export { ALT_PROGRAM_ID, ASSETS_MAINNET, ASSETS_TESTS, AssetTier, BaseClient, ClusterNetwork, CompanyModel, ContractTier, ContractType, CreatedModel, DRIFT_PROGRAM_ID, DRIFT_VAULTS_PROGRAM_ID, DRIFT_VAULT_DEPOSITOR_SIZE, DefaultOrderParams, DelegateAcl, DepositDirection, DepositExplanation, DriftClient, DriftVaultsClient, ExchangeStatus, FuelOverflowStatus, FundOpenfundsModel, GLAM_REFERRER, GOVERNANCE_PROGRAM_ID, GlamClient, GlamError, GlamIdl, GlamIntegrations, GlamPermissions, GlamProtocolIdlJson, InsuranceFundOperation, JITO_STAKE_POOL, JITO_TIP_DEFAULT, JUP, JUPITER_API_DEFAULT, JUPITER_PROGRAM_ID, JUPSOL_STAKE_POOL, JUP_VOTE_PROGRAM, JupiterSwapClient, JupiterVoteClient, KAMINO_FARM_PROGRAM, KAMINO_LENDING_PROGRAM, KAMINO_OBTRIGATION_SIZE, KAMINO_SCOPE_PRICES, KAMINO_VAULTS_PROGRAM, LPAction, LiquidationType, MARINADE_NATIVE_STAKE_AUTHORITY, MARINADE_PROGRAM_ID, MEMO_PROGRAM, MERKLE_DISTRIBUTOR_PROGRAM, METEORA_DLMM_PROGRAM, METEORA_POSITION_SIZE, MSOL, ManagerModel, MarginMode, MarketStatus, MarketType, Metadata, MintIdlModel, MintModel, MintOpenfundsModel, ModifyOrderPolicy, OracleSource, OracleSourceNum, OrderAction, OrderActionExplanation, OrderStatus, OrderTriggerCondition, OrderType, PerpOperation, PlaceAndTakeOrderSuccessCondition, PositionDirection, PostOnlyParams, PriceDenom, ReferrerStatus, SANCTUM_STAKE_POOL_PROGRAM_ID, SEED_ESCROW, SEED_METADATA, SEED_MINT, SEED_STATE, SEED_VAULT, SOL_ORACLE, STAKE_ACCOUNT_SIZE, STAKE_POOLS, STAKE_POOLS_MAP, SettlePnlExplanation, SettlePnlMode, SpotBalanceType, SpotFulfillmentConfigStatus, SpotFulfillmentStatus, SpotFulfillmentType, SpotOperation, StakeAction, StateIdlModel, StateModel, SwapDirection, SwapReduceOnly, TRANSFER_HOOK_PROGRAM, TimeUnit, TradeSide, USDC, USDC_ORACLE, UserStatus, VoteAuthorize, WSOL, ZERO, decodeUser, fetchMeteoraPositions, fetchProgramLabels, fetchTokenPrices, fetchTokensList, findStakeAccounts, getAccountPolicyPda, getEscrowPda, getExtraMetasPda, getGlamProgram, getGlamProgramId, getLimitOrderParams, getMarketOrderParams, getMintPda, getOpenfundsPda, getOrderParams, getPriorityFeeEstimate, getSimulationResult, getSolAndTokenBalances, getStakeAccountsWithStates, getStatePda, getTokenAccountsByOwner, getTriggerLimitOrderParams, getTriggerMarketOrderParams, getVariant, getVaultPda, isBrowser, isOneOfVariant, isVariant, parseMeteoraPosition, parseProgramLogs, setsAreEqual };
22696
+ export { ALT_PROGRAM_ID, ASSETS_MAINNET, ASSETS_TESTS, AssetTier, BaseClient, ClusterNetwork, CompanyModel, ContractTier, ContractType, CreatedModel, DRIFT_PROGRAM_ID, DRIFT_VAULTS_PROGRAM_ID, DRIFT_VAULT_DEPOSITOR_SIZE, DefaultOrderParams, DelegateAcl, DepositDirection, DepositExplanation, DriftClient, DriftVaultsClient, ExchangeStatus, FuelOverflowStatus, FundOpenfundsModel, GLAM_REFERRER, GOVERNANCE_PROGRAM_ID, GlamClient, GlamError, GlamIdl, GlamIntegrations, GlamPermissions, GlamProtocolIdlJson, InsuranceFundOperation, JITO_STAKE_POOL, JITO_TIP_DEFAULT, JUP, JUPITER_API_DEFAULT, JUPITER_PROGRAM_ID, JUPSOL_STAKE_POOL, JUP_VOTE_PROGRAM, JupiterSwapClient, JupiterVoteClient, KAMINO_FARM_PROGRAM, KAMINO_LENDING_PROGRAM, KAMINO_OBTRIGATION_SIZE, KAMINO_SCOPE_PRICES, KAMINO_VAULTS_PROGRAM, LPAction, LiquidationType, MARINADE_NATIVE_STAKE_AUTHORITY, MARINADE_PROGRAM_ID, MEMO_PROGRAM, MERKLE_DISTRIBUTOR_PROGRAM, METEORA_DLMM_PROGRAM, METEORA_POSITION_SIZE, MSOL, ManagerModel, MarginMode, MarketStatus, MarketType, Metadata, MintIdlModel, MintModel, MintOpenfundsModel, ModifyOrderPolicy, OracleSource, OracleSourceNum, OrderAction, OrderActionExplanation, OrderStatus, OrderTriggerCondition, OrderType, PerpOperation, PlaceAndTakeOrderSuccessCondition, PositionDirection, PostOnlyParams, PriceDenom, ReferrerStatus, SANCTUM_STAKE_POOL_PROGRAM_ID, SEED_ESCROW, SEED_METADATA, SEED_MINT, SEED_STATE, SEED_VAULT, SOL_ORACLE, STAKE_ACCOUNT_SIZE, STAKE_POOLS, STAKE_POOLS_MAP, SettlePnlExplanation, SettlePnlMode, SpotBalanceType, SpotFulfillmentConfigStatus, SpotFulfillmentStatus, SpotFulfillmentType, SpotOperation, StakeAction, StateIdlModel, StateModel, SwapDirection, SwapReduceOnly, TRANSFER_HOOK_PROGRAM, TimeUnit, TradeSide, USDC, USDC_ORACLE, UserStatus, VoteAuthorize, WSOL, ZERO, decodeUser, fetchMeteoraPositions, fetchProgramLabels, fetchTokenPrices, fetchTokensList, findStakeAccounts, getAccountPolicyPda, getEscrowPda, getExtraMetasPda, getGlamProgram, getGlamProgramId, getLimitOrderParams, getMarketOrderParams, getMintPda, getOpenfundsPda, getOrderParams, getPriorityFeeEstimate, getProgramAccountsV2, getSimulationResult, getSolAndTokenBalances, getStakeAccountsWithStates, getStatePda, getTokenAccountsByOwner, getTriggerLimitOrderParams, getTriggerMarketOrderParams, getVariant, getVaultPda, isBrowser, isOneOfVariant, isVariant, parseMeteoraPosition, parseProgramLogs, setsAreEqual };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glamsystems/glam-sdk",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "TypeScript SDK for the GLAM Protocol",
5
5
  "main": "./index.cjs.js",
6
6
  "module": "./index.esm.js",
@@ -4,12 +4,42 @@ import { BaseClient, TxOptions } from "./base";
4
4
  export declare class InvestClient {
5
5
  readonly base: BaseClient;
6
6
  constructor(base: BaseClient);
7
+ /**
8
+ * Subscribe to a tokenized vault
9
+ *
10
+ * @param asset Deposit asset
11
+ * @param amount
12
+ * @param mintId
13
+ * @param queued by default false, set to true to subscribe in queued mode
14
+ * @param txOptions
15
+ * @returns
16
+ */
7
17
  subscribe(asset: PublicKey, amount: BN, mintId?: number, queued?: boolean, txOptions?: TxOptions): Promise<TransactionSignature>;
18
+ /**
19
+ * Request to redeem share tokens of a tokenized vault in queued mode
20
+ *
21
+ * @param amount
22
+ * @param mintId
23
+ * @param txOptions
24
+ * @returns
25
+ */
8
26
  queuedRedeem(amount: BN, mintId?: number, txOptions?: TxOptions): Promise<TransactionSignature>;
27
+ /**
28
+ * Redeem share tokens of a tokenized vault instantly. Preconditions:
29
+ * 1. The vault must allow permissionless fulfillment
30
+ * 2. The vault must have sufficient liquidity
31
+ *
32
+ * @param amount
33
+ * @param mintId
34
+ * @param txOptions
35
+ * @returns
36
+ */
37
+ instantRedeem(amount: BN, mintId?: number, txOptions?: TxOptions): Promise<TransactionSignature>;
9
38
  fulfill(mintId?: number, txOptions?: TxOptions): Promise<TransactionSignature>;
10
39
  claim(asset: PublicKey, mintId?: number, txOptions?: TxOptions): Promise<TransactionSignature>;
11
40
  subscribeTx(asset: PublicKey, amount: BN, mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
12
41
  queuedSubscribeTx(asset: PublicKey, amount: BN, mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
42
+ instantRedeemTx(amount: BN, mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
13
43
  queuedRedeemTx(amount: BN, mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
14
44
  fulfillTx(mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
15
45
  claimAssetTx(asset: PublicKey, mintId?: number, txOptions?: TxOptions): Promise<VersionedTransaction>;
@@ -149,9 +149,10 @@ export declare class KaminoLendingClient {
149
149
  export declare class KaminoFarmClient {
150
150
  readonly base: BaseClient;
151
151
  constructor(base: BaseClient);
152
- findAndParseFarmStates(owner: PublicKey): Promise<{
153
- userFarmState: PublicKey;
152
+ findAndParseUserStates(owner: PublicKey): Promise<{
153
+ userState: any;
154
154
  farmState: PublicKey;
155
+ unclaimedRewards: BN[];
155
156
  }[]>;
156
157
  parseFarm(data: Buffer): Promise<{
157
158
  globalConfig: PublicKey;
@@ -10,6 +10,7 @@ export declare class MintClient {
10
10
  update(mintModel: Partial<MintModel>, txOptions?: TxOptions): Promise<string>;
11
11
  updateApplyTimelock(txOptions?: TxOptions): Promise<string>;
12
12
  emergencyUpdate(mintModel: Partial<MintModel>, txOptions?: TxOptions): Promise<string>;
13
+ setPermissionlessFulfill(enabled: boolean, txOptions?: TxOptions): Promise<string>;
13
14
  closeMintIx(): Promise<anchor.web3.TransactionInstruction>;
14
15
  closeMint(txOptions?: TxOptions): Promise<string>;
15
16
  /**
@@ -6,6 +6,7 @@ export type StakeAccountInfo = {
6
6
  state: string;
7
7
  voter?: PublicKey;
8
8
  };
9
+ export declare function getProgramAccountsV2(programId: PublicKey, limit?: number, filters?: any[]): Promise<any[]>;
9
10
  /**
10
11
  * Fetches all the token accounts owned by the specified pubkey.
11
12
  */