@glamsystems/glam-sdk 0.1.34 → 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,
@@ -21090,26 +21179,39 @@ class KaminoLendingClient {
21090
21179
  }
21091
21180
  }
21092
21181
  class KaminoFarmClient {
21093
- async findAndParseFarmStates(owner) {
21094
- const accounts = await this.base.provider.connection.getProgramAccounts(KAMINO_FARM_PROGRAM, {
21095
- filters: [
21096
- {
21097
- dataSize: 920
21098
- },
21099
- {
21100
- memcmp: {
21101
- offset: 48,
21102
- bytes: owner.toBase58()
21103
- }
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()
21104
21191
  }
21105
- ]
21106
- });
21107
- return accounts.map((account)=>{
21108
- const data = account.account.data;
21109
- 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
+ });
21110
21211
  return {
21111
- userFarmState: account.pubkey,
21112
- farmState
21212
+ userState: pubkey,
21213
+ farmState,
21214
+ unclaimedRewards
21113
21215
  };
21114
21216
  });
21115
21217
  }
@@ -21169,20 +21271,23 @@ class KaminoFarmClient {
21169
21271
  }
21170
21272
  async harvestTx(txOptions = {}) {
21171
21273
  const glamSigner = txOptions.signer || this.base.getSigner();
21172
- const vault = this.base.vaultPda;
21173
- const farmStates = await this.findAndParseFarmStates(vault);
21274
+ const farmStates = await this.findAndParseUserStates(this.base.vaultPda);
21174
21275
  const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f)=>f.farmState));
21175
21276
  const tx = new web3_js.Transaction();
21176
- for (const { userFarmState, farmState } of farmStates){
21277
+ console.log("Building transaction to harvest the following rewards:");
21278
+ for (const { userState, farmState, unclaimedRewards } of farmStates){
21177
21279
  const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
21178
21280
  for (const { index, mint, tokenProgram, rewardsVault } of rewards){
21179
- 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}`);
21180
21285
  const vaultAta = this.base.getVaultAta(mint, tokenProgram);
21181
- const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, vault, mint, tokenProgram);
21286
+ const createAtaIx = splToken.createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, this.base.vaultPda, mint, tokenProgram);
21182
21287
  const harvestIx = await this.base.program.methods.kaminoFarmHarvestReward(new anchor.BN(index)).accounts({
21183
21288
  glamState: this.base.statePda,
21184
21289
  glamSigner,
21185
- userState: userFarmState,
21290
+ userState,
21186
21291
  farmState,
21187
21292
  globalConfig,
21188
21293
  rewardMint: mint,
@@ -21196,6 +21301,9 @@ class KaminoFarmClient {
21196
21301
  tx.add(createAtaIx, harvestIx);
21197
21302
  }
21198
21303
  }
21304
+ if (tx.instructions.length === 0) {
21305
+ throw new Error("No rewards to harvest");
21306
+ }
21199
21307
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
21200
21308
  return vTx;
21201
21309
  }
@@ -21605,14 +21713,43 @@ class MeteoraDlmmClient {
21605
21713
  }
21606
21714
 
21607
21715
  class InvestClient {
21608
- 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 = {}) {
21609
21726
  const tx = await (queued ? this.queuedSubscribeTx(asset, amount, mintId, txOptions) : this.subscribeTx(asset, amount, mintId, txOptions));
21610
21727
  return await this.base.sendAndConfirm(tx);
21611
21728
  }
21612
- 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 = {}) {
21613
21737
  const tx = await this.queuedRedeemTx(amount, mintId, txOptions);
21614
21738
  return await this.base.sendAndConfirm(tx);
21615
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
+ }
21616
21753
  async fulfill(mintId = 0, txOptions = {}) {
21617
21754
  const tx = await this.fulfillTx(mintId, txOptions);
21618
21755
  return await this.base.sendAndConfirm(tx);
@@ -21660,7 +21797,6 @@ class InvestClient {
21660
21797
  signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21661
21798
  console.log(`signerPolicy: ${signerPolicy} for signer ${signer} and token account ${mintTo}`);
21662
21799
  }
21663
- // @ts-ignore
21664
21800
  const tx = await this.base.program.methods.subscribe(0, amount).accounts({
21665
21801
  glamState: this.base.statePda,
21666
21802
  glamMint,
@@ -21702,6 +21838,58 @@ class InvestClient {
21702
21838
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
21703
21839
  return await this.base.intoVersionedTransaction(tx, txOptions);
21704
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
+ }
21705
21893
  async queuedRedeemTx(amount, mintId = 0, txOptions = {}) {
21706
21894
  if (mintId !== 0) {
21707
21895
  throw new Error("mintId must be 0");
@@ -22649,6 +22837,7 @@ exports.getMintPda = getMintPda;
22649
22837
  exports.getOpenfundsPda = getOpenfundsPda;
22650
22838
  exports.getOrderParams = getOrderParams;
22651
22839
  exports.getPriorityFeeEstimate = getPriorityFeeEstimate;
22840
+ exports.getProgramAccountsV2 = getProgramAccountsV2;
22652
22841
  exports.getSimulationResult = getSimulationResult;
22653
22842
  exports.getSolAndTokenBalances = getSolAndTokenBalances;
22654
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,
@@ -21070,26 +21159,39 @@ class KaminoLendingClient {
21070
21159
  }
21071
21160
  }
21072
21161
  class KaminoFarmClient {
21073
- async findAndParseFarmStates(owner) {
21074
- const accounts = await this.base.provider.connection.getProgramAccounts(KAMINO_FARM_PROGRAM, {
21075
- filters: [
21076
- {
21077
- dataSize: 920
21078
- },
21079
- {
21080
- memcmp: {
21081
- offset: 48,
21082
- bytes: owner.toBase58()
21083
- }
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()
21084
21171
  }
21085
- ]
21086
- });
21087
- return accounts.map((account)=>{
21088
- const data = account.account.data;
21089
- 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
+ });
21090
21191
  return {
21091
- userFarmState: account.pubkey,
21092
- farmState
21192
+ userState: pubkey,
21193
+ farmState,
21194
+ unclaimedRewards
21093
21195
  };
21094
21196
  });
21095
21197
  }
@@ -21149,20 +21251,23 @@ class KaminoFarmClient {
21149
21251
  }
21150
21252
  async harvestTx(txOptions = {}) {
21151
21253
  const glamSigner = txOptions.signer || this.base.getSigner();
21152
- const vault = this.base.vaultPda;
21153
- const farmStates = await this.findAndParseFarmStates(vault);
21254
+ const farmStates = await this.findAndParseUserStates(this.base.vaultPda);
21154
21255
  const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f)=>f.farmState));
21155
21256
  const tx = new Transaction();
21156
- for (const { userFarmState, farmState } of farmStates){
21257
+ console.log("Building transaction to harvest the following rewards:");
21258
+ for (const { userState, farmState, unclaimedRewards } of farmStates){
21157
21259
  const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
21158
21260
  for (const { index, mint, tokenProgram, rewardsVault } of rewards){
21159
- 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}`);
21160
21265
  const vaultAta = this.base.getVaultAta(mint, tokenProgram);
21161
- const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, vault, mint, tokenProgram);
21266
+ const createAtaIx = createAssociatedTokenAccountIdempotentInstruction(glamSigner, vaultAta, this.base.vaultPda, mint, tokenProgram);
21162
21267
  const harvestIx = await this.base.program.methods.kaminoFarmHarvestReward(new BN(index)).accounts({
21163
21268
  glamState: this.base.statePda,
21164
21269
  glamSigner,
21165
- userState: userFarmState,
21270
+ userState,
21166
21271
  farmState,
21167
21272
  globalConfig,
21168
21273
  rewardMint: mint,
@@ -21176,6 +21281,9 @@ class KaminoFarmClient {
21176
21281
  tx.add(createAtaIx, harvestIx);
21177
21282
  }
21178
21283
  }
21284
+ if (tx.instructions.length === 0) {
21285
+ throw new Error("No rewards to harvest");
21286
+ }
21179
21287
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
21180
21288
  return vTx;
21181
21289
  }
@@ -21585,14 +21693,43 @@ class MeteoraDlmmClient {
21585
21693
  }
21586
21694
 
21587
21695
  class InvestClient {
21588
- 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 = {}) {
21589
21706
  const tx = await (queued ? this.queuedSubscribeTx(asset, amount, mintId, txOptions) : this.subscribeTx(asset, amount, mintId, txOptions));
21590
21707
  return await this.base.sendAndConfirm(tx);
21591
21708
  }
21592
- 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 = {}) {
21593
21717
  const tx = await this.queuedRedeemTx(amount, mintId, txOptions);
21594
21718
  return await this.base.sendAndConfirm(tx);
21595
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
+ }
21596
21733
  async fulfill(mintId = 0, txOptions = {}) {
21597
21734
  const tx = await this.fulfillTx(mintId, txOptions);
21598
21735
  return await this.base.sendAndConfirm(tx);
@@ -21640,7 +21777,6 @@ class InvestClient {
21640
21777
  signerPolicy = getAccountPolicyPda(this.base.getMintAta(signer));
21641
21778
  console.log(`signerPolicy: ${signerPolicy} for signer ${signer} and token account ${mintTo}`);
21642
21779
  }
21643
- // @ts-ignore
21644
21780
  const tx = await this.base.program.methods.subscribe(0, amount).accounts({
21645
21781
  glamState: this.base.statePda,
21646
21782
  glamMint,
@@ -21682,6 +21818,58 @@ class InvestClient {
21682
21818
  }).preInstructions(preInstructions).postInstructions(postInstructions).transaction();
21683
21819
  return await this.base.intoVersionedTransaction(tx, txOptions);
21684
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
+ }
21685
21873
  async queuedRedeemTx(amount, mintId = 0, txOptions = {}) {
21686
21874
  if (mintId !== 0) {
21687
21875
  throw new Error("mintId must be 0");
@@ -22505,4 +22693,4 @@ function getMarketOrderParams(params) {
22505
22693
  return Object.assign({}, DefaultOrderParams, optionalOrderParams, overridingParams);
22506
22694
  }
22507
22695
 
22508
- 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.34",
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
  */