@everstake/wallet-sdk-hysp-solana 1.2.0 → 1.3.1

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/dist/index.d.mts CHANGED
@@ -126,6 +126,8 @@ type Params = {
126
126
  };
127
127
  /** Instructions to be added after the main instructions created by SDK */
128
128
  afterInstructions?: Instruction[];
129
+ /** Memo text to be included in the transaction */
130
+ memo?: string;
129
131
  };
130
132
  interface ShareToken {
131
133
  address: string;
@@ -271,6 +273,12 @@ declare class HyspSolana extends Blockchain {
271
273
  * @returns Returns a promise that resolves with the withdraw transaction response.
272
274
  */
273
275
  withdraw(userAddress: Address, sharesAmount: number | string | bigint | Decimal, params?: Params): Promise<ApiResponse<TransactionMessageWithLifetime>>;
276
+ /**
277
+ * Processes and creates a memo instruction according to SDK requirements
278
+ * @param memo - Input memo text
279
+ * @returns Memo instruction
280
+ */
281
+ protected processMemo(memo?: string): Instruction;
274
282
  private buildTx;
275
283
  }
276
284
 
package/dist/index.d.ts CHANGED
@@ -126,6 +126,8 @@ type Params = {
126
126
  };
127
127
  /** Instructions to be added after the main instructions created by SDK */
128
128
  afterInstructions?: Instruction[];
129
+ /** Memo text to be included in the transaction */
130
+ memo?: string;
129
131
  };
130
132
  interface ShareToken {
131
133
  address: string;
@@ -271,6 +273,12 @@ declare class HyspSolana extends Blockchain {
271
273
  * @returns Returns a promise that resolves with the withdraw transaction response.
272
274
  */
273
275
  withdraw(userAddress: Address, sharesAmount: number | string | bigint | Decimal, params?: Params): Promise<ApiResponse<TransactionMessageWithLifetime>>;
276
+ /**
277
+ * Processes and creates a memo instruction according to SDK requirements
278
+ * @param memo - Input memo text
279
+ * @returns Memo instruction
280
+ */
281
+ protected processMemo(memo?: string): Instruction;
274
282
  private buildTx;
275
283
  }
276
284
 
package/dist/index.js CHANGED
@@ -31,6 +31,7 @@ module.exports = __toCommonJS(index_exports);
31
31
  // src/hysp.ts
32
32
  var import_kit2 = require("@solana/kit");
33
33
  var import_compute_budget = require("@solana-program/compute-budget");
34
+ var import_memo = require("@solana-program/memo");
34
35
  var import_klend_sdk = require("@kamino-finance/klend-sdk");
35
36
  var import_decimal = require("decimal.js");
36
37
 
@@ -393,6 +394,38 @@ var HyspSolana = class extends Blockchain {
393
394
  throw this.handleError("WITHDRAW_ERROR", error);
394
395
  }
395
396
  }
397
+ /**
398
+ * Processes and creates a memo instruction according to SDK requirements
399
+ * @param memo - Input memo text
400
+ * @returns Memo instruction
401
+ */
402
+ processMemo(memo) {
403
+ let processedMemo;
404
+ if (!memo || memo.trim() === "") {
405
+ processedMemo = "SDK";
406
+ } else {
407
+ const trimmedMemo = memo.trim();
408
+ if (trimmedMemo === "SDK") {
409
+ processedMemo = trimmedMemo;
410
+ } else if (!trimmedMemo.startsWith("SDK:")) {
411
+ processedMemo = `SDK:${trimmedMemo}`;
412
+ } else {
413
+ processedMemo = trimmedMemo;
414
+ }
415
+ }
416
+ if (processedMemo.length > 64) {
417
+ throw new Error(
418
+ `Invalid memo: "${processedMemo}". Must be max 64 characters`
419
+ );
420
+ }
421
+ const validPattern = /^[A-Za-z0-9:_-]*$/;
422
+ if (!validPattern.test(processedMemo)) {
423
+ throw new Error(
424
+ `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`
425
+ );
426
+ }
427
+ return (0, import_memo.getAddMemoInstruction)({ memo: processedMemo });
428
+ }
396
429
  async buildTx(sender, instructions, params, lookupTableAddresses) {
397
430
  let transactionMessage = (0, import_kit2.pipe)(
398
431
  (0, import_kit2.createTransactionMessage)({ version: 0 }),
@@ -424,6 +457,13 @@ var HyspSolana = class extends Blockchain {
424
457
  transactionMessage
425
458
  );
426
459
  }
460
+ if (params?.memo !== void 0) {
461
+ const memoInstruction = this.processMemo(params.memo);
462
+ transactionMessage = (0, import_kit2.appendTransactionMessageInstruction)(
463
+ memoInstruction,
464
+ transactionMessage
465
+ );
466
+ }
427
467
  if (params?.afterInstructions) {
428
468
  for (const instruction of params.afterInstructions) {
429
469
  transactionMessage = (0, import_kit2.appendTransactionMessageInstruction)(
package/dist/index.mjs CHANGED
@@ -16,6 +16,7 @@ import {
16
16
  getSetComputeUnitLimitInstruction,
17
17
  getSetComputeUnitPriceInstruction
18
18
  } from "@solana-program/compute-budget";
19
+ import { getAddMemoInstruction } from "@solana-program/memo";
19
20
  import { KaminoVault } from "@kamino-finance/klend-sdk";
20
21
  import { Decimal } from "decimal.js";
21
22
 
@@ -378,6 +379,38 @@ var HyspSolana = class extends Blockchain {
378
379
  throw this.handleError("WITHDRAW_ERROR", error);
379
380
  }
380
381
  }
382
+ /**
383
+ * Processes and creates a memo instruction according to SDK requirements
384
+ * @param memo - Input memo text
385
+ * @returns Memo instruction
386
+ */
387
+ processMemo(memo) {
388
+ let processedMemo;
389
+ if (!memo || memo.trim() === "") {
390
+ processedMemo = "SDK";
391
+ } else {
392
+ const trimmedMemo = memo.trim();
393
+ if (trimmedMemo === "SDK") {
394
+ processedMemo = trimmedMemo;
395
+ } else if (!trimmedMemo.startsWith("SDK:")) {
396
+ processedMemo = `SDK:${trimmedMemo}`;
397
+ } else {
398
+ processedMemo = trimmedMemo;
399
+ }
400
+ }
401
+ if (processedMemo.length > 64) {
402
+ throw new Error(
403
+ `Invalid memo: "${processedMemo}". Must be max 64 characters`
404
+ );
405
+ }
406
+ const validPattern = /^[A-Za-z0-9:_-]*$/;
407
+ if (!validPattern.test(processedMemo)) {
408
+ throw new Error(
409
+ `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`
410
+ );
411
+ }
412
+ return getAddMemoInstruction({ memo: processedMemo });
413
+ }
381
414
  async buildTx(sender, instructions, params, lookupTableAddresses) {
382
415
  let transactionMessage = pipe(
383
416
  createTransactionMessage({ version: 0 }),
@@ -409,6 +442,13 @@ var HyspSolana = class extends Blockchain {
409
442
  transactionMessage
410
443
  );
411
444
  }
445
+ if (params?.memo !== void 0) {
446
+ const memoInstruction = this.processMemo(params.memo);
447
+ transactionMessage = appendTransactionMessageInstruction(
448
+ memoInstruction,
449
+ transactionMessage
450
+ );
451
+ }
412
452
  if (params?.afterInstructions) {
413
453
  for (const instruction of params.afterInstructions) {
414
454
  transactionMessage = appendTransactionMessageInstruction(
package/jest.config.ts ADDED
@@ -0,0 +1,18 @@
1
+ import type { Config } from 'jest';
2
+
3
+ const config: Config = {
4
+ preset: 'ts-jest',
5
+ testEnvironment: 'node',
6
+ verbose: true,
7
+ roots: ['<rootDir>/src'],
8
+ testMatch: ['**/__tests__/**/*.test.ts'],
9
+ collectCoverageFrom: [
10
+ 'src/**/*.ts',
11
+ '!src/**/*.d.ts',
12
+ '!src/**/index.ts',
13
+ ],
14
+ coverageDirectory: 'coverage',
15
+ coverageReporters: ['text', 'lcov', 'html'],
16
+ };
17
+
18
+ export default config;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everstake/wallet-sdk-hysp-solana",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "HYSP Solana - Everstake Wallet SDK",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -20,8 +20,8 @@
20
20
  "type-check": "tsc",
21
21
  "lint": "eslint 'src/**/*.{ts,tsx}'",
22
22
  "prettier": "prettier --write 'src/**/*.{ts,tsx}'",
23
- "test": "jest",
24
- "prebuild": "pnpm run type-check && pnpm run lint"
23
+ "test": "jest --passWithNoTests",
24
+ "prebuild": "pnpm run type-check && pnpm run lint && npm run test"
25
25
  },
26
26
  "keywords": [
27
27
  "hysp",
@@ -44,6 +44,7 @@
44
44
  "dependencies": {
45
45
  "@kamino-finance/klend-sdk": "^7.3.4",
46
46
  "@solana-program/compute-budget": "^0.7.0",
47
+ "@solana-program/memo": "^0.10.0",
47
48
  "@solana/kit": "^3.0.3",
48
49
  "decimal.js": "^10.6.0"
49
50
  },
@@ -65,5 +66,14 @@
65
66
  "tsup": "^8.1.0",
66
67
  "typescript": "^5.5.3",
67
68
  "typescript-eslint": "^8.46.2"
69
+ },
70
+ "pnpm": {
71
+ "overrides": {
72
+ "js-yaml@<3.14.2": ">=3.14.2",
73
+ "js-yaml@>=4.0.0 <4.1.1": ">=4.1.1",
74
+ "glob@>=10.2.0 <10.5.0": ">=10.5.0",
75
+ "js-yaml": ">=4.1.1",
76
+ "glob": ">=10.5.0"
77
+ }
68
78
  }
69
79
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Copyright (c) 2025, Everstake.
3
+ * Licensed under the BSD-3-Clause License. See LICENSE file for details.
4
+ */
5
+
6
+ import { HyspSolana } from '..';
7
+ import { SupportedToken } from '../constants';
8
+
9
+ // Create a test class to access protected methods
10
+ class TestableHyspSolana extends HyspSolana {
11
+ public testProcessMemo(memo?: string) {
12
+ return this.processMemo(memo);
13
+ }
14
+ }
15
+
16
+ describe('HyspSolana processMemo method', () => {
17
+ const hyspSolana: TestableHyspSolana = new TestableHyspSolana(
18
+ 'USDC' as SupportedToken,
19
+ 'https://api.mainnet-beta.solana.com',
20
+ );
21
+
22
+ it('should return SDK when memo is empty', () => {
23
+ const result = hyspSolana.testProcessMemo('');
24
+
25
+ expect(new TextDecoder().decode(result.data)).toBe('SDK');
26
+ });
27
+
28
+ it('should prepend SDK: to memo without SDK prefix', () => {
29
+ const result = hyspSolana.testProcessMemo('acme:pilotQ1:prod:v1');
30
+
31
+ expect(new TextDecoder().decode(result.data)).toBe(
32
+ 'SDK:acme:pilotQ1:prod:v1',
33
+ );
34
+ });
35
+
36
+ it('should keep memo unchanged when it has SDK prefix', () => {
37
+ const result = hyspSolana.testProcessMemo('SDK:bankxyz::prod:v1');
38
+
39
+ expect(new TextDecoder().decode(result.data)).toBe('SDK:bankxyz::prod:v1');
40
+ });
41
+ });
package/src/hysp.ts CHANGED
@@ -28,6 +28,8 @@ import {
28
28
  getSetComputeUnitPriceInstruction,
29
29
  } from '@solana-program/compute-budget';
30
30
 
31
+ import { getAddMemoInstruction } from '@solana-program/memo';
32
+
31
33
  import { KaminoVault, VaultHoldings, APY } from '@kamino-finance/klend-sdk';
32
34
 
33
35
  import { Decimal } from 'decimal.js';
@@ -341,6 +343,44 @@ export class HyspSolana extends Blockchain {
341
343
  }
342
344
  }
343
345
 
346
+ /**
347
+ * Processes and creates a memo instruction according to SDK requirements
348
+ * @param memo - Input memo text
349
+ * @returns Memo instruction
350
+ */
351
+ protected processMemo(memo?: string): Instruction {
352
+ // Process memo text
353
+ let processedMemo: string;
354
+ if (!memo || memo.trim() === '') {
355
+ processedMemo = 'SDK';
356
+ } else {
357
+ const trimmedMemo = memo.trim();
358
+ if (trimmedMemo === 'SDK') {
359
+ processedMemo = trimmedMemo;
360
+ } else if (!trimmedMemo.startsWith('SDK:')) {
361
+ processedMemo = `SDK:${trimmedMemo}`;
362
+ } else {
363
+ processedMemo = trimmedMemo;
364
+ }
365
+ }
366
+
367
+ // Validate memo
368
+ if (processedMemo.length > 64) {
369
+ throw new Error(
370
+ `Invalid memo: "${processedMemo}". Must be max 64 characters`,
371
+ );
372
+ }
373
+
374
+ const validPattern = /^[A-Za-z0-9:_-]*$/;
375
+ if (!validPattern.test(processedMemo)) {
376
+ throw new Error(
377
+ `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`,
378
+ );
379
+ }
380
+
381
+ return getAddMemoInstruction({ memo: processedMemo });
382
+ }
383
+
344
384
  private async buildTx(
345
385
  sender: string,
346
386
  instructions: Instruction[],
@@ -388,6 +428,15 @@ export class HyspSolana extends Blockchain {
388
428
  );
389
429
  }
390
430
 
431
+ // Add memo instruction if memo is provided in params
432
+ if (params?.memo !== undefined) {
433
+ const memoInstruction = this.processMemo(params.memo);
434
+ transactionMessage = appendTransactionMessageInstruction(
435
+ memoInstruction,
436
+ transactionMessage,
437
+ );
438
+ }
439
+
391
440
  if (params?.afterInstructions) {
392
441
  for (const instruction of params.afterInstructions) {
393
442
  transactionMessage = appendTransactionMessageInstruction(
@@ -20,6 +20,8 @@ export type Params = {
20
20
  };
21
21
  /** Instructions to be added after the main instructions created by SDK */
22
22
  afterInstructions?: Instruction[];
23
+ /** Memo text to be included in the transaction */
24
+ memo?: string;
23
25
  };
24
26
 
25
27
  export interface ShareToken {