@hsuite/native-connect-client 1.0.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.
Files changed (107) hide show
  1. package/README.md +51 -0
  2. package/coverage/base.css +224 -0
  3. package/coverage/block-navigation.js +87 -0
  4. package/coverage/coverage-summary.json +10 -0
  5. package/coverage/favicon.png +0 -0
  6. package/coverage/index.html +161 -0
  7. package/coverage/lcov-report/base.css +224 -0
  8. package/coverage/lcov-report/block-navigation.js +87 -0
  9. package/coverage/lcov-report/favicon.png +0 -0
  10. package/coverage/lcov-report/index.html +161 -0
  11. package/coverage/lcov-report/prettify.css +1 -0
  12. package/coverage/lcov-report/prettify.js +2 -0
  13. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  14. package/coverage/lcov-report/sorter.js +210 -0
  15. package/coverage/lcov-report/src/handlers/index.html +146 -0
  16. package/coverage/lcov-report/src/handlers/session-handler.ts.html +223 -0
  17. package/coverage/lcov-report/src/handlers/signing-handler.ts.html +217 -0
  18. package/coverage/lcov-report/src/handlers/wallet-handler.ts.html +193 -0
  19. package/coverage/lcov-report/src/harness/index.html +116 -0
  20. package/coverage/lcov-report/src/harness/signing-harness.ts.html +352 -0
  21. package/coverage/lcov-report/src/index.html +146 -0
  22. package/coverage/lcov-report/src/memory-transport.ts.html +358 -0
  23. package/coverage/lcov-report/src/protocol/e2e-harness.ts.html +568 -0
  24. package/coverage/lcov-report/src/protocol/index.html +116 -0
  25. package/coverage/lcov-report/src/query-client.ts.html +277 -0
  26. package/coverage/lcov-report/src/reference-client.ts.html +691 -0
  27. package/coverage/lcov.info +981 -0
  28. package/coverage/prettify.css +1 -0
  29. package/coverage/prettify.js +2 -0
  30. package/coverage/sort-arrow-sprite.png +0 -0
  31. package/coverage/sorter.js +210 -0
  32. package/coverage/src/handlers/index.html +146 -0
  33. package/coverage/src/handlers/session-handler.ts.html +223 -0
  34. package/coverage/src/handlers/signing-handler.ts.html +217 -0
  35. package/coverage/src/handlers/wallet-handler.ts.html +193 -0
  36. package/coverage/src/harness/index.html +116 -0
  37. package/coverage/src/harness/signing-harness.ts.html +352 -0
  38. package/coverage/src/index.html +146 -0
  39. package/coverage/src/memory-transport.ts.html +358 -0
  40. package/coverage/src/protocol/e2e-harness.ts.html +568 -0
  41. package/coverage/src/protocol/index.html +116 -0
  42. package/coverage/src/query-client.ts.html +277 -0
  43. package/coverage/src/reference-client.ts.html +691 -0
  44. package/dist/handlers/session-handler.d.ts +22 -0
  45. package/dist/handlers/session-handler.d.ts.map +1 -0
  46. package/dist/handlers/session-handler.js +41 -0
  47. package/dist/handlers/session-handler.js.map +1 -0
  48. package/dist/handlers/signing-handler.d.ts +22 -0
  49. package/dist/handlers/signing-handler.d.ts.map +1 -0
  50. package/dist/handlers/signing-handler.js +39 -0
  51. package/dist/handlers/signing-handler.js.map +1 -0
  52. package/dist/handlers/wallet-handler.d.ts +22 -0
  53. package/dist/handlers/wallet-handler.d.ts.map +1 -0
  54. package/dist/handlers/wallet-handler.js +32 -0
  55. package/dist/handlers/wallet-handler.js.map +1 -0
  56. package/dist/harness/signing-harness.d.ts +47 -0
  57. package/dist/harness/signing-harness.d.ts.map +1 -0
  58. package/dist/harness/signing-harness.js +79 -0
  59. package/dist/harness/signing-harness.js.map +1 -0
  60. package/dist/index.d.ts +24 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +24 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/memory-transport-options.d.ts +27 -0
  65. package/dist/memory-transport-options.d.ts.map +1 -0
  66. package/dist/memory-transport-options.js +11 -0
  67. package/dist/memory-transport-options.js.map +1 -0
  68. package/dist/memory-transport.d.ts +55 -0
  69. package/dist/memory-transport.d.ts.map +1 -0
  70. package/dist/memory-transport.js +81 -0
  71. package/dist/memory-transport.js.map +1 -0
  72. package/dist/protocol/e2e-harness.d.ts +65 -0
  73. package/dist/protocol/e2e-harness.d.ts.map +1 -0
  74. package/dist/protocol/e2e-harness.js +130 -0
  75. package/dist/protocol/e2e-harness.js.map +1 -0
  76. package/dist/protocol/index.d.ts +22 -0
  77. package/dist/protocol/index.d.ts.map +1 -0
  78. package/dist/protocol/index.js +22 -0
  79. package/dist/protocol/index.js.map +1 -0
  80. package/dist/query-client.d.ts +41 -0
  81. package/dist/query-client.d.ts.map +1 -0
  82. package/dist/query-client.js +52 -0
  83. package/dist/query-client.js.map +1 -0
  84. package/dist/reference-client.d.ts +100 -0
  85. package/dist/reference-client.d.ts.map +1 -0
  86. package/dist/reference-client.js +149 -0
  87. package/dist/reference-client.js.map +1 -0
  88. package/package.json +46 -0
  89. package/src/handlers/session-handler.ts +46 -0
  90. package/src/handlers/signing-handler.ts +44 -0
  91. package/src/handlers/wallet-handler.ts +36 -0
  92. package/src/harness/signing-harness.ts +89 -0
  93. package/src/index.ts +24 -0
  94. package/src/memory-transport-options.ts +28 -0
  95. package/src/memory-transport.ts +91 -0
  96. package/src/protocol/e2e-harness.ts +161 -0
  97. package/src/protocol/index.ts +23 -0
  98. package/src/query-client.ts +64 -0
  99. package/src/reference-client.spec.ts +408 -0
  100. package/src/reference-client.ts +202 -0
  101. package/tests/e2e-harness.spec.ts +64 -0
  102. package/tests/realnet-session.spec.ts +303 -0
  103. package/tests/signing-harness.spec.ts +41 -0
  104. package/tsconfig.build.json +10 -0
  105. package/tsconfig.build.tsbuildinfo +1 -0
  106. package/tsconfig.json +17 -0
  107. package/vitest.config.ts +35 -0
@@ -0,0 +1,303 @@
1
+ /**
2
+ * @e2e Session-based realnet tests (env-gated)
3
+ * @compodoc
4
+ * Scaffolding for session approval and sign/submit flows against Hedera/XRPL testnets using
5
+ * SDK session controllers. These tests are gated by environment variables and remain skipped
6
+ * unless secrets are provided. Full implementation composes unsigned payloads via adapters,
7
+ * approves a session, signs via SessionFlowController, then submits.
8
+ *
9
+ * Hedera env:
10
+ * - HEDERA_TESTNET_ACCOUNT_ID
11
+ * - HEDERA_TESTNET_PUBLIC_KEY
12
+ * - HEDERA_TESTNET_PRIVATE_KEY
13
+ *
14
+ * XRPL env:
15
+ * - XRPL_TESTNET_ADDRESS
16
+ * - XRPL_TESTNET_SECRET (family seed starting with 's')
17
+ */
18
+ import { describe, it, expect } from 'vitest';
19
+ import {
20
+ WalletCore,
21
+ LedgerRegistry,
22
+ SessionManager,
23
+ CryptoService,
24
+ SessionFlowController,
25
+ } from '@hsuite/native-connect-sdk';
26
+
27
+ const haveHedera = Boolean(
28
+ process.env.HEDERA_TESTNET_ACCOUNT_ID &&
29
+ process.env.HEDERA_TESTNET_PUBLIC_KEY &&
30
+ process.env.HEDERA_TESTNET_PRIVATE_KEY,
31
+ );
32
+ const haveXrpl = Boolean(process.env.XRPL_TESTNET_ADDRESS && process.env.XRPL_TESTNET_SECRET);
33
+
34
+ (haveHedera || haveXrpl ? describe : describe.skip)(
35
+ 'Realnet session-based flows (env-gated)',
36
+ () => {
37
+ it('Hedera: approve session, sign native transfer, attempt submit (optional)', async () => {
38
+ if (!haveHedera) return; // skip when not configured
39
+ const registry = new LedgerRegistry({
40
+ context: {
41
+ secureRandom: (n) => crypto.getRandomValues(new Uint8Array(n)),
42
+ httpClient: {
43
+ async get() {
44
+ return {} as unknown as never;
45
+ },
46
+ async post() {
47
+ return {} as unknown as never;
48
+ },
49
+ },
50
+ },
51
+ });
52
+ const sessionManager = new SessionManager();
53
+ const mem = new Map<string, Uint8Array>();
54
+ const secureMemory = {
55
+ async store(id?: string, value?: Uint8Array) {
56
+ if (id && value) mem.set(id, value);
57
+ },
58
+ async retrieve(id?: string) {
59
+ return id ? mem.get(id) : undefined;
60
+ },
61
+ async remove(id?: string) {
62
+ if (id) mem.delete(id);
63
+ },
64
+ async clear() {
65
+ mem.clear();
66
+ },
67
+ };
68
+ const sessionFlow = new SessionFlowController({ registry, sessionManager, secureMemory });
69
+ const wallet = new WalletCore({
70
+ registry,
71
+ sessionManager,
72
+ keyVault: {
73
+ async storeSeed() {
74
+ return { id: 'seed' } as never;
75
+ },
76
+ async listSeeds() {
77
+ return [] as never;
78
+ },
79
+ async storeKey() {},
80
+ async listKeys() {
81
+ return [] as never;
82
+ },
83
+ async getConfig() {
84
+ return undefined as never;
85
+ },
86
+ async setConfig() {},
87
+ async createRecoveryBundle() {
88
+ throw new Error('no-op');
89
+ },
90
+ async deleteRecoveryBundle() {},
91
+ async listRecoveryBundles() {
92
+ return [] as never;
93
+ },
94
+ } as never,
95
+ crypto: new CryptoService(),
96
+ secureMemory,
97
+ });
98
+
99
+ const { HederaLedgerAdapter } = await import('../../ledger-hedera/dist/index.js');
100
+ await wallet.registerLedger(new HederaLedgerAdapter());
101
+
102
+ // Import private key into secure memory (store under address and publicKey aliases)
103
+ await wallet.importPrivateKey({
104
+ ledgerId: 'hedera',
105
+ networkId: 'hedera:testnet',
106
+ address: process.env.HEDERA_TESTNET_ACCOUNT_ID!,
107
+ publicKey: process.env.HEDERA_TESTNET_PUBLIC_KEY!,
108
+ privateKey: new TextEncoder().encode(process.env.HEDERA_TESTNET_PRIVATE_KEY!),
109
+ metadata: { label: 'HBAR Realnet' },
110
+ } as never);
111
+
112
+ const sessionId = await sessionFlow.create({
113
+ appId: 'realnet-suite',
114
+ appName: 'Realnet Suite',
115
+ ledgerId: 'hedera',
116
+ networkId: 'hedera:testnet',
117
+ nonce: crypto.randomUUID(),
118
+ timestamp: Date.now(),
119
+ permissions: ['sign', 'submit'],
120
+ purpose: [],
121
+ } as never);
122
+
123
+ await sessionFlow.approve(sessionId, [
124
+ {
125
+ ledgerId: 'hedera',
126
+ networkId: 'hedera:testnet',
127
+ address: process.env.HEDERA_TESTNET_ACCOUNT_ID!,
128
+ publicKey: process.env.HEDERA_TESTNET_PUBLIC_KEY!,
129
+ metadata: {},
130
+ },
131
+ ]);
132
+
133
+ // Compose unsigned native transfer
134
+ const adapter = registry.get('hedera')!.adapter as unknown as {
135
+ composeTransferNative: (p: {
136
+ from: { address: string; ledgerId: string; networkId: string };
137
+ to: string;
138
+ amount: string;
139
+ }) => Promise<Uint8Array>;
140
+ };
141
+ const unsigned = await adapter.composeTransferNative({
142
+ from: {
143
+ ledgerId: 'hedera',
144
+ networkId: 'hedera:testnet',
145
+ address: process.env.HEDERA_TESTNET_ACCOUNT_ID!,
146
+ },
147
+ to: process.env.HEDERA_TESTNET_ACCOUNT_ID!,
148
+ amount: '1',
149
+ });
150
+
151
+ const result = await sessionFlow.sign(sessionId, {
152
+ ledgerId: 'hedera',
153
+ networkId: 'hedera:testnet',
154
+ accountAddress: process.env.HEDERA_TESTNET_PUBLIC_KEY!,
155
+ payload: unsigned,
156
+ } as never);
157
+ expect(result.signature.length).toBeGreaterThan(0);
158
+
159
+ // Submit; accept invalid signature temporarily
160
+ const signedBytes = Uint8Array.from(Buffer.from(result.signature, 'hex'));
161
+ const submission = await sessionFlow
162
+ .submit(sessionId, {
163
+ ledgerId: 'hedera',
164
+ networkId: 'hedera:testnet',
165
+ accountAddress: process.env.HEDERA_TESTNET_ACCOUNT_ID!,
166
+ payload: signedBytes,
167
+ } as never)
168
+ .then((h) => String(h))
169
+ .catch((e) => String(e?.message ?? e));
170
+ expect(typeof submission).toBe('string');
171
+ });
172
+
173
+ it('XRPL: approve session, trustline compose→sign→submit (optional)', async () => {
174
+ if (!haveXrpl) return; // skip when not configured
175
+ const registry = new LedgerRegistry({
176
+ context: {
177
+ secureRandom: (n) => crypto.getRandomValues(new Uint8Array(n)),
178
+ httpClient: {
179
+ async get() {
180
+ return {} as unknown as never;
181
+ },
182
+ async post() {
183
+ return {} as unknown as never;
184
+ },
185
+ },
186
+ },
187
+ });
188
+ const sessionManager = new SessionManager();
189
+ const mem = new Map<string, Uint8Array>();
190
+ const secureMemory = {
191
+ async store(id?: string, value?: Uint8Array) {
192
+ if (id && value) mem.set(id, value);
193
+ },
194
+ async retrieve(id?: string) {
195
+ return id ? mem.get(id) : undefined;
196
+ },
197
+ async remove(id?: string) {
198
+ if (id) mem.delete(id);
199
+ },
200
+ async clear() {
201
+ mem.clear();
202
+ },
203
+ };
204
+ const sessionFlow = new SessionFlowController({ registry, sessionManager, secureMemory });
205
+ const wallet = new WalletCore({
206
+ registry,
207
+ sessionManager,
208
+ keyVault: {
209
+ async storeSeed() {
210
+ return { id: 'seed' } as never;
211
+ },
212
+ async listSeeds() {
213
+ return [] as never;
214
+ },
215
+ async storeKey() {},
216
+ async listKeys() {
217
+ return [] as never;
218
+ },
219
+ async getConfig() {
220
+ return undefined as never;
221
+ },
222
+ async setConfig() {},
223
+ async createRecoveryBundle() {
224
+ throw new Error('no-op');
225
+ },
226
+ async deleteRecoveryBundle() {},
227
+ async listRecoveryBundles() {
228
+ return [] as never;
229
+ },
230
+ } as never,
231
+ crypto: new CryptoService(),
232
+ secureMemory,
233
+ });
234
+
235
+ const { XrplLedgerAdapter } = await import('../../ledger-xrpl/dist/index.js');
236
+ await wallet.registerLedger(new XrplLedgerAdapter());
237
+
238
+ // Import XRPL secret as UTF-8 bytes under the account address
239
+ await wallet.importPrivateKey({
240
+ ledgerId: 'xrpl',
241
+ networkId: 'xrpl:testnet',
242
+ address: process.env.XRPL_TESTNET_ADDRESS!,
243
+ publicKey: '',
244
+ privateKey: new TextEncoder().encode(process.env.XRPL_TESTNET_SECRET!),
245
+ metadata: { label: 'XRP Realnet' },
246
+ } as never);
247
+
248
+ const sessionId = await sessionFlow.create({
249
+ appId: 'realnet-suite',
250
+ appName: 'Realnet Suite',
251
+ ledgerId: 'xrpl',
252
+ networkId: 'xrpl:testnet',
253
+ nonce: crypto.randomUUID(),
254
+ timestamp: Date.now(),
255
+ permissions: ['sign', 'submit'],
256
+ purpose: [],
257
+ } as never);
258
+ await sessionFlow.approve(sessionId, [
259
+ {
260
+ ledgerId: 'xrpl',
261
+ networkId: 'xrpl:testnet',
262
+ address: process.env.XRPL_TESTNET_ADDRESS!,
263
+ publicKey: '',
264
+ metadata: {},
265
+ },
266
+ ]);
267
+
268
+ const adapter = registry.get('xrpl')!.adapter as unknown as {
269
+ composeEnableAsset: (p: {
270
+ kind: 'fungible';
271
+ account: { address: string; ledgerId: string; networkId: string };
272
+ ref: { symbol: string; issuer: string };
273
+ }) => Promise<Uint8Array>;
274
+ };
275
+ const unsigned = await adapter.composeEnableAsset({
276
+ kind: 'fungible',
277
+ account: {
278
+ ledgerId: 'xrpl',
279
+ networkId: 'xrpl:testnet',
280
+ address: process.env.XRPL_TESTNET_ADDRESS!,
281
+ },
282
+ ref: { symbol: 'USD', issuer: 'rPT1Sjq2YGrBMTttX4GZHjKu9dyfzbpAYe' },
283
+ });
284
+
285
+ const signed = await sessionFlow.sign(sessionId, {
286
+ ledgerId: 'xrpl',
287
+ networkId: 'xrpl:testnet',
288
+ accountAddress: process.env.XRPL_TESTNET_ADDRESS!,
289
+ payload: unsigned,
290
+ } as never);
291
+ expect(signed.signature.length).toBeGreaterThan(0);
292
+
293
+ const signedBytes = Uint8Array.from(Buffer.from(signed.signature, 'hex'));
294
+ const hash = await sessionFlow.submit(sessionId, {
295
+ ledgerId: 'xrpl',
296
+ networkId: 'xrpl:testnet',
297
+ accountAddress: process.env.XRPL_TESTNET_ADDRESS!,
298
+ payload: signedBytes,
299
+ } as never);
300
+ expect(typeof hash).toBe('string');
301
+ });
302
+ },
303
+ );
@@ -0,0 +1,41 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import { RpcRouter, setLogSink, type LogEvent, getLogSink } from '@hsuite/native-connect-sdk';
3
+ import { SigningHarness } from '../src/harness/signing-harness';
4
+
5
+ describe('SigningHarness', () => {
6
+ it('responds with simulated signature', async () => {
7
+ const previousSink = getLogSink();
8
+ const events: LogEvent[] = [];
9
+ setLogSink((event) => {
10
+ events.push(event);
11
+ });
12
+
13
+ const router = new RpcRouter();
14
+ router.register('ledger/sign', async () => ({
15
+ signature: 'demo',
16
+ payloadHash: 'hash',
17
+ publicKey: 'key',
18
+ algorithm: 'ed25519',
19
+ }));
20
+
21
+ const harness = new SigningHarness(router);
22
+ const response = await harness.send({
23
+ jsonrpc: '2.0',
24
+ id: '11111111-1111-1111-1111-111111111111',
25
+ method: 'ledger/sign',
26
+ params: {
27
+ ledgerId: 'xrpl',
28
+ networkId: 'xrpl:testnet',
29
+ accountAddress: 'r123',
30
+ payload: 'ABC=',
31
+ },
32
+ timestamp: Date.now(),
33
+ });
34
+
35
+ expect(response).toMatchObject({ id: '11111111-1111-1111-1111-111111111111', jsonrpc: '2.0' });
36
+ expect(events.some((event) => event.scope?.includes('ReferenceClient'))).toBe(true);
37
+
38
+ harness.destroy();
39
+ setLogSink(previousSink);
40
+ });
41
+ });
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "declaration": true,
5
+ "declarationMap": true,
6
+ "sourceMap": true
7
+ },
8
+ "exclude": ["src/**/*.spec.ts", "tests/**/*", "vitest.config.ts"],
9
+ "references": [{ "path": "../native-wallet-sdk" }, { "path": "../types" }]
10
+ }