@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 +9 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +13 -17
- package/dist/index.mjs +13 -17
- package/package.json +13 -3
- package/src/__tests__/processMemo.test.ts +146 -0
- package/src/__tests__/{transaction-size.ts → transaction-size.test.ts} +6 -4
- package/src/constants/errors.ts +5 -0
- package/src/hysp.ts +13 -17
- package/src/__tests__/index.ts +0 -41
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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
|
|
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(
|
|
56
|
-
|
|
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);
|
package/src/constants/errors.ts
CHANGED
|
@@ -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
|
-
//
|
|
355
|
-
let processedMemo: string;
|
|
354
|
+
// Check if memo is empty
|
|
356
355
|
if (!memo || memo.trim() === '') {
|
|
357
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 });
|
package/src/__tests__/index.ts
DELETED
|
@@ -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
|
-
});
|