@karn_lat/protocol-sdk 0.1.0-alpha.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.
Files changed (157) hide show
  1. package/README.md +68 -0
  2. package/dist/__tests__/setup.d.ts +14 -0
  3. package/dist/__tests__/setup.d.ts.map +1 -0
  4. package/dist/__tests__/setup.js +44 -0
  5. package/dist/__tests__/setup.js.map +1 -0
  6. package/dist/clients/GovernorClient.d.ts +9 -0
  7. package/dist/clients/GovernorClient.d.ts.map +1 -0
  8. package/dist/clients/GovernorClient.js +18 -0
  9. package/dist/clients/GovernorClient.js.map +1 -0
  10. package/dist/clients/TreasuryClient.d.ts +9 -0
  11. package/dist/clients/TreasuryClient.d.ts.map +1 -0
  12. package/dist/clients/TreasuryClient.js +18 -0
  13. package/dist/clients/TreasuryClient.js.map +1 -0
  14. package/dist/clients/ValocracyClient.d.ts +13 -0
  15. package/dist/clients/ValocracyClient.d.ts.map +1 -0
  16. package/dist/clients/ValocracyClient.js +32 -0
  17. package/dist/clients/ValocracyClient.js.map +1 -0
  18. package/dist/clients/index.d.ts +4 -0
  19. package/dist/clients/index.d.ts.map +1 -0
  20. package/dist/clients/index.js +4 -0
  21. package/dist/clients/index.js.map +1 -0
  22. package/dist/generated/governor/src/index.d.ts +400 -0
  23. package/dist/generated/governor/src/index.d.ts.map +1 -0
  24. package/dist/generated/governor/src/index.js +63 -0
  25. package/dist/generated/governor/src/index.js.map +1 -0
  26. package/dist/generated/treasury/src/index.d.ts +474 -0
  27. package/dist/generated/treasury/src/index.d.ts.map +1 -0
  28. package/dist/generated/treasury/src/index.js +54 -0
  29. package/dist/generated/treasury/src/index.js.map +1 -0
  30. package/dist/generated/valocracy/src/index.d.ts +807 -0
  31. package/dist/generated/valocracy/src/index.d.ts.map +1 -0
  32. package/dist/generated/valocracy/src/index.js +114 -0
  33. package/dist/generated/valocracy/src/index.js.map +1 -0
  34. package/dist/index.d.ts +5 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +5 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/react/hooks/useGovernor.d.ts +24 -0
  39. package/dist/react/hooks/useGovernor.d.ts.map +1 -0
  40. package/dist/react/hooks/useGovernor.js +45 -0
  41. package/dist/react/hooks/useGovernor.js.map +1 -0
  42. package/dist/react/hooks/useMultiWallet.d.ts +35 -0
  43. package/dist/react/hooks/useMultiWallet.d.ts.map +1 -0
  44. package/dist/react/hooks/useMultiWallet.js +87 -0
  45. package/dist/react/hooks/useMultiWallet.js.map +1 -0
  46. package/dist/react/hooks/useTreasury.d.ts +14 -0
  47. package/dist/react/hooks/useTreasury.d.ts.map +1 -0
  48. package/dist/react/hooks/useTreasury.js +45 -0
  49. package/dist/react/hooks/useTreasury.js.map +1 -0
  50. package/dist/react/hooks/useValocracy.d.ts +16 -0
  51. package/dist/react/hooks/useValocracy.d.ts.map +1 -0
  52. package/dist/react/hooks/useValocracy.js +54 -0
  53. package/dist/react/hooks/useValocracy.js.map +1 -0
  54. package/dist/react/hooks/useWallet.d.ts +13 -0
  55. package/dist/react/hooks/useWallet.d.ts.map +1 -0
  56. package/dist/react/hooks/useWallet.js +51 -0
  57. package/dist/react/hooks/useWallet.js.map +1 -0
  58. package/dist/react/index.d.ts +7 -0
  59. package/dist/react/index.d.ts.map +1 -0
  60. package/dist/react/index.js +7 -0
  61. package/dist/react/index.js.map +1 -0
  62. package/dist/react/providers/KarnProvider.d.ts +25 -0
  63. package/dist/react/providers/KarnProvider.d.ts.map +1 -0
  64. package/dist/react/providers/KarnProvider.js +25 -0
  65. package/dist/react/providers/KarnProvider.js.map +1 -0
  66. package/dist/utils/decay.d.ts +19 -0
  67. package/dist/utils/decay.d.ts.map +1 -0
  68. package/dist/utils/decay.js +33 -0
  69. package/dist/utils/decay.js.map +1 -0
  70. package/dist/utils/index.d.ts +4 -0
  71. package/dist/utils/index.d.ts.map +1 -0
  72. package/dist/utils/index.js +4 -0
  73. package/dist/utils/index.js.map +1 -0
  74. package/dist/utils/polling.d.ts +75 -0
  75. package/dist/utils/polling.d.ts.map +1 -0
  76. package/dist/utils/polling.js +104 -0
  77. package/dist/utils/polling.js.map +1 -0
  78. package/dist/utils/simulation.d.ts +67 -0
  79. package/dist/utils/simulation.d.ts.map +1 -0
  80. package/dist/utils/simulation.js +88 -0
  81. package/dist/utils/simulation.js.map +1 -0
  82. package/dist/wallet/WalletManager.d.ts +77 -0
  83. package/dist/wallet/WalletManager.d.ts.map +1 -0
  84. package/dist/wallet/WalletManager.js +268 -0
  85. package/dist/wallet/WalletManager.js.map +1 -0
  86. package/dist/wallet/adapters/AlbedoAdapter.d.ts +47 -0
  87. package/dist/wallet/adapters/AlbedoAdapter.d.ts.map +1 -0
  88. package/dist/wallet/adapters/AlbedoAdapter.js +84 -0
  89. package/dist/wallet/adapters/AlbedoAdapter.js.map +1 -0
  90. package/dist/wallet/adapters/FreighterAdapter.d.ts +42 -0
  91. package/dist/wallet/adapters/FreighterAdapter.d.ts.map +1 -0
  92. package/dist/wallet/adapters/FreighterAdapter.js +107 -0
  93. package/dist/wallet/adapters/FreighterAdapter.js.map +1 -0
  94. package/dist/wallet/adapters/LobstrAdapter.d.ts +34 -0
  95. package/dist/wallet/adapters/LobstrAdapter.d.ts.map +1 -0
  96. package/dist/wallet/adapters/LobstrAdapter.js +89 -0
  97. package/dist/wallet/adapters/LobstrAdapter.js.map +1 -0
  98. package/dist/wallet/adapters/RabetAdapter.d.ts +39 -0
  99. package/dist/wallet/adapters/RabetAdapter.d.ts.map +1 -0
  100. package/dist/wallet/adapters/RabetAdapter.js +104 -0
  101. package/dist/wallet/adapters/RabetAdapter.js.map +1 -0
  102. package/dist/wallet/adapters/xBullAdapter.d.ts +41 -0
  103. package/dist/wallet/adapters/xBullAdapter.d.ts.map +1 -0
  104. package/dist/wallet/adapters/xBullAdapter.js +72 -0
  105. package/dist/wallet/adapters/xBullAdapter.js.map +1 -0
  106. package/dist/wallet/index.d.ts +20 -0
  107. package/dist/wallet/index.d.ts.map +1 -0
  108. package/dist/wallet/index.js +23 -0
  109. package/dist/wallet/index.js.map +1 -0
  110. package/dist/wallet/types.d.ts +165 -0
  111. package/dist/wallet/types.d.ts.map +1 -0
  112. package/dist/wallet/types.js +50 -0
  113. package/dist/wallet/types.js.map +1 -0
  114. package/examples/basic-usage.ts +28 -0
  115. package/jest.config.js +37 -0
  116. package/package.json +58 -0
  117. package/src/__tests__/README.md +364 -0
  118. package/src/__tests__/setup.ts +57 -0
  119. package/src/__tests__/utils/decay.test.ts +331 -0
  120. package/src/__tests__/wallet/WalletManager.test.ts +410 -0
  121. package/src/clients/GovernorClient.ts +23 -0
  122. package/src/clients/TreasuryClient.ts +23 -0
  123. package/src/clients/ValocracyClient.ts +48 -0
  124. package/src/clients/index.ts +3 -0
  125. package/src/generated/governor/README.md +54 -0
  126. package/src/generated/governor/package.json +17 -0
  127. package/src/generated/governor/src/index.ts +428 -0
  128. package/src/generated/governor/tsconfig.json +98 -0
  129. package/src/generated/treasury/README.md +54 -0
  130. package/src/generated/treasury/package.json +17 -0
  131. package/src/generated/treasury/src/index.ts +495 -0
  132. package/src/generated/treasury/tsconfig.json +98 -0
  133. package/src/generated/valocracy/README.md +54 -0
  134. package/src/generated/valocracy/package.json +17 -0
  135. package/src/generated/valocracy/src/index.ts +831 -0
  136. package/src/generated/valocracy/tsconfig.json +98 -0
  137. package/src/index.ts +4 -0
  138. package/src/react/hooks/useGovernor.ts +69 -0
  139. package/src/react/hooks/useMultiWallet.ts +169 -0
  140. package/src/react/hooks/useTreasury.ts +57 -0
  141. package/src/react/hooks/useValocracy.ts +66 -0
  142. package/src/react/hooks/useWallet.ts +60 -0
  143. package/src/react/index.ts +6 -0
  144. package/src/react/providers/KarnProvider.tsx +63 -0
  145. package/src/utils/decay.ts +44 -0
  146. package/src/utils/index.ts +3 -0
  147. package/src/utils/polling.ts +193 -0
  148. package/src/utils/simulation.ts +136 -0
  149. package/src/wallet/WalletManager.ts +360 -0
  150. package/src/wallet/adapters/AlbedoAdapter.ts +140 -0
  151. package/src/wallet/adapters/FreighterAdapter.ts +179 -0
  152. package/src/wallet/adapters/LobstrAdapter.ts +142 -0
  153. package/src/wallet/adapters/RabetAdapter.ts +162 -0
  154. package/src/wallet/adapters/xBullAdapter.ts +123 -0
  155. package/src/wallet/index.ts +37 -0
  156. package/src/wallet/types.ts +204 -0
  157. package/tsconfig.json +40 -0
@@ -0,0 +1,410 @@
1
+ /**
2
+ * Tests for WalletManager
3
+ */
4
+
5
+ import { WalletManager } from '../../wallet/WalletManager.js';
6
+ import {
7
+ WalletType,
8
+ WalletEvent,
9
+ WalletErrorCode,
10
+ WalletError,
11
+ } from '../../wallet/types.js';
12
+
13
+ // Mock wallet APIs
14
+ const mockFreighterAPI = {
15
+ isConnected: jest.fn(),
16
+ getPublicKey: jest.fn(),
17
+ signTransaction: jest.fn(),
18
+ getNetwork: jest.fn(),
19
+ getNetworkDetails: jest.fn(),
20
+ };
21
+
22
+ const mockAlbedoAPI = {
23
+ publicKey: jest.fn(),
24
+ tx: jest.fn(),
25
+ trust: jest.fn(),
26
+ };
27
+
28
+ const mockLobstrAPI = {
29
+ isConnected: jest.fn(),
30
+ getPublicKey: jest.fn(),
31
+ signTransaction: jest.fn(),
32
+ };
33
+
34
+ describe('WalletManager', () => {
35
+ let manager: WalletManager;
36
+
37
+ beforeEach(() => {
38
+ // Reset all mocks
39
+ jest.clearAllMocks();
40
+
41
+ // Reset window mocks
42
+ (global.window as any) = {
43
+ freighter: undefined,
44
+ albedo: undefined,
45
+ lobstrExtension: undefined,
46
+ xBullSDK: undefined,
47
+ rabet: undefined,
48
+ localStorage: {
49
+ getItem: jest.fn(),
50
+ setItem: jest.fn(),
51
+ removeItem: jest.fn(),
52
+ clear: jest.fn(),
53
+ },
54
+ };
55
+
56
+ manager = new WalletManager();
57
+ });
58
+
59
+ describe('Initialization', () => {
60
+ it('should initialize with default state', () => {
61
+ const state = manager.getState();
62
+
63
+ expect(state.isConnected).toBe(false);
64
+ expect(state.address).toBeNull();
65
+ expect(state.walletType).toBeNull();
66
+ expect(state.isConnecting).toBe(false);
67
+ expect(state.error).toBeNull();
68
+ expect(state.walletName).toBeNull();
69
+ });
70
+
71
+ it('should initialize all wallet adapters', async () => {
72
+ const wallets = manager.getAllWallets();
73
+
74
+ expect(wallets).toHaveLength(5);
75
+ expect(wallets.map(w => w.type)).toEqual([
76
+ WalletType.FREIGHTER,
77
+ WalletType.ALBEDO,
78
+ WalletType.LOBSTR,
79
+ WalletType.XBULL,
80
+ WalletType.RABET,
81
+ ]);
82
+ });
83
+ });
84
+
85
+ describe('getAvailableWallets', () => {
86
+ it('should return empty array when no wallets installed', async () => {
87
+ const available = await manager.getAvailableWallets();
88
+
89
+ expect(available).toEqual([]);
90
+ });
91
+
92
+ it('should return Freighter when installed', async () => {
93
+ (global.window as any).freighter = mockFreighterAPI;
94
+ manager = new WalletManager();
95
+
96
+ const available = await manager.getAvailableWallets();
97
+
98
+ expect(available).toHaveLength(1);
99
+ expect(available[0].type).toBe(WalletType.FREIGHTER);
100
+ expect(available[0].name).toBe('Freighter');
101
+ });
102
+
103
+ it('should return Albedo (always available as web-based)', async () => {
104
+ // Albedo is web-based, so it's always "available"
105
+ const manager2 = new WalletManager();
106
+ const available = await manager2.getAvailableWallets();
107
+
108
+ // Albedo should be in the list (or at least detectable)
109
+ // Note: Actual implementation may vary
110
+ const hasAlbedo = available.some(w => w.type === WalletType.ALBEDO);
111
+ expect(hasAlbedo || available.length === 0).toBe(true);
112
+ });
113
+
114
+ it('should return multiple wallets when multiple installed', async () => {
115
+ (global.window as any).freighter = mockFreighterAPI;
116
+ (global.window as any).lobstrExtension = mockLobstrAPI;
117
+ manager = new WalletManager();
118
+
119
+ const available = await manager.getAvailableWallets();
120
+
121
+ expect(available.length).toBeGreaterThanOrEqual(2);
122
+ const types = available.map(w => w.type);
123
+ expect(types).toContain(WalletType.FREIGHTER);
124
+ expect(types).toContain(WalletType.LOBSTR);
125
+ });
126
+ });
127
+
128
+ describe('connect', () => {
129
+ it('should connect to Freighter successfully', async () => {
130
+ (global.window as any).freighter = mockFreighterAPI;
131
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
132
+ manager = new WalletManager();
133
+
134
+ const connection = await manager.connect(WalletType.FREIGHTER);
135
+
136
+ expect(connection.walletType).toBe(WalletType.FREIGHTER);
137
+ expect(connection.address).toBe('GTEST123...');
138
+ expect(mockFreighterAPI.getPublicKey).toHaveBeenCalled();
139
+
140
+ const state = manager.getState();
141
+ expect(state.isConnected).toBe(true);
142
+ expect(state.address).toBe('GTEST123...');
143
+ expect(state.walletType).toBe(WalletType.FREIGHTER);
144
+ expect(state.walletName).toBe('Freighter');
145
+ });
146
+
147
+ it('should throw NOT_INSTALLED error when wallet not installed', async () => {
148
+ await expect(manager.connect(WalletType.FREIGHTER))
149
+ .rejects
150
+ .toThrow(WalletError);
151
+
152
+ try {
153
+ await manager.connect(WalletType.FREIGHTER);
154
+ } catch (error) {
155
+ expect(error).toBeInstanceOf(WalletError);
156
+ expect((error as WalletError).code).toBe(WalletErrorCode.NOT_INSTALLED);
157
+ expect((error as WalletError).walletType).toBe(WalletType.FREIGHTER);
158
+ }
159
+ });
160
+
161
+ it('should throw USER_REJECTED error when user declines', async () => {
162
+ (global.window as any).freighter = mockFreighterAPI;
163
+ mockFreighterAPI.getPublicKey.mockResolvedValue('');
164
+ manager = new WalletManager();
165
+
166
+ await expect(manager.connect(WalletType.FREIGHTER))
167
+ .rejects
168
+ .toThrow(WalletError);
169
+
170
+ try {
171
+ await manager.connect(WalletType.FREIGHTER);
172
+ } catch (error) {
173
+ expect((error as WalletError).code).toBe(WalletErrorCode.USER_REJECTED);
174
+ }
175
+ });
176
+
177
+ it('should save connection to localStorage', async () => {
178
+ const setItem = jest.fn();
179
+ (global.window as any).localStorage.setItem = setItem;
180
+ (global.window as any).freighter = mockFreighterAPI;
181
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
182
+ manager = new WalletManager();
183
+
184
+ await manager.connect(WalletType.FREIGHTER);
185
+
186
+ expect(setItem).toHaveBeenCalledWith(
187
+ 'karn_wallet_connection',
188
+ expect.stringContaining('FREIGHTER')
189
+ );
190
+ });
191
+
192
+ it('should disconnect previous wallet before connecting new one', async () => {
193
+ (global.window as any).freighter = mockFreighterAPI;
194
+ (global.window as any).lobstrExtension = mockLobstrAPI;
195
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GFREIGHTER...');
196
+ mockLobstrAPI.getPublicKey.mockResolvedValue('GLOBSTR...');
197
+ manager = new WalletManager();
198
+
199
+ // Connect to Freighter
200
+ await manager.connect(WalletType.FREIGHTER);
201
+ expect(manager.getState().walletType).toBe(WalletType.FREIGHTER);
202
+
203
+ // Connect to Lobstr (should disconnect Freighter first)
204
+ await manager.connect(WalletType.LOBSTR);
205
+ expect(manager.getState().walletType).toBe(WalletType.LOBSTR);
206
+ expect(manager.getState().address).toBe('GLOBSTR...');
207
+ });
208
+ });
209
+
210
+ describe('disconnect', () => {
211
+ it('should disconnect successfully', async () => {
212
+ (global.window as any).freighter = mockFreighterAPI;
213
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
214
+ manager = new WalletManager();
215
+
216
+ await manager.connect(WalletType.FREIGHTER);
217
+ await manager.disconnect();
218
+
219
+ const state = manager.getState();
220
+ expect(state.isConnected).toBe(false);
221
+ expect(state.address).toBeNull();
222
+ expect(state.walletType).toBeNull();
223
+ expect(state.walletName).toBeNull();
224
+ });
225
+
226
+ it('should clear localStorage on disconnect', async () => {
227
+ const removeItem = jest.fn();
228
+ (global.window as any).localStorage.removeItem = removeItem;
229
+ (global.window as any).freighter = mockFreighterAPI;
230
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
231
+ manager = new WalletManager();
232
+
233
+ await manager.connect(WalletType.FREIGHTER);
234
+ await manager.disconnect();
235
+
236
+ expect(removeItem).toHaveBeenCalledWith('karn_wallet_connection');
237
+ });
238
+
239
+ it('should handle disconnect when not connected', async () => {
240
+ await expect(manager.disconnect()).resolves.not.toThrow();
241
+ });
242
+ });
243
+
244
+ describe('signTransaction', () => {
245
+ it('should sign transaction with connected wallet', async () => {
246
+ (global.window as any).freighter = mockFreighterAPI;
247
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
248
+ mockFreighterAPI.signTransaction.mockResolvedValue('signed_xdr_123');
249
+ manager = new WalletManager();
250
+
251
+ await manager.connect(WalletType.FREIGHTER);
252
+ const signedXdr = await manager.signTransaction('unsigned_xdr_123');
253
+
254
+ expect(signedXdr).toBe('signed_xdr_123');
255
+ expect(mockFreighterAPI.signTransaction).toHaveBeenCalledWith(
256
+ 'unsigned_xdr_123',
257
+ expect.any(Object)
258
+ );
259
+ });
260
+
261
+ it('should throw NOT_CONNECTED error when not connected', async () => {
262
+ await expect(manager.signTransaction('xdr'))
263
+ .rejects
264
+ .toThrow(WalletError);
265
+
266
+ try {
267
+ await manager.signTransaction('xdr');
268
+ } catch (error) {
269
+ expect((error as WalletError).code).toBe(WalletErrorCode.NOT_CONNECTED);
270
+ }
271
+ });
272
+
273
+ it('should pass network passphrase to wallet', async () => {
274
+ (global.window as any).freighter = mockFreighterAPI;
275
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
276
+ mockFreighterAPI.signTransaction.mockResolvedValue('signed');
277
+ manager = new WalletManager();
278
+
279
+ await manager.connect(WalletType.FREIGHTER);
280
+ await manager.signTransaction('xdr', {
281
+ networkPassphrase: 'Test SDF Network ; September 2015',
282
+ });
283
+
284
+ expect(mockFreighterAPI.signTransaction).toHaveBeenCalledWith(
285
+ 'xdr',
286
+ expect.objectContaining({
287
+ networkPassphrase: 'Test SDF Network ; September 2015',
288
+ })
289
+ );
290
+ });
291
+ });
292
+
293
+ describe('Event System', () => {
294
+ it('should emit CONNECT event when connecting', async () => {
295
+ (global.window as any).freighter = mockFreighterAPI;
296
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
297
+ manager = new WalletManager();
298
+
299
+ const connectListener = jest.fn();
300
+ manager.on(WalletEvent.CONNECT, connectListener);
301
+
302
+ await manager.connect(WalletType.FREIGHTER);
303
+
304
+ expect(connectListener).toHaveBeenCalledWith({
305
+ walletType: WalletType.FREIGHTER,
306
+ address: 'GTEST123...',
307
+ });
308
+ });
309
+
310
+ it('should emit DISCONNECT event when disconnecting', async () => {
311
+ (global.window as any).freighter = mockFreighterAPI;
312
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
313
+ manager = new WalletManager();
314
+
315
+ const disconnectListener = jest.fn();
316
+ manager.on(WalletEvent.DISCONNECT, disconnectListener);
317
+
318
+ await manager.connect(WalletType.FREIGHTER);
319
+ await manager.disconnect();
320
+
321
+ expect(disconnectListener).toHaveBeenCalledWith({
322
+ walletType: WalletType.FREIGHTER,
323
+ });
324
+ });
325
+
326
+ it('should allow removing event listeners', async () => {
327
+ (global.window as any).freighter = mockFreighterAPI;
328
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
329
+ manager = new WalletManager();
330
+
331
+ const listener = jest.fn();
332
+ manager.on(WalletEvent.CONNECT, listener);
333
+ manager.off(WalletEvent.CONNECT, listener);
334
+
335
+ await manager.connect(WalletType.FREIGHTER);
336
+
337
+ expect(listener).not.toHaveBeenCalled();
338
+ });
339
+
340
+ it('should support multiple listeners for same event', async () => {
341
+ (global.window as any).freighter = mockFreighterAPI;
342
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
343
+ manager = new WalletManager();
344
+
345
+ const listener1 = jest.fn();
346
+ const listener2 = jest.fn();
347
+ manager.on(WalletEvent.CONNECT, listener1);
348
+ manager.on(WalletEvent.CONNECT, listener2);
349
+
350
+ await manager.connect(WalletType.FREIGHTER);
351
+
352
+ expect(listener1).toHaveBeenCalled();
353
+ expect(listener2).toHaveBeenCalled();
354
+ });
355
+ });
356
+
357
+ describe('Auto-Reconnect', () => {
358
+ it('should attempt to restore connection from localStorage', async () => {
359
+ const getItem = jest.fn().mockReturnValue(
360
+ JSON.stringify({ walletType: WalletType.FREIGHTER, address: 'GTEST123...' })
361
+ );
362
+ (global.window as any).localStorage.getItem = getItem;
363
+ (global.window as any).freighter = mockFreighterAPI;
364
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
365
+
366
+ // Wait a bit for auto-reconnect
367
+ manager = new WalletManager();
368
+ await new Promise(resolve => setTimeout(resolve, 100));
369
+
370
+ expect(getItem).toHaveBeenCalledWith('karn_wallet_connection');
371
+ });
372
+
373
+ it('should handle auto-reconnect failure gracefully', async () => {
374
+ const getItem = jest.fn().mockReturnValue(
375
+ JSON.stringify({ walletType: WalletType.FREIGHTER })
376
+ );
377
+ (global.window as any).localStorage.getItem = getItem;
378
+ // Freighter not installed
379
+
380
+ expect(() => new WalletManager()).not.toThrow();
381
+ });
382
+ });
383
+
384
+ describe('getNetwork', () => {
385
+ it('should return network from wallet that supports it', async () => {
386
+ (global.window as any).freighter = mockFreighterAPI;
387
+ mockFreighterAPI.getPublicKey.mockResolvedValue('GTEST123...');
388
+ mockFreighterAPI.getNetwork.mockResolvedValue('TESTNET');
389
+ manager = new WalletManager();
390
+
391
+ await manager.connect(WalletType.FREIGHTER);
392
+ const network = await manager.getNetwork();
393
+
394
+ expect(network).toBe('TESTNET');
395
+ });
396
+
397
+ it('should throw UNSUPPORTED_METHOD for wallets without network support', async () => {
398
+ // Assuming Lobstr doesn't support getNetwork
399
+ (global.window as any).lobstrExtension = mockLobstrAPI;
400
+ mockLobstrAPI.getPublicKey.mockResolvedValue('GTEST123...');
401
+ manager = new WalletManager();
402
+
403
+ await manager.connect(WalletType.LOBSTR);
404
+
405
+ await expect(manager.getNetwork())
406
+ .rejects
407
+ .toThrow(WalletError);
408
+ });
409
+ });
410
+ });
@@ -0,0 +1,23 @@
1
+ import { Client as GeneratedGovernorClient } from '../generated/governor/src/index.js';
2
+
3
+ export class GovernorClient {
4
+ private client: GeneratedGovernorClient;
5
+
6
+ constructor(
7
+ public readonly networkPassphrase: string,
8
+ public readonly rpcUrl: string,
9
+ public readonly contractId: string
10
+ ) {
11
+ this.client = new GeneratedGovernorClient({
12
+ networkPassphrase,
13
+ rpcUrl,
14
+ contractId,
15
+ });
16
+ }
17
+
18
+
19
+ async getProposal(proposalId: bigint): Promise<any> {
20
+ const tx = await this.client.get_proposal({ proposal_id: BigInt(proposalId) });
21
+ return tx.result;
22
+ }
23
+ }
@@ -0,0 +1,23 @@
1
+ import { Client as GeneratedTreasuryClient } from '../generated/treasury/src/index.js';
2
+
3
+ export class TreasuryClient {
4
+ private client: GeneratedTreasuryClient;
5
+
6
+ constructor(
7
+ public readonly networkPassphrase: string,
8
+ public readonly rpcUrl: string,
9
+ public readonly contractId: string
10
+ ) {
11
+ this.client = new GeneratedTreasuryClient({
12
+ networkPassphrase,
13
+ rpcUrl,
14
+ contractId,
15
+ });
16
+ }
17
+
18
+
19
+ async getClaimableBalance(member: string): Promise<bigint> {
20
+ const tx = await this.client.get_claimable_balance({ member });
21
+ return tx.result;
22
+ }
23
+ }
@@ -0,0 +1,48 @@
1
+ import { Client as GeneratedValocracyClient } from '../generated/valocracy/src/index.js';
2
+ import type { AssembledTransaction, Result } from '@stellar/stellar-sdk/contract';
3
+ import type { u64 } from '@stellar/stellar-sdk/contract';
4
+
5
+ export class ValocracyClient {
6
+ private client: GeneratedValocracyClient;
7
+
8
+ constructor(
9
+ public readonly networkPassphrase: string,
10
+ public readonly rpcUrl: string,
11
+ public readonly contractId: string
12
+ ) {
13
+ this.client = new GeneratedValocracyClient({
14
+ networkPassphrase,
15
+ rpcUrl,
16
+ contractId,
17
+ });
18
+ }
19
+
20
+
21
+ async getLevel(address: string): Promise<number> {
22
+ const tx = await this.client.level_of({ account: address });
23
+ return Number(tx.result);
24
+ }
25
+
26
+ // Get voting power (Mana) with decay
27
+
28
+ async getMana(address: string): Promise<number> {
29
+ const tx = await this.client.get_votes({ account: address });
30
+ return Number(tx.result);
31
+ }
32
+
33
+ // Self-register with backend signature
34
+
35
+ async selfRegister(
36
+ caller: string,
37
+ signature: Buffer,
38
+ nonce: bigint,
39
+ expiry: bigint
40
+ ): Promise<AssembledTransaction<Result<u64>>> {
41
+ return await this.client.self_register({
42
+ caller,
43
+ signature,
44
+ nonce: BigInt(nonce),
45
+ expiry: BigInt(expiry),
46
+ });
47
+ }
48
+ }
@@ -0,0 +1,3 @@
1
+ export * from './ValocracyClient.js';
2
+ export * from './GovernorClient.js';
3
+ export * from './TreasuryClient.js';
@@ -0,0 +1,54 @@
1
+ # governor JS
2
+
3
+ JS library for interacting with [Soroban](https://soroban.stellar.org/) smart contract `governor` via Soroban RPC.
4
+
5
+ This library was automatically generated by Soroban CLI using a command similar to:
6
+
7
+ ```bash
8
+ soroban contract bindings ts \
9
+ --rpc-url INSERT_RPC_URL_HERE \
10
+ --network-passphrase "INSERT_NETWORK_PASSPHRASE_HERE" \
11
+ --contract-id INSERT_CONTRACT_ID_HERE \
12
+ --output-dir ./path/to/governor
13
+ ```
14
+
15
+ The network passphrase and contract ID are exported from [index.ts](./src/index.ts) in the `networks` constant. If you are the one who generated this library and you know that this contract is also deployed to other networks, feel free to update `networks` with other valid options. This will help your contract consumers use this library more easily.
16
+
17
+ # To publish or not to publish
18
+
19
+ This library is suitable for publishing to NPM. You can publish it to NPM using the `npm publish` command.
20
+
21
+ But you don't need to publish this library to NPM to use it. You can add it to your project's `package.json` using a file path:
22
+
23
+ ```json
24
+ "dependencies": {
25
+ "governor": "./path/to/this/folder"
26
+ }
27
+ ```
28
+
29
+ However, we've actually encountered [frustration](https://github.com/stellar/soroban-example-dapp/pull/117#discussion_r1232873560) using local libraries with NPM in this way. Though it seems a bit messy, we suggest generating the library directly to your `node_modules` folder automatically after each install by using a `postinstall` script. We've had the least trouble with this approach. NPM will automatically remove what it sees as erroneous directories during the `install` step, and then regenerate them when it gets to your `postinstall` step, which will keep the library up-to-date with your contract.
30
+
31
+ ```json
32
+ "scripts": {
33
+ "postinstall": "soroban contract bindings ts --rpc-url INSERT_RPC_URL_HERE --network-passphrase \"INSERT_NETWORK_PASSPHRASE_HERE\" --id INSERT_CONTRACT_ID_HERE --name governor"
34
+ }
35
+ ```
36
+
37
+ Obviously you need to adjust the above command based on the actual command you used to generate the library.
38
+
39
+ # Use it
40
+
41
+ Now that you have your library up-to-date and added to your project, you can import it in a file and see inline documentation for all of its exported methods:
42
+
43
+ ```js
44
+ import { Contract, networks } from "governor"
45
+
46
+ const contract = new Contract({
47
+ ...networks.futurenet, // for example; check which networks this library exports
48
+ rpcUrl: '...', // use your own, or find one for testing at https://soroban.stellar.org/docs/reference/rpc#public-rpc-providers
49
+ })
50
+
51
+ contract.|
52
+ ```
53
+
54
+ As long as your editor is configured to show JavaScript/TypeScript documentation, you can pause your typing at that `|` to get a list of all exports and inline-documentation for each. It exports a separate [async](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function) function for each method in the smart contract, with documentation for each generated from the comments the contract's author included in the original source code.
@@ -0,0 +1,17 @@
1
+ {
2
+ "version": "0.0.0",
3
+ "name": "governor",
4
+ "type": "module",
5
+ "exports": "./dist/index.js",
6
+ "typings": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc"
9
+ },
10
+ "dependencies": {
11
+ "@stellar/stellar-sdk": "^14.1.1",
12
+ "buffer": "6.0.3"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.6.2"
16
+ }
17
+ }