@everstake/wallet-sdk-hysp-solana 1.4.1 → 1.5.0

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
@@ -89,6 +89,9 @@ declare const ERROR_MESSAGES: {
89
89
  VAULT_LOAD_ERROR: string;
90
90
  VAULT_NOT_FOUND_ERROR: string;
91
91
  TX_TOO_LARGE: string;
92
+ MEMO_REQUIRED_ERROR: string;
93
+ MEMO_TOO_LONG_ERROR: string;
94
+ MEMO_INVALID_CHARACTERS_ERROR: string;
92
95
  };
93
96
 
94
97
  /**
@@ -173,6 +176,9 @@ declare class HyspSolana extends Blockchain {
173
176
  VAULT_LOAD_ERROR: string;
174
177
  VAULT_NOT_FOUND_ERROR: string;
175
178
  TX_TOO_LARGE: string;
179
+ MEMO_REQUIRED_ERROR: string;
180
+ MEMO_TOO_LONG_ERROR: string;
181
+ MEMO_INVALID_CHARACTERS_ERROR: string;
176
182
  };
177
183
  protected ORIGINAL_ERROR_MESSAGES: {
178
184
  INITIALIZATION_ERROR: string;
@@ -183,6 +189,9 @@ declare class HyspSolana extends Blockchain {
183
189
  VAULT_LOAD_ERROR: string;
184
190
  VAULT_NOT_FOUND_ERROR: string;
185
191
  TX_TOO_LARGE: string;
192
+ MEMO_REQUIRED_ERROR: string;
193
+ MEMO_TOO_LONG_ERROR: string;
194
+ MEMO_INVALID_CHARACTERS_ERROR: string;
186
195
  };
187
196
  private connection;
188
197
  private vault;
package/dist/index.d.ts CHANGED
@@ -89,6 +89,9 @@ declare const ERROR_MESSAGES: {
89
89
  VAULT_LOAD_ERROR: string;
90
90
  VAULT_NOT_FOUND_ERROR: string;
91
91
  TX_TOO_LARGE: string;
92
+ MEMO_REQUIRED_ERROR: string;
93
+ MEMO_TOO_LONG_ERROR: string;
94
+ MEMO_INVALID_CHARACTERS_ERROR: string;
92
95
  };
93
96
 
94
97
  /**
@@ -173,6 +176,9 @@ declare class HyspSolana extends Blockchain {
173
176
  VAULT_LOAD_ERROR: string;
174
177
  VAULT_NOT_FOUND_ERROR: string;
175
178
  TX_TOO_LARGE: string;
179
+ MEMO_REQUIRED_ERROR: string;
180
+ MEMO_TOO_LONG_ERROR: string;
181
+ MEMO_INVALID_CHARACTERS_ERROR: string;
176
182
  };
177
183
  protected ORIGINAL_ERROR_MESSAGES: {
178
184
  INITIALIZATION_ERROR: string;
@@ -183,6 +189,9 @@ declare class HyspSolana extends Blockchain {
183
189
  VAULT_LOAD_ERROR: string;
184
190
  VAULT_NOT_FOUND_ERROR: string;
185
191
  TX_TOO_LARGE: string;
192
+ MEMO_REQUIRED_ERROR: string;
193
+ MEMO_TOO_LONG_ERROR: string;
194
+ MEMO_INVALID_CHARACTERS_ERROR: string;
186
195
  };
187
196
  private connection;
188
197
  private vault;
package/dist/index.js CHANGED
@@ -128,7 +128,10 @@ var ERROR_MESSAGES = {
128
128
  GET_BALANCE_ERROR: "An error occurred while fetching user token balance",
129
129
  VAULT_LOAD_ERROR: "An error occurred while loading vault info",
130
130
  VAULT_NOT_FOUND_ERROR: "Vault not found for token: {0}",
131
- TX_TOO_LARGE: "Transaction exceeds the maximum size limit of 1232 bytes"
131
+ TX_TOO_LARGE: "Transaction exceeds the maximum size limit of 1232 bytes",
132
+ MEMO_REQUIRED_ERROR: "Memo is required. Please contact us to get your referrer ID.",
133
+ MEMO_TOO_LONG_ERROR: 'Invalid memo: "{0}". Must be max 64 characters',
134
+ MEMO_INVALID_CHARACTERS_ERROR: 'Invalid memo: "{0}". Must contain only [A-Za-z0-9:_-] characters'
132
135
  };
133
136
 
134
137
  // src/constants/index.ts
@@ -403,29 +406,22 @@ var HyspSolana = class extends Blockchain {
403
406
  * @returns Memo instruction
404
407
  */
405
408
  processMemo(memo) {
406
- let processedMemo;
407
409
  if (!memo || memo.trim() === "") {
408
- processedMemo = "SDK";
410
+ throw this.throwError("MEMO_REQUIRED_ERROR");
411
+ }
412
+ const trimmedMemo = memo.trim();
413
+ let processedMemo;
414
+ if (!trimmedMemo.startsWith("SDK")) {
415
+ processedMemo = `SDK:${trimmedMemo}`;
409
416
  } else {
410
- const trimmedMemo = memo.trim();
411
- if (trimmedMemo === "SDK") {
412
- processedMemo = trimmedMemo;
413
- } else if (!trimmedMemo.startsWith("SDK:")) {
414
- processedMemo = `SDK:${trimmedMemo}`;
415
- } else {
416
- processedMemo = trimmedMemo;
417
- }
417
+ processedMemo = trimmedMemo;
418
418
  }
419
419
  if (processedMemo.length > 64) {
420
- throw new Error(
421
- `Invalid memo: "${processedMemo}". Must be max 64 characters`
422
- );
420
+ throw this.throwError("MEMO_TOO_LONG_ERROR", processedMemo);
423
421
  }
424
422
  const validPattern = /^[A-Za-z0-9:_-]*$/;
425
423
  if (!validPattern.test(processedMemo)) {
426
- throw new Error(
427
- `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`
428
- );
424
+ throw this.throwError("MEMO_INVALID_CHARACTERS_ERROR", processedMemo);
429
425
  }
430
426
  return (0, import_memo.getAddMemoInstruction)({ memo: processedMemo });
431
427
  }
package/dist/index.mjs CHANGED
@@ -113,7 +113,10 @@ var ERROR_MESSAGES = {
113
113
  GET_BALANCE_ERROR: "An error occurred while fetching user token balance",
114
114
  VAULT_LOAD_ERROR: "An error occurred while loading vault info",
115
115
  VAULT_NOT_FOUND_ERROR: "Vault not found for token: {0}",
116
- TX_TOO_LARGE: "Transaction exceeds the maximum size limit of 1232 bytes"
116
+ TX_TOO_LARGE: "Transaction exceeds the maximum size limit of 1232 bytes",
117
+ MEMO_REQUIRED_ERROR: "Memo is required. Please contact us to get your referrer ID.",
118
+ MEMO_TOO_LONG_ERROR: 'Invalid memo: "{0}". Must be max 64 characters',
119
+ MEMO_INVALID_CHARACTERS_ERROR: 'Invalid memo: "{0}". Must contain only [A-Za-z0-9:_-] characters'
117
120
  };
118
121
 
119
122
  // src/constants/index.ts
@@ -388,29 +391,22 @@ var HyspSolana = class extends Blockchain {
388
391
  * @returns Memo instruction
389
392
  */
390
393
  processMemo(memo) {
391
- let processedMemo;
392
394
  if (!memo || memo.trim() === "") {
393
- processedMemo = "SDK";
395
+ throw this.throwError("MEMO_REQUIRED_ERROR");
396
+ }
397
+ const trimmedMemo = memo.trim();
398
+ let processedMemo;
399
+ if (!trimmedMemo.startsWith("SDK")) {
400
+ processedMemo = `SDK:${trimmedMemo}`;
394
401
  } else {
395
- const trimmedMemo = memo.trim();
396
- if (trimmedMemo === "SDK") {
397
- processedMemo = trimmedMemo;
398
- } else if (!trimmedMemo.startsWith("SDK:")) {
399
- processedMemo = `SDK:${trimmedMemo}`;
400
- } else {
401
- processedMemo = trimmedMemo;
402
- }
402
+ processedMemo = trimmedMemo;
403
403
  }
404
404
  if (processedMemo.length > 64) {
405
- throw new Error(
406
- `Invalid memo: "${processedMemo}". Must be max 64 characters`
407
- );
405
+ throw this.throwError("MEMO_TOO_LONG_ERROR", processedMemo);
408
406
  }
409
407
  const validPattern = /^[A-Za-z0-9:_-]*$/;
410
408
  if (!validPattern.test(processedMemo)) {
411
- throw new Error(
412
- `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`
413
- );
409
+ throw this.throwError("MEMO_INVALID_CHARACTERS_ERROR", processedMemo);
414
410
  }
415
411
  return getAddMemoInstruction({ memo: processedMemo });
416
412
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@everstake/wallet-sdk-hysp-solana",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "HYSP Solana - Everstake Wallet SDK",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "sideEffects": false,
18
18
  "scripts": {
19
- "build": "tsup src/index.ts --format cjs,esm --dts",
19
+ "build": "pnpm run prebuild && tsup src/index.ts --format cjs,esm --dts",
20
20
  "type-check": "tsc",
21
21
  "lint": "eslint 'src/**/*.{ts,tsx}'",
22
22
  "prettier": "prettier --write 'src/**/*.{ts,tsx}'",
@@ -73,7 +73,17 @@
73
73
  "js-yaml@>=4.0.0 <4.1.1": ">=4.1.1",
74
74
  "glob@>=10.2.0 <10.5.0": ">=10.5.0",
75
75
  "js-yaml": ">=4.1.1",
76
- "glob": ">=10.5.0"
76
+ "glob": ">=10.5.0",
77
+ "lodash@>=4.0.0 <=4.17.22": ">=4.17.23",
78
+ "diff@>=4.0.0 <4.0.4": ">=4.0.4",
79
+ "@isaacs/brace-expansion@<=5.0.0": ">=5.0.1",
80
+ "axios@>=1.0.0 <=1.13.4": ">=1.13.5",
81
+ "bn.js@>=5.0.0 <5.2.3": ">=5.2.3",
82
+ "rollup@>=4.0.0 <4.59.0": ">=4.59.0",
83
+ "minimatch@<10.2.3": ">=10.2.3",
84
+ "flatted@<3.4.0": ">=3.4.0",
85
+ "flatted@<=3.4.1": ">=3.4.2",
86
+ "picomatch@<4.0.4": ">=4.0.4"
77
87
  }
78
88
  }
79
89
  }
@@ -0,0 +1,146 @@
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
+ import { WalletSDKError } from '../../../utils';
9
+
10
+ // Create a test class to access protected methods
11
+ class TestableHyspSolana extends HyspSolana {
12
+ public testProcessMemo(memo?: string) {
13
+ return this.processMemo(memo);
14
+ }
15
+ }
16
+
17
+ describe('HyspSolana processMemo method', () => {
18
+ const hyspSolana: TestableHyspSolana = new TestableHyspSolana(
19
+ 'USDC' as SupportedToken,
20
+ 'https://api.mainnet-beta.solana.com',
21
+ );
22
+
23
+ describe('Error cases', () => {
24
+ it('should throw MEMO_REQUIRED_ERROR when memo is undefined', () => {
25
+ expect(() => hyspSolana.testProcessMemo(undefined)).toThrow(
26
+ WalletSDKError,
27
+ );
28
+ expect(() => hyspSolana.testProcessMemo(undefined)).toThrow(
29
+ 'Memo is required. Please contact us to get your referrer ID.',
30
+ );
31
+ });
32
+
33
+ it('should throw MEMO_REQUIRED_ERROR when memo is empty string', () => {
34
+ expect(() => hyspSolana.testProcessMemo('')).toThrow(WalletSDKError);
35
+ expect(() => hyspSolana.testProcessMemo('')).toThrow(
36
+ 'Memo is required. Please contact us to get your referrer ID.',
37
+ );
38
+ });
39
+
40
+ it('should throw MEMO_REQUIRED_ERROR when memo is only whitespace', () => {
41
+ expect(() => hyspSolana.testProcessMemo(' ')).toThrow(WalletSDKError);
42
+ expect(() => hyspSolana.testProcessMemo(' ')).toThrow(
43
+ 'Memo is required. Please contact us to get your referrer ID.',
44
+ );
45
+ });
46
+
47
+ it('should throw MEMO_TOO_LONG_ERROR when processed memo exceeds 64 characters', () => {
48
+ const longMemo = 'a'.repeat(70);
49
+ expect(() => hyspSolana.testProcessMemo(longMemo)).toThrow(
50
+ WalletSDKError,
51
+ );
52
+ expect(() => hyspSolana.testProcessMemo(longMemo)).toThrow(
53
+ 'Must be max 64 characters',
54
+ );
55
+ });
56
+
57
+ it('should throw MEMO_INVALID_CHARACTERS_ERROR when memo contains invalid characters', () => {
58
+ expect(() => hyspSolana.testProcessMemo('test@domain')).toThrow(
59
+ WalletSDKError,
60
+ );
61
+ expect(() => hyspSolana.testProcessMemo('test@domain')).toThrow(
62
+ 'Must contain only [A-Za-z0-9:_-] characters',
63
+ );
64
+ });
65
+
66
+ it('should throw MEMO_INVALID_CHARACTERS_ERROR when memo contains spaces', () => {
67
+ expect(() => hyspSolana.testProcessMemo('test memo')).toThrow(
68
+ WalletSDKError,
69
+ );
70
+ expect(() => hyspSolana.testProcessMemo('test memo')).toThrow(
71
+ 'Must contain only [A-Za-z0-9:_-] characters',
72
+ );
73
+ });
74
+
75
+ it('should throw MEMO_INVALID_CHARACTERS_ERROR when memo contains special characters', () => {
76
+ expect(() => hyspSolana.testProcessMemo('test!memo#')).toThrow(
77
+ WalletSDKError,
78
+ );
79
+ expect(() => hyspSolana.testProcessMemo('test!memo#')).toThrow(
80
+ 'Must contain only [A-Za-z0-9:_-] characters',
81
+ );
82
+ });
83
+ });
84
+
85
+ describe('Success cases', () => {
86
+ it('should prepend SDK: to memo without SDK prefix', () => {
87
+ const result = hyspSolana.testProcessMemo('acme:pilotQ1:prod:v1');
88
+
89
+ expect(new TextDecoder().decode(result.data)).toBe(
90
+ 'SDK:acme:pilotQ1:prod:v1',
91
+ );
92
+ });
93
+
94
+ it('should keep memo unchanged when it starts with SDK (but not SDK:)', () => {
95
+ const result = hyspSolana.testProcessMemo('SDKtest');
96
+
97
+ expect(new TextDecoder().decode(result.data)).toBe('SDKtest');
98
+ });
99
+
100
+ it('should keep memo unchanged when it starts with SDK:', () => {
101
+ const result = hyspSolana.testProcessMemo('SDK:bankxyz::prod:v1');
102
+
103
+ expect(new TextDecoder().decode(result.data)).toBe(
104
+ 'SDK:bankxyz::prod:v1',
105
+ );
106
+ });
107
+
108
+ it('should handle simple referrer ID', () => {
109
+ const result = hyspSolana.testProcessMemo('referrer123');
110
+
111
+ expect(new TextDecoder().decode(result.data)).toBe('SDK:referrer123');
112
+ });
113
+
114
+ it('should handle referrer ID with underscores and hyphens', () => {
115
+ const result = hyspSolana.testProcessMemo('test_ref-id_123');
116
+
117
+ expect(new TextDecoder().decode(result.data)).toBe('SDK:test_ref-id_123');
118
+ });
119
+
120
+ it('should handle referrer ID with colons', () => {
121
+ const result = hyspSolana.testProcessMemo('org:team:user');
122
+
123
+ expect(new TextDecoder().decode(result.data)).toBe('SDK:org:team:user');
124
+ });
125
+
126
+ it('should handle maximum allowed length without SDK prefix', () => {
127
+ const maxMemo = 'a'.repeat(59); // 59 + 'SDK:' = 63 chars (under limit)
128
+ const result = hyspSolana.testProcessMemo(maxMemo);
129
+
130
+ expect(new TextDecoder().decode(result.data)).toBe(`SDK:${maxMemo}`);
131
+ });
132
+
133
+ it('should handle maximum allowed length with SDK prefix', () => {
134
+ const maxMemo = 'SDK:' + 'a'.repeat(60); // exactly 64 chars
135
+ const result = hyspSolana.testProcessMemo(maxMemo);
136
+
137
+ expect(new TextDecoder().decode(result.data)).toBe(maxMemo);
138
+ });
139
+
140
+ it('should trim whitespace from memo', () => {
141
+ const result = hyspSolana.testProcessMemo(' referrer123 ');
142
+
143
+ expect(new TextDecoder().decode(result.data)).toBe('SDK:referrer123');
144
+ });
145
+ });
146
+ });
@@ -44,17 +44,19 @@ describe('HyspSolana Transaction Size Tests', () => {
44
44
  });
45
45
 
46
46
  describe('Transaction Size Limits', () => {
47
- it('should check balance and error if insufficient shares', async () => {
47
+ it('should fetch user shares successfully', async () => {
48
48
  const userShares = await hyspSolana.getUserShares(
49
49
  PLACEHOLDER_USER_ADDRESS,
50
50
  );
51
51
 
52
52
  console.log(`User shares: ${userShares.result.toString()}`);
53
53
 
54
+ expect(userShares.result).toBeDefined();
55
+ expect(userShares.result).toBeInstanceOf(Decimal);
56
+
54
57
  if (userShares.result.lt(PLACEHOLDER_SHARES_AMOUNT)) {
55
- console.warn('Insufficient shares for tests.');
56
- fail(
57
- 'Insufficient shares for tests. Cannot proceed with transaction size tests.',
58
+ console.warn(
59
+ 'Note: User has insufficient shares for withdrawal tests.',
58
60
  );
59
61
  }
60
62
  }, 30000);
@@ -12,4 +12,9 @@ export const ERROR_MESSAGES = {
12
12
  VAULT_LOAD_ERROR: 'An error occurred while loading vault info',
13
13
  VAULT_NOT_FOUND_ERROR: 'Vault not found for token: {0}',
14
14
  TX_TOO_LARGE: 'Transaction exceeds the maximum size limit of 1232 bytes',
15
+ MEMO_REQUIRED_ERROR:
16
+ 'Memo is required. Please contact us to get your referrer ID.',
17
+ MEMO_TOO_LONG_ERROR: 'Invalid memo: "{0}". Must be max 64 characters',
18
+ MEMO_INVALID_CHARACTERS_ERROR:
19
+ 'Invalid memo: "{0}". Must contain only [A-Za-z0-9:_-] characters',
15
20
  };
package/src/hysp.ts CHANGED
@@ -351,33 +351,29 @@ export class HyspSolana extends Blockchain {
351
351
  * @returns Memo instruction
352
352
  */
353
353
  protected processMemo(memo?: string): Instruction {
354
- // Process memo text
355
- let processedMemo: string;
354
+ // Check if memo is empty
356
355
  if (!memo || memo.trim() === '') {
357
- processedMemo = 'SDK';
356
+ throw this.throwError('MEMO_REQUIRED_ERROR');
357
+ }
358
+
359
+ const trimmedMemo = memo.trim();
360
+ let processedMemo: string;
361
+
362
+ // If memo doesn't start with 'SDK', prepend 'SDK:'
363
+ if (!trimmedMemo.startsWith('SDK')) {
364
+ processedMemo = `SDK:${trimmedMemo}`;
358
365
  } else {
359
- const trimmedMemo = memo.trim();
360
- if (trimmedMemo === 'SDK') {
361
- processedMemo = trimmedMemo;
362
- } else if (!trimmedMemo.startsWith('SDK:')) {
363
- processedMemo = `SDK:${trimmedMemo}`;
364
- } else {
365
- processedMemo = trimmedMemo;
366
- }
366
+ processedMemo = trimmedMemo;
367
367
  }
368
368
 
369
369
  // Validate memo
370
370
  if (processedMemo.length > 64) {
371
- throw new Error(
372
- `Invalid memo: "${processedMemo}". Must be max 64 characters`,
373
- );
371
+ throw this.throwError('MEMO_TOO_LONG_ERROR', processedMemo);
374
372
  }
375
373
 
376
374
  const validPattern = /^[A-Za-z0-9:_-]*$/;
377
375
  if (!validPattern.test(processedMemo)) {
378
- throw new Error(
379
- `Invalid memo: "${processedMemo}". Must contain only [A-Za-z0-9:_-] characters`,
380
- );
376
+ throw this.throwError('MEMO_INVALID_CHARACTERS_ERROR', processedMemo);
381
377
  }
382
378
 
383
379
  return getAddMemoInstruction({ memo: processedMemo });
@@ -1,41 +0,0 @@
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
- });