@glamsystems/glam-cli 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.
Files changed (2) hide show
  1. package/main.js +284 -51
  2. package/package.json +2 -2
package/main.js CHANGED
@@ -275,12 +275,15 @@ class StateModel extends StateIdlModel {
275
275
  // @ts-ignore
276
276
  const value = Object.values(param.value)[0].val;
277
277
  // Ledger is a mint param but we store it on the state model
278
- if (Object.keys(stateAccount.accountType)[0] === "fund") {
279
- if (name === "ledger") {
280
- stateModel["ledger"] = value;
281
- }
278
+ if (name === "ledger") {
279
+ stateModel["ledger"] = value;
280
+ }
281
+ else if (name === "redemptionNotifyAndSettle") {
282
+ mintIdlModel["notifyAndSettle"] = value;
283
+ }
284
+ else {
285
+ mintIdlModel[name] = value;
282
286
  }
283
- mintIdlModel[name] = value;
284
287
  });
285
288
  if (openfundsMetadataAccount) {
286
289
  const mintOpenfundsFields = {};
@@ -763,7 +766,7 @@ const blockhash_1 = __webpack_require__(20);
763
766
  const glamPDAs_1 = __webpack_require__(21);
764
767
  const DEFAULT_PRIORITY_FEE = 10000; // microLamports
765
768
  const LOOKUP_TABLES = [
766
- new web3_js_1.PublicKey("284iwGtA9X9aLy3KsyV8uT2pXLARhYbiSi5SiM2g47M2"), // kamino
769
+ new web3_js_1.PublicKey("284iwGtA9X9aLy3KsyV8uT2pXLARhYbiSi5SiM2g47M2"), // kamino lending
767
770
  new web3_js_1.PublicKey("D9cnvzswDikQDf53k4HpQ3KJ9y1Fv3HGGDFYMXnK5T6c"), // drift
768
771
  new web3_js_1.PublicKey("EiWSskK5HXnBTptiS5DH6gpAJRVNQ3cAhTKBGaiaysAb"), // drift
769
772
  ];
@@ -1353,6 +1356,7 @@ exports.BaseClient = BaseClient;
1353
1356
 
1354
1357
  Object.defineProperty(exports, "__esModule", ({ value: true }));
1355
1358
  exports.setsAreEqual = exports.getSimulationResult = exports.parseMeteoraPosition = exports.fetchMeteoraPositions = exports.getStakeAccountsWithStates = exports.findStakeAccounts = void 0;
1359
+ exports.getProgramAccountsV2 = getProgramAccountsV2;
1356
1360
  exports.getTokenAccountsByOwner = getTokenAccountsByOwner;
1357
1361
  exports.getSolAndTokenBalances = getSolAndTokenBalances;
1358
1362
  exports.parseProgramLogs = parseProgramLogs;
@@ -1362,6 +1366,78 @@ const dlmm_1 = __webpack_require__(15);
1362
1366
  const anchor_1 = __webpack_require__(4);
1363
1367
  const glamExports_1 = __webpack_require__(3);
1364
1368
  const spl_token_1 = __webpack_require__(8);
1369
+ // Example response from Helius:
1370
+ // {
1371
+ // "jsonrpc": "2.0",
1372
+ // "id": "1",
1373
+ // "result": {
1374
+ // "accounts": [
1375
+ // {
1376
+ // "pubkey": "CxELquR1gPP8wHe33gZ4QxqGB3sZ9RSwsJ2KshVewkFY",
1377
+ // "account": {
1378
+ // "lamports": 15298080,
1379
+ // "owner": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
1380
+ // "data": [
1381
+ // "2R9jLfiAQ9bgdcw6h8s44439",
1382
+ // "base64"
1383
+ // ],
1384
+ // "executable": false,
1385
+ // "rentEpoch": 28,
1386
+ // "space": 165
1387
+ // }
1388
+ // }
1389
+ // ],
1390
+ // "paginationKey": "8WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM",
1391
+ // "totalResults": 25000
1392
+ // }
1393
+ // }
1394
+ async function getProgramAccountsV2(programId, limit = 100, filters) {
1395
+ const heliusApiKey = process.env.NEXT_PUBLIC_HELIUS_API_KEY || process.env.HELIUS_API_KEY;
1396
+ let allAccounts = [];
1397
+ let paginationKey = null;
1398
+ let encoding = "base64";
1399
+ do {
1400
+ const response = await fetch(`https://mainnet.helius-rpc.com/?api-key=${heliusApiKey}`, {
1401
+ method: "POST",
1402
+ headers: { "Content-Type": "application/json" },
1403
+ body: JSON.stringify({
1404
+ jsonrpc: "2.0",
1405
+ id: "1",
1406
+ method: "getProgramAccountsV2",
1407
+ params: [
1408
+ programId.toBase58(),
1409
+ {
1410
+ encoding,
1411
+ filters,
1412
+ limit,
1413
+ ...(paginationKey && { paginationKey }),
1414
+ },
1415
+ ],
1416
+ }),
1417
+ });
1418
+ const data = await response.json();
1419
+ data.result.accounts.forEach(({ pubkey, account }) => {
1420
+ const [acountData, encoding] = account.data;
1421
+ let decodedData;
1422
+ if (encoding === "base64") {
1423
+ decodedData = Buffer.from(acountData, "base64");
1424
+ }
1425
+ if (!decodedData) {
1426
+ throw new Error("Failed to decode base64 account data");
1427
+ }
1428
+ allAccounts.push({
1429
+ pubkey: new web3_js_1.PublicKey(pubkey),
1430
+ account: {
1431
+ ...account,
1432
+ owner: new web3_js_1.PublicKey(account.owner),
1433
+ data: decodedData,
1434
+ },
1435
+ });
1436
+ });
1437
+ paginationKey = data.result.paginationKey;
1438
+ } while (paginationKey);
1439
+ return allAccounts;
1440
+ }
1365
1441
  /**
1366
1442
  * Fetches all the token accounts owned by the specified pubkey.
1367
1443
  */
@@ -5823,7 +5899,6 @@ class MintClient {
5823
5899
  });
5824
5900
  }
5825
5901
  async update(mintModel, txOptions = {}) {
5826
- // @ts-ignore
5827
5902
  const tx = await this.base.program.methods
5828
5903
  .updateMint(0, new models_1.MintIdlModel(mintModel))
5829
5904
  .accounts({
@@ -5856,6 +5931,19 @@ class MintClient {
5856
5931
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
5857
5932
  return await this.base.sendAndConfirm(vTx);
5858
5933
  }
5934
+ async setPermissionlessFulfill(enabled, txOptions = {}) {
5935
+ const stateModel = await this.base.fetchStateModel();
5936
+ const notifyAndSettle = stateModel.mints?.[0]?.notifyAndSettle;
5937
+ if (!notifyAndSettle) {
5938
+ throw new Error("Mint does not have notifyAndSettle configured.");
5939
+ }
5940
+ return await this.update({
5941
+ notifyAndSettle: {
5942
+ ...notifyAndSettle,
5943
+ permissionlessFulfillment: enabled,
5944
+ },
5945
+ }, txOptions);
5946
+ }
5859
5947
  async closeMintIx() {
5860
5948
  return await this.base.program.methods
5861
5949
  .closeMint(0)
@@ -6087,6 +6175,7 @@ const borsh = tslib_1.__importStar(__webpack_require__(25));
6087
6175
  const spl_token_1 = __webpack_require__(8);
6088
6176
  const constants_1 = __webpack_require__(10);
6089
6177
  const kaminoLayouts_1 = __webpack_require__(36);
6178
+ const helpers_1 = __webpack_require__(14);
6090
6179
  const DEFAULT_OBLIGATION_ARGS = { tag: 0, id: 0 };
6091
6180
  const EVENT_AUTHORITY = new web3_js_1.PublicKey("24tHwQyJJ9akVXxnvkekGfAoeUJXXS7mE6kQNioNySsK");
6092
6181
  class KaminoLendingClient {
@@ -6496,7 +6585,6 @@ class KaminoLendingClient {
6496
6585
  const vault = this.base.vaultPda;
6497
6586
  const userMetadata = this.getUserMetadataPda(vault);
6498
6587
  const lookupTable = new web3_js_1.PublicKey(0); // FIXME: create lookup table
6499
- // @ts-ignore
6500
6588
  const tx = await this.base.program.methods
6501
6589
  .kaminoLendingInitUserMetadata(lookupTable)
6502
6590
  .accounts({
@@ -6553,7 +6641,7 @@ class KaminoLendingClient {
6553
6641
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
6554
6642
  if (!obligationFarmAccount) {
6555
6643
  preInstructions.push(await this.base.program.methods
6556
- .kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
6644
+ .kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
6557
6645
  .accounts({
6558
6646
  glamState: this.base.statePda,
6559
6647
  glamSigner,
@@ -6591,7 +6679,8 @@ class KaminoLendingClient {
6591
6679
  postInstructions.push(...ixs); // farms must be refreshed after deposit
6592
6680
  }
6593
6681
  // If deposit asset is WSOL, wrap SOL first in case vault doesn't have enough wSOL
6594
- const userSourceLiquidity = this.base.getVaultAta(asset);
6682
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
6683
+ const userSourceLiquidity = this.base.getVaultAta(asset, tokenProgram);
6595
6684
  if (asset.equals(constants_1.WSOL)) {
6596
6685
  const wrapSolIxs = await this.base.maybeWrapSol(amount);
6597
6686
  preInstructions.unshift(...wrapSolIxs);
@@ -6609,7 +6698,6 @@ class KaminoLendingClient {
6609
6698
  postInstructions.push(closeIx);
6610
6699
  }
6611
6700
  }
6612
- // @ts-ignore
6613
6701
  const tx = await this.base.program.methods
6614
6702
  .kaminoLendingDepositReserveLiquidityAndObligationCollateralV2(amount)
6615
6703
  .accounts({
@@ -6626,7 +6714,7 @@ class KaminoLendingClient {
6626
6714
  userSourceLiquidity,
6627
6715
  placeholderUserDestinationCollateral: constants_1.KAMINO_LENDING_PROGRAM,
6628
6716
  collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6629
- liquidityTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6717
+ liquidityTokenProgram: tokenProgram,
6630
6718
  instructionSysvarAccount: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
6631
6719
  obligationFarmUserState: obligationFarm,
6632
6720
  reserveFarmState: depositReserve.farmCollateral,
@@ -6652,7 +6740,7 @@ class KaminoLendingClient {
6652
6740
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
6653
6741
  if (!obligationFarmAccount) {
6654
6742
  preInstructions.push(await this.base.program.methods
6655
- .kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
6743
+ .kaminoLendingInitObligationFarmsForReserve(0) // 0 - collateral farm
6656
6744
  .accounts({
6657
6745
  glamState: this.base.statePda,
6658
6746
  glamSigner,
@@ -6690,8 +6778,9 @@ class KaminoLendingClient {
6690
6778
  postInstructions.push(...ixs); // farms must be refreshed after withdraw
6691
6779
  }
6692
6780
  // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
6693
- const userDestinationLiquidity = this.base.getVaultAta(asset);
6694
- const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, userDestinationLiquidity, vault, asset);
6781
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
6782
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
6783
+ const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
6695
6784
  preInstructions.unshift(createAtaIx);
6696
6785
  const withdrawIx = await this.base.program.methods
6697
6786
  .kaminoLendingWithdrawObligationCollateralAndRedeemReserveCollateralV2(amount)
@@ -6709,12 +6798,19 @@ class KaminoLendingClient {
6709
6798
  userDestinationLiquidity,
6710
6799
  placeholderUserDestinationCollateral: null,
6711
6800
  collateralTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6712
- liquidityTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6801
+ liquidityTokenProgram: tokenProgram,
6713
6802
  instructionSysvarAccount: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
6714
6803
  obligationFarmUserState: obligationFarm,
6715
6804
  reserveFarmState: withdrawReserve.farmCollateral,
6716
6805
  farmsProgram: constants_1.KAMINO_FARM_PROGRAM,
6717
6806
  })
6807
+ .remainingAccounts([
6808
+ {
6809
+ pubkey: web3_js_1.SystemProgram.programId,
6810
+ isSigner: false,
6811
+ isWritable: false,
6812
+ },
6813
+ ])
6718
6814
  .instruction();
6719
6815
  // The final instructions in the tx:
6720
6816
  // - refreshReserve * N
@@ -6741,7 +6837,7 @@ class KaminoLendingClient {
6741
6837
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
6742
6838
  if (!obligationFarmAccount) {
6743
6839
  preInstructions.push(await this.base.program.methods
6744
- .kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
6840
+ .kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
6745
6841
  .accounts({
6746
6842
  glamState: this.base.statePda,
6747
6843
  glamSigner,
@@ -6784,8 +6880,9 @@ class KaminoLendingClient {
6784
6880
  }
6785
6881
  */
6786
6882
  // Create asset ATA in case it doesn't exist. Add it to the beginning of preInstructions
6787
- const userDestinationLiquidity = this.base.getVaultAta(asset);
6788
- const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, userDestinationLiquidity, vault, asset);
6883
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
6884
+ const userDestinationLiquidity = this.base.getVaultAta(asset, tokenProgram);
6885
+ const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, userDestinationLiquidity, vault, asset, tokenProgram);
6789
6886
  preInstructions.unshift(createAtaIx);
6790
6887
  const borrowIx = await this.base.program.methods
6791
6888
  .kaminoLendingBorrowObligationLiquidityV2(amount)
@@ -6802,7 +6899,7 @@ class KaminoLendingClient {
6802
6899
  userDestinationLiquidity,
6803
6900
  referrerTokenState: null,
6804
6901
  instructionSysvarAccount: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
6805
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6902
+ tokenProgram,
6806
6903
  obligationFarmUserState: obligationFarm,
6807
6904
  reserveFarmState: borrowReserve.farmDebt,
6808
6905
  farmsProgram: constants_1.KAMINO_FARM_PROGRAM,
@@ -6830,7 +6927,7 @@ class KaminoLendingClient {
6830
6927
  const obligationFarmAccount = await this.base.provider.connection.getAccountInfo(obligationFarm);
6831
6928
  if (!obligationFarmAccount) {
6832
6929
  preInstructions.push(await this.base.program.methods
6833
- .kaminoLendingInitObligationFarmsForReserve(0) // TODO: What does mode do?
6930
+ .kaminoLendingInitObligationFarmsForReserve(1) // 1 - debt farm
6834
6931
  .accounts({
6835
6932
  glamState: this.base.statePda,
6836
6933
  glamSigner,
@@ -6862,6 +6959,7 @@ class KaminoLendingClient {
6862
6959
  obligation,
6863
6960
  reserves: reservesInUse,
6864
6961
  }));
6962
+ const { tokenProgram } = await this.base.fetchMintAndTokenProgram(asset);
6865
6963
  const repayIx = await this.base.program.methods
6866
6964
  .kaminoLendingRepayObligationLiquidityV2(amount)
6867
6965
  .accounts({
@@ -6873,9 +6971,9 @@ class KaminoLendingClient {
6873
6971
  repayReserve: repayReserve.address,
6874
6972
  reserveLiquidityMint: asset,
6875
6973
  reserveDestinationLiquidity: repayReserve.liquiditySupplyVault,
6876
- userSourceLiquidity: this.base.getVaultAta(asset),
6974
+ userSourceLiquidity: this.base.getVaultAta(asset, tokenProgram),
6877
6975
  instructionSysvarAccount: web3_js_1.SYSVAR_INSTRUCTIONS_PUBKEY,
6878
- tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
6976
+ tokenProgram,
6879
6977
  obligationFarmUserState: obligationFarm,
6880
6978
  reserveFarmState: repayReserve.farmDebt,
6881
6979
  farmsProgram: constants_1.KAMINO_FARM_PROGRAM,
@@ -6898,19 +6996,30 @@ class KaminoFarmClient {
6898
6996
  this.farmVaultsAuthority = (farm) => web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("authority"), farm.toBuffer()], constants_1.KAMINO_FARM_PROGRAM)[0];
6899
6997
  this.rewardsTreasuryVault = (globalConfig, mint) => web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("tvault"), globalConfig.toBuffer(), mint.toBuffer()], constants_1.KAMINO_FARM_PROGRAM)[0];
6900
6998
  }
6901
- async findAndParseFarmStates(owner) {
6902
- const accounts = await this.base.provider.connection.getProgramAccounts(constants_1.KAMINO_FARM_PROGRAM, {
6903
- filters: [
6904
- { dataSize: 920 },
6905
- { memcmp: { offset: 48, bytes: owner.toBase58() } },
6906
- ],
6907
- });
6908
- return accounts.map((account) => {
6909
- const data = account.account.data;
6910
- const farmState = new web3_js_1.PublicKey(data.subarray(16, 48));
6999
+ async findAndParseUserStates(owner) {
7000
+ const accounts = await (0, helpers_1.getProgramAccountsV2)(constants_1.KAMINO_FARM_PROGRAM, 10, [
7001
+ { dataSize: 920 },
7002
+ { memcmp: { offset: 48, bytes: owner.toBase58() } },
7003
+ ]);
7004
+ return accounts.map(({ pubkey, account }) => {
7005
+ // farmState: [16, 48]
7006
+ // owner: [48, 80]
7007
+ // isFarmDelegated + padding: [80, 88]
7008
+ // rewardsTallyScaled: [88, 248]
7009
+ // unclaimedRewards[0..10]: [248, 328]
7010
+ const farmState = new web3_js_1.PublicKey(account.data.subarray(16, 48));
7011
+ const rewardsOffset = 248;
7012
+ const numRewards = 10;
7013
+ const rewardSize = 8;
7014
+ const rewardsData = account.data.subarray(rewardsOffset, rewardsOffset + numRewards * rewardSize);
7015
+ const unclaimedRewards = Array.from({ length: numRewards }, (_, i) => {
7016
+ const rewardData = rewardsData.subarray(i * rewardSize, (i + 1) * rewardSize);
7017
+ return new anchor_1.BN(rewardData, "le");
7018
+ });
6911
7019
  return {
6912
- userFarmState: account.pubkey,
7020
+ userState: pubkey,
6913
7021
  farmState,
7022
+ unclaimedRewards,
6914
7023
  };
6915
7024
  });
6916
7025
  }
@@ -6965,22 +7074,25 @@ class KaminoFarmClient {
6965
7074
  }
6966
7075
  async harvestTx(txOptions = {}) {
6967
7076
  const glamSigner = txOptions.signer || this.base.getSigner();
6968
- const vault = this.base.vaultPda;
6969
- const farmStates = await this.findAndParseFarmStates(vault);
7077
+ const farmStates = await this.findAndParseUserStates(this.base.vaultPda);
6970
7078
  const parsedFarms = await this.fetchAndParseFarms(farmStates.map((f) => f.farmState));
6971
7079
  const tx = new web3_js_1.Transaction();
6972
- for (const { userFarmState, farmState } of farmStates) {
7080
+ console.log("Building transaction to harvest the following rewards:");
7081
+ for (const { userState, farmState, unclaimedRewards } of farmStates) {
6973
7082
  const { globalConfig, rewards } = parsedFarms.get(farmState.toBase58());
6974
7083
  for (const { index, mint, tokenProgram, rewardsVault } of rewards) {
6975
- console.log("Reward token:", mint.toBase58());
7084
+ if (unclaimedRewards[index].eq(new anchor_1.BN(0))) {
7085
+ continue;
7086
+ }
7087
+ console.log(`userState: ${userState}, farmState: ${farmState}, unclaimedReward: ${unclaimedRewards[index]}, token: ${mint}`);
6976
7088
  const vaultAta = this.base.getVaultAta(mint, tokenProgram);
6977
- const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, vaultAta, vault, mint, tokenProgram);
7089
+ const createAtaIx = (0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(glamSigner, vaultAta, this.base.vaultPda, mint, tokenProgram);
6978
7090
  const harvestIx = await this.base.program.methods
6979
7091
  .kaminoFarmHarvestReward(new anchor_1.BN(index))
6980
7092
  .accounts({
6981
7093
  glamState: this.base.statePda,
6982
7094
  glamSigner,
6983
- userState: userFarmState,
7095
+ userState,
6984
7096
  farmState,
6985
7097
  globalConfig,
6986
7098
  rewardMint: mint,
@@ -6995,6 +7107,9 @@ class KaminoFarmClient {
6995
7107
  tx.add(createAtaIx, harvestIx);
6996
7108
  }
6997
7109
  }
7110
+ if (tx.instructions.length === 0) {
7111
+ throw new Error("No rewards to harvest");
7112
+ }
6998
7113
  const vTx = await this.base.intoVersionedTransaction(tx, txOptions);
6999
7114
  return vTx;
7000
7115
  }
@@ -7495,16 +7610,48 @@ class InvestClient {
7495
7610
  constructor(base) {
7496
7611
  this.base = base;
7497
7612
  }
7613
+ /**
7614
+ * Subscribe to a tokenized vault
7615
+ *
7616
+ * @param asset Deposit asset
7617
+ * @param amount
7618
+ * @param mintId
7619
+ * @param queued by default false, set to true to subscribe in queued mode
7620
+ * @param txOptions
7621
+ * @returns
7622
+ */
7498
7623
  async subscribe(asset, amount, mintId = 0, queued = false, txOptions = {}) {
7499
7624
  const tx = await (queued
7500
7625
  ? this.queuedSubscribeTx(asset, amount, mintId, txOptions)
7501
7626
  : this.subscribeTx(asset, amount, mintId, txOptions));
7502
7627
  return await this.base.sendAndConfirm(tx);
7503
7628
  }
7629
+ /**
7630
+ * Request to redeem share tokens of a tokenized vault in queued mode
7631
+ *
7632
+ * @param amount
7633
+ * @param mintId
7634
+ * @param txOptions
7635
+ * @returns
7636
+ */
7504
7637
  async queuedRedeem(amount, mintId = 0, txOptions = {}) {
7505
7638
  const tx = await this.queuedRedeemTx(amount, mintId, txOptions);
7506
7639
  return await this.base.sendAndConfirm(tx);
7507
7640
  }
7641
+ /**
7642
+ * Redeem share tokens of a tokenized vault instantly. Preconditions:
7643
+ * 1. The vault must allow permissionless fulfillment
7644
+ * 2. The vault must have sufficient liquidity
7645
+ *
7646
+ * @param amount
7647
+ * @param mintId
7648
+ * @param txOptions
7649
+ * @returns
7650
+ */
7651
+ async instantRedeem(amount, mintId = 0, txOptions = {}) {
7652
+ const tx = await this.instantRedeemTx(amount, mintId, txOptions);
7653
+ return await this.base.sendAndConfirm(tx);
7654
+ }
7508
7655
  async fulfill(mintId = 0, txOptions = {}) {
7509
7656
  const tx = await this.fulfillTx(mintId, txOptions);
7510
7657
  return await this.base.sendAndConfirm(tx);
@@ -7554,7 +7701,6 @@ class InvestClient {
7554
7701
  signerPolicy = (0, glamPDAs_1.getAccountPolicyPda)(this.base.getMintAta(signer));
7555
7702
  console.log(`signerPolicy: ${signerPolicy} for signer ${signer} and token account ${mintTo}`);
7556
7703
  }
7557
- // @ts-ignore
7558
7704
  const tx = await this.base.program.methods
7559
7705
  .subscribe(0, amount)
7560
7706
  .accounts({
@@ -7608,6 +7754,67 @@ class InvestClient {
7608
7754
  .transaction();
7609
7755
  return await this.base.intoVersionedTransaction(tx, txOptions);
7610
7756
  }
7757
+ async instantRedeemTx(amount, mintId = 0, txOptions = {}) {
7758
+ if (mintId !== 0) {
7759
+ throw new Error("mintId must be 0");
7760
+ }
7761
+ // Instant redemption flow is realized by enqueueing a redemption, fulfilling it, and then claiming the tokens in a single transaction.
7762
+ const preInstructions = txOptions.preInstructions || [];
7763
+ const signer = txOptions.signer || this.base.getSigner();
7764
+ const glamMint = this.base.mintPda;
7765
+ const stateModel = await this.base.fetchStateModel();
7766
+ const baseAsset = stateModel.baseAsset;
7767
+ const signerAta = this.base.getAta(baseAsset, signer);
7768
+ const fulfillIx = await this.base.program.methods
7769
+ .fulfill(mintId)
7770
+ .accounts({
7771
+ glamState: this.base.statePda,
7772
+ glamMint,
7773
+ signer,
7774
+ asset: baseAsset,
7775
+ depositTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
7776
+ })
7777
+ .instruction();
7778
+ preInstructions.push((0, spl_token_1.createAssociatedTokenAccountIdempotentInstruction)(signer, signerAta, signer, baseAsset));
7779
+ const claimIx = await this.base.program.methods
7780
+ .claim(0)
7781
+ .accounts({
7782
+ glamState: this.base.statePda,
7783
+ signer,
7784
+ tokenMint: baseAsset,
7785
+ claimTokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
7786
+ })
7787
+ .instruction();
7788
+ const remainingAccounts = [];
7789
+ if (await this.base.isLockupEnabled()) {
7790
+ const extraMetasAccount = this.base.extraMetasPda;
7791
+ const signerPolicy = (0, glamPDAs_1.getAccountPolicyPda)(this.base.getMintAta(signer));
7792
+ const escrow = this.base.escrowPda;
7793
+ const escrowPolicy = (0, glamPDAs_1.getAccountPolicyPda)(this.base.getMintAta(escrow));
7794
+ remainingAccounts.push(...[
7795
+ extraMetasAccount,
7796
+ signerPolicy,
7797
+ escrowPolicy,
7798
+ constants_1.TRANSFER_HOOK_PROGRAM,
7799
+ ]);
7800
+ }
7801
+ const tx = await this.base.program.methods
7802
+ .queuedRedeem(0, amount)
7803
+ .accounts({
7804
+ glamState: this.base.statePda,
7805
+ glamMint,
7806
+ signer,
7807
+ })
7808
+ .remainingAccounts(remainingAccounts.map((pubkey) => ({
7809
+ pubkey,
7810
+ isSigner: false,
7811
+ isWritable: false,
7812
+ })))
7813
+ .preInstructions(preInstructions)
7814
+ .postInstructions([fulfillIx, claimIx])
7815
+ .transaction();
7816
+ return await this.base.intoVersionedTransaction(tx, txOptions);
7817
+ }
7611
7818
  async queuedRedeemTx(amount, mintId = 0, txOptions = {}) {
7612
7819
  if (mintId !== 0) {
7613
7820
  throw new Error("mintId must be 0");
@@ -8512,6 +8719,7 @@ class CliConfig {
8512
8719
  }
8513
8720
  process.env.ANCHOR_PROVIDER_URL = cliConfig.json_rpc_url;
8514
8721
  process.env.ANCHOR_WALLET = cliConfig.keypair_path;
8722
+ process.env.HELIUS_API_KEY = cliConfig.priority_fee?.helius_api_key;
8515
8723
  return cliConfig;
8516
8724
  }
8517
8725
  catch (err) {
@@ -10074,17 +10282,28 @@ function installInvestCommands(tokenized, glamClient, cliConfig, txOptions) {
10074
10282
  });
10075
10283
  tokenized
10076
10284
  .command("redeem <amount>")
10285
+ .option("-i, --instant", "Redeem share tokens instantly", false)
10286
+ .option("-y, --yes", "Skip confirmation prompt", false)
10077
10287
  .description("Request to redeem share tokens")
10078
- .option("-y, --yes", "Skip confirmation prompt")
10079
- .action(async (amount, options) => {
10288
+ .action(async (amount, { instant, yes }) => {
10080
10289
  const amountBN = new anchor_1.BN(parseFloat(amount) * web3_js_1.LAMPORTS_PER_SOL);
10081
- options?.yes ||
10082
- (await (0, utils_1.confirmOperation)(`Confirm queued redemption of ${amount} shares?`));
10290
+ yes ||
10291
+ (await (0, utils_1.confirmOperation)(`Confirm ${instant ? "instant" : "queued"} redemption of ${amount} shares?`));
10083
10292
  try {
10084
- const txSig = await glamClient.invest.queuedRedeem(amountBN, 0, {
10085
- ...txOptions,
10086
- });
10087
- console.log(`${glamClient.signer} requested to redeem:`, txSig);
10293
+ let txSig;
10294
+ if (instant) {
10295
+ const preInstructions = await glamClient.price.priceVaultIxs(glam_sdk_1.PriceDenom.SOL);
10296
+ const lookupTables = glamClient.price.lookupTables;
10297
+ txSig = await glamClient.invest.instantRedeem(amountBN, 0, {
10298
+ ...txOptions,
10299
+ preInstructions,
10300
+ lookupTables,
10301
+ });
10302
+ }
10303
+ else {
10304
+ txSig = await glamClient.invest.queuedRedeem(amountBN, 0, txOptions);
10305
+ }
10306
+ console.log(`${glamClient.signer} ${instant ? "instantly" : "queued"} redeemed:`, txSig);
10088
10307
  }
10089
10308
  catch (e) {
10090
10309
  console.error((0, utils_1.parseTxError)(e));
@@ -10123,6 +10342,20 @@ function installInvestCommands(tokenized, glamClient, cliConfig, txOptions) {
10123
10342
  throw e;
10124
10343
  }
10125
10344
  });
10345
+ tokenized
10346
+ .command("set-permissionless-fulfill")
10347
+ .argument("<enabled>", "Enable or disable permissionless fulfillment", (v) => v === "true" || v === "1", false)
10348
+ .description("Enable or disable permissionless fulfillment")
10349
+ .action(async (enabled) => {
10350
+ try {
10351
+ const txSig = await glamClient.mint.setPermissionlessFulfill(enabled);
10352
+ console.log(`Permissionless fulfillment set to ${enabled}:`, txSig);
10353
+ }
10354
+ catch (e) {
10355
+ console.error((0, utils_1.parseTxError)(e));
10356
+ process.exit(1);
10357
+ }
10358
+ });
10126
10359
  }
10127
10360
 
10128
10361
 
@@ -10141,7 +10374,7 @@ function installAltCommands(alt, glamClient, cliConfig, txOptions = {}) {
10141
10374
  .description("Create address lookup table (ALT) for the active GLAM")
10142
10375
  .action(async () => {
10143
10376
  try {
10144
- const response = await fetch(`https://rest2.glam.systems/v0/lut/vault/create?state=${glamClient.statePda}&payer=${glamClient.getSigner()}`);
10377
+ const response = await fetch(`https://api.glam.systems/v0/lut/vault/create?state=${glamClient.statePda}&payer=${glamClient.getSigner()}`);
10145
10378
  const data = await response.json();
10146
10379
  const table = data.tables[0];
10147
10380
  const b64Txs = data.tx;
@@ -10633,7 +10866,7 @@ program
10633
10866
  .hook("preSubcommand", async (thisCommand, actionCommand) => {
10634
10867
  await (0, idl_1.idlCheck)(glamClient);
10635
10868
  })
10636
- .version("0.1.33");
10869
+ .version("0.1.35");
10637
10870
  program
10638
10871
  .command("env")
10639
10872
  .description("Display current environment setup")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glamsystems/glam-cli",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
4
4
  "description": "CLI for interacting with the GLAM Protocol",
5
5
  "main": "./main.js",
6
6
  "bin": {
@@ -21,7 +21,7 @@
21
21
  "node": ">=20.18.0"
22
22
  },
23
23
  "dependencies": {
24
- "@glamsystems/glam-sdk": "0.1.33",
24
+ "@glamsystems/glam-sdk": "0.1.35",
25
25
  "commander": "^11.1.0",
26
26
  "inquirer": "^8.2.6",
27
27
  "@switchboard-xyz/common": "^3.0.0"