@openzeppelin/adapter-stellar 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 (165) hide show
  1. package/README.md +272 -0
  2. package/dist/config.cjs +21 -0
  3. package/dist/config.cjs.map +1 -0
  4. package/dist/config.d.cts +8 -0
  5. package/dist/config.d.cts.map +1 -0
  6. package/dist/config.d.mts +8 -0
  7. package/dist/config.d.mts.map +1 -0
  8. package/dist/config.mjs +20 -0
  9. package/dist/config.mjs.map +1 -0
  10. package/dist/index.cjs +7564 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +261 -0
  13. package/dist/index.d.cts.map +1 -0
  14. package/dist/index.d.mts +263 -0
  15. package/dist/index.d.mts.map +1 -0
  16. package/dist/index.mjs +7529 -0
  17. package/dist/index.mjs.map +1 -0
  18. package/dist/metadata.cjs +22 -0
  19. package/dist/metadata.cjs.map +1 -0
  20. package/dist/metadata.d.cts +7 -0
  21. package/dist/metadata.d.cts.map +1 -0
  22. package/dist/metadata.d.mts +7 -0
  23. package/dist/metadata.d.mts.map +1 -0
  24. package/dist/metadata.mjs +21 -0
  25. package/dist/metadata.mjs.map +1 -0
  26. package/dist/networks-BrV516-R.d.cts +15 -0
  27. package/dist/networks-BrV516-R.d.cts.map +1 -0
  28. package/dist/networks-C0MmhJcu.d.mts +15 -0
  29. package/dist/networks-C0MmhJcu.d.mts.map +1 -0
  30. package/dist/networks-DgUFSTiC.cjs +76 -0
  31. package/dist/networks-DgUFSTiC.cjs.map +1 -0
  32. package/dist/networks-QbEPbaGT.mjs +46 -0
  33. package/dist/networks-QbEPbaGT.mjs.map +1 -0
  34. package/dist/networks.cjs +8 -0
  35. package/dist/networks.d.cts +2 -0
  36. package/dist/networks.d.mts +2 -0
  37. package/dist/networks.mjs +3 -0
  38. package/dist/vite-config.cjs +43 -0
  39. package/dist/vite-config.cjs.map +1 -0
  40. package/dist/vite-config.d.cts +35 -0
  41. package/dist/vite-config.d.cts.map +1 -0
  42. package/dist/vite-config.d.mts +35 -0
  43. package/dist/vite-config.d.mts.map +1 -0
  44. package/dist/vite-config.mjs +42 -0
  45. package/dist/vite-config.mjs.map +1 -0
  46. package/package.json +114 -0
  47. package/src/__tests__/getDefaultServiceConfig.test.ts +105 -0
  48. package/src/access-control/actions.ts +214 -0
  49. package/src/access-control/feature-detection.ts +238 -0
  50. package/src/access-control/index.ts +54 -0
  51. package/src/access-control/indexer-client.ts +1474 -0
  52. package/src/access-control/onchain-reader.ts +446 -0
  53. package/src/access-control/service.ts +1431 -0
  54. package/src/access-control/validation.ts +256 -0
  55. package/src/adapter.ts +659 -0
  56. package/src/config.ts +43 -0
  57. package/src/configuration/__tests__/explorer.test.ts +80 -0
  58. package/src/configuration/__tests__/rpc.test.ts +355 -0
  59. package/src/configuration/execution.ts +83 -0
  60. package/src/configuration/explorer.ts +105 -0
  61. package/src/configuration/index.ts +5 -0
  62. package/src/configuration/network-services.ts +210 -0
  63. package/src/configuration/rpc.ts +270 -0
  64. package/src/configuration.ts +2 -0
  65. package/src/contract/__tests__/complete-type-coverage.test.ts +78 -0
  66. package/src/contract/index.ts +3 -0
  67. package/src/contract/loader.ts +498 -0
  68. package/src/contract/transformer.ts +1 -0
  69. package/src/contract/type.ts +65 -0
  70. package/src/index.ts +23 -0
  71. package/src/mapping/constants.ts +89 -0
  72. package/src/mapping/enum-metadata.ts +237 -0
  73. package/src/mapping/field-generator.ts +296 -0
  74. package/src/mapping/index.ts +5 -0
  75. package/src/mapping/struct-fields.ts +106 -0
  76. package/src/mapping/tuple-components.ts +43 -0
  77. package/src/mapping/type-coverage-validator.ts +151 -0
  78. package/src/mapping/type-mapper.ts +203 -0
  79. package/src/metadata.ts +16 -0
  80. package/src/networks/README.md +84 -0
  81. package/src/networks/index.ts +19 -0
  82. package/src/networks/mainnet.ts +20 -0
  83. package/src/networks/testnet.ts +20 -0
  84. package/src/networks.ts +2 -0
  85. package/src/query/handler.ts +411 -0
  86. package/src/query/index.ts +4 -0
  87. package/src/query/view-checker.ts +32 -0
  88. package/src/sac/spec-cache.ts +68 -0
  89. package/src/sac/spec-source.ts +35 -0
  90. package/src/sac/xdr.ts +101 -0
  91. package/src/transaction/components/AdvancedInfo.tsx +34 -0
  92. package/src/transaction/components/FeeConfiguration.tsx +41 -0
  93. package/src/transaction/components/StellarRelayerOptions.tsx +60 -0
  94. package/src/transaction/components/TransactionTiming.tsx +77 -0
  95. package/src/transaction/components/index.ts +5 -0
  96. package/src/transaction/components/useStellarRelayerOptions.ts +114 -0
  97. package/src/transaction/eoa.ts +229 -0
  98. package/src/transaction/execution-strategy.ts +33 -0
  99. package/src/transaction/formatter.ts +296 -0
  100. package/src/transaction/index.ts +4 -0
  101. package/src/transaction/relayer.ts +575 -0
  102. package/src/transaction/sender.ts +156 -0
  103. package/src/transform/index.ts +4 -0
  104. package/src/transform/input-parser.ts +9 -0
  105. package/src/transform/output-formatter.ts +133 -0
  106. package/src/transform/parsers/complex-parser.ts +157 -0
  107. package/src/transform/parsers/generic-parser.ts +171 -0
  108. package/src/transform/parsers/index.ts +86 -0
  109. package/src/transform/parsers/primitive-parser.ts +123 -0
  110. package/src/transform/parsers/scval-converter.ts +405 -0
  111. package/src/transform/parsers/struct-parser.ts +324 -0
  112. package/src/transform/parsers/types.ts +35 -0
  113. package/src/types/__tests__/artifacts.test.ts +89 -0
  114. package/src/types/artifacts.ts +19 -0
  115. package/src/utils/__tests__/artifacts.test.ts +77 -0
  116. package/src/utils/artifacts.ts +30 -0
  117. package/src/utils/formatting.ts +122 -0
  118. package/src/utils/index.ts +6 -0
  119. package/src/utils/input-parsing.ts +336 -0
  120. package/src/utils/safe-type-parser.ts +303 -0
  121. package/src/utils/stellar-types.ts +35 -0
  122. package/src/utils/type-detection.ts +163 -0
  123. package/src/utils/xdr-ordering.ts +36 -0
  124. package/src/validation/__tests__/address.test.ts +267 -0
  125. package/src/validation/address.ts +136 -0
  126. package/src/validation/eoa.ts +33 -0
  127. package/src/validation/index.ts +3 -0
  128. package/src/validation/relayer.ts +13 -0
  129. package/src/vite-config.ts +67 -0
  130. package/src/wallet/README.md +93 -0
  131. package/src/wallet/__tests__/connection.test.ts +72 -0
  132. package/src/wallet/components/StellarWalletUiRoot.tsx +161 -0
  133. package/src/wallet/components/account/AccountDisplay.tsx +50 -0
  134. package/src/wallet/components/connect/ConnectButton.tsx +100 -0
  135. package/src/wallet/components/connect/ConnectorDialog.tsx +125 -0
  136. package/src/wallet/components/index.ts +3 -0
  137. package/src/wallet/connection.ts +151 -0
  138. package/src/wallet/context/StellarWalletContext.ts +32 -0
  139. package/src/wallet/context/index.ts +4 -0
  140. package/src/wallet/context/useStellarWalletContext.ts +17 -0
  141. package/src/wallet/hooks/facade-hooks.ts +31 -0
  142. package/src/wallet/hooks/index.ts +7 -0
  143. package/src/wallet/hooks/useStellarAccount.ts +27 -0
  144. package/src/wallet/hooks/useStellarConnect.ts +60 -0
  145. package/src/wallet/hooks/useStellarDisconnect.ts +47 -0
  146. package/src/wallet/hooks/useUiKitConfig.ts +40 -0
  147. package/src/wallet/implementation/wallets-kit-implementation.ts +379 -0
  148. package/src/wallet/index.ts +11 -0
  149. package/src/wallet/services/__tests__/configResolutionService.test.ts +163 -0
  150. package/src/wallet/services/configResolutionService.ts +65 -0
  151. package/src/wallet/stellar-wallets-kit/StellarWalletsKitConnectButton.tsx +82 -0
  152. package/src/wallet/stellar-wallets-kit/__mocks__/@creit.tech/stellar-wallets-kit.ts +48 -0
  153. package/src/wallet/stellar-wallets-kit/__tests__/export-service.test.ts +93 -0
  154. package/src/wallet/stellar-wallets-kit/__tests__/stellarUiKitManager.test.ts +0 -0
  155. package/src/wallet/stellar-wallets-kit/config-generator.ts +75 -0
  156. package/src/wallet/stellar-wallets-kit/export-service.ts +19 -0
  157. package/src/wallet/stellar-wallets-kit/index.ts +3 -0
  158. package/src/wallet/stellar-wallets-kit/stellarUiKitManager.ts +235 -0
  159. package/src/wallet/types.ts +19 -0
  160. package/src/wallet/utils/__tests__/filterWalletComponents.test.ts +150 -0
  161. package/src/wallet/utils/__tests__/uiKitService.test.ts +189 -0
  162. package/src/wallet/utils/filterWalletComponents.ts +89 -0
  163. package/src/wallet/utils/index.ts +3 -0
  164. package/src/wallet/utils/stellarWalletImplementationManager.ts +118 -0
  165. package/src/wallet/utils/uiKitService.ts +74 -0
@@ -0,0 +1,379 @@
1
+ /**
2
+ * Private Stellar wallet implementation for Stellar wallet connection
3
+ *
4
+ * This file contains the internal implementation of StellarWalletsKit for wallet connection.
5
+ * It's encapsulated within the Stellar adapter and not exposed to the rest of the application.
6
+ */
7
+ import {
8
+ allowAllModules,
9
+ ISupportedWallet,
10
+ StellarWalletsKit,
11
+ WalletNetwork,
12
+ } from '@creit.tech/stellar-wallets-kit';
13
+
14
+ import type { Connector, StellarNetworkConfig, UiKitConfiguration } from '@openzeppelin/ui-types';
15
+ import { logger } from '@openzeppelin/ui-utils';
16
+
17
+ import type { StellarConnectionStatusListener, StellarWalletConnectionStatus } from '../types';
18
+
19
+ const LOG_SYSTEM = 'StellarWalletImplementation';
20
+
21
+ /**
22
+ * Class responsible for encapsulating StellarWalletsKit logic for wallet interactions.
23
+ * This class should not be used directly by UI components. The StellarAdapter
24
+ * exposes a standardized interface for wallet operations.
25
+ * It manages StellarWalletsKit instances and provides methods for wallet actions.
26
+ */
27
+ export class WalletsKitImplementation {
28
+ private defaultInstanceKit: StellarWalletsKit | null = null;
29
+ private activeStellarKit: StellarWalletsKit | null = null; // To be set by StellarUiKitManager
30
+ private unsubscribeFromStatusChanges?: () => void;
31
+ private initialized: boolean = false;
32
+ private networkConfig: StellarNetworkConfig | null = null;
33
+
34
+ // Internal state tracking for connection status
35
+ private currentAddress: string | null = null;
36
+ private currentWalletId: string | null = null;
37
+ private connectionStatusListeners = new Set<StellarConnectionStatusListener>();
38
+
39
+ /**
40
+ * Constructs the StellarWalletImplementation.
41
+ * Configuration for StellarWalletsKit is deferred until actually needed or set externally.
42
+ * @param networkConfig - Stellar network configuration
43
+ * @param initialUiKitConfig - Optional initial UI kit configuration, primarily for logging the anticipated kit.
44
+ */
45
+ constructor(networkConfig?: StellarNetworkConfig, initialUiKitConfig?: UiKitConfiguration) {
46
+ this.networkConfig = networkConfig || null;
47
+ logger.info(
48
+ LOG_SYSTEM,
49
+ 'Constructor called. Initial anticipated kitName:',
50
+ initialUiKitConfig?.kitName,
51
+ 'Network:',
52
+ networkConfig?.name
53
+ );
54
+ this.initialized = true;
55
+ logger.info(
56
+ LOG_SYSTEM,
57
+ 'StellarWalletImplementation instance initialized (StellarWalletsKit config creation deferred).'
58
+ );
59
+ // No kit created here by default anymore.
60
+ }
61
+
62
+ /**
63
+ * Sets the network configuration for the wallet implementation
64
+ * @param config - The Stellar network configuration
65
+ */
66
+ public setNetworkConfig(config: StellarNetworkConfig): void {
67
+ logger.info(LOG_SYSTEM, 'Network config updated:', config.name);
68
+ this.networkConfig = config;
69
+
70
+ // If we have active kits, they might need to be recreated with new network
71
+ // For now, just log - the kit manager will handle reconfiguration
72
+ if (this.activeStellarKit || this.defaultInstanceKit) {
73
+ logger.info(LOG_SYSTEM, 'Active kits detected - may need reconfiguration for new network');
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Sets the externally determined, currently active StellarWalletsKit instance.
79
+ * This is typically called by StellarUiKitManager after it has resolved the appropriate
80
+ * kit for the selected UI kit mode.
81
+ * @param kit - The StellarWalletsKit object to set as active, or null to clear it.
82
+ */
83
+ public setActiveStellarKit(kit: StellarWalletsKit | null): void {
84
+ logger.info(
85
+ LOG_SYSTEM,
86
+ 'setActiveStellarKit called with kit:',
87
+ kit ? 'Valid StellarWalletsKit' : 'Null'
88
+ );
89
+ this.activeStellarKit = kit;
90
+
91
+ // If there was an existing direct subscription, it might need to be updated
92
+ if (this.unsubscribeFromStatusChanges) {
93
+ logger.info(LOG_SYSTEM, 'Re-establishing connection status monitoring with new kit');
94
+ // The connection status will be managed internally
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Creates a default StellarWalletsKit instance when no active kit is available.
100
+ * This ensures wallet functionality works even without explicit UI kit configuration.
101
+ * @returns A default StellarWalletsKit instance
102
+ */
103
+ private createDefaultKit(): StellarWalletsKit {
104
+ logger.info(LOG_SYSTEM, 'Creating default StellarWalletsKit instance');
105
+
106
+ const network = this.getWalletNetwork();
107
+ const kit = new StellarWalletsKit({
108
+ network,
109
+ selectedWalletId: undefined,
110
+ modules: allowAllModules(),
111
+ });
112
+
113
+ logger.info(LOG_SYSTEM, 'Default StellarWalletsKit instance created');
114
+ return kit;
115
+ }
116
+
117
+ /**
118
+ * Gets the appropriate WalletNetwork enum value based on network configuration
119
+ */
120
+ private getWalletNetwork(): WalletNetwork {
121
+ if (!this.networkConfig) {
122
+ logger.warn(LOG_SYSTEM, 'No network config available, defaulting to TESTNET');
123
+ return WalletNetwork.TESTNET;
124
+ }
125
+ return this.networkConfig.type === 'mainnet' ? WalletNetwork.PUBLIC : WalletNetwork.TESTNET;
126
+ }
127
+
128
+ /**
129
+ * Gets the kit to use for operations (active or default)
130
+ */
131
+ private getKitToUse(): StellarWalletsKit {
132
+ const kit =
133
+ this.activeStellarKit ||
134
+ this.defaultInstanceKit ||
135
+ (this.defaultInstanceKit = this.createDefaultKit());
136
+ return kit;
137
+ }
138
+
139
+ /**
140
+ * Gets available wallet connectors from StellarWalletsKit
141
+ * @returns Promise resolving to array of available connectors
142
+ */
143
+ public async getAvailableConnectors(): Promise<Connector[]> {
144
+ if (!this.initialized) {
145
+ logger.warn(LOG_SYSTEM, 'getAvailableConnectors called before initialization');
146
+ return [];
147
+ }
148
+
149
+ try {
150
+ const kit = this.getKitToUse();
151
+ const wallets = await kit.getSupportedWallets();
152
+
153
+ const connectors: Connector[] = wallets.map((wallet: ISupportedWallet) => ({
154
+ id: wallet.id,
155
+ name: wallet.name,
156
+ icon: wallet.icon,
157
+ installed: wallet.isAvailable,
158
+ type: (wallet.type as string) || 'browser',
159
+ }));
160
+
161
+ logger.info(LOG_SYSTEM, `Found ${connectors.length} available wallet connectors`);
162
+ return connectors;
163
+ } catch (error) {
164
+ logger.error(LOG_SYSTEM, 'Failed to get available connectors:', error);
165
+ return [];
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Connects to a wallet using the specified connector ID
171
+ * @param connectorId - The ID of the wallet connector to use
172
+ * @returns Promise resolving to connection result
173
+ */
174
+ public async connect(connectorId: string): Promise<{
175
+ connected: boolean;
176
+ address?: string;
177
+ chainId?: string;
178
+ error?: string;
179
+ }> {
180
+ if (!this.initialized) {
181
+ return { connected: false, error: 'Wallet implementation not initialized' };
182
+ }
183
+
184
+ try {
185
+ const prevStatus = this.getWalletConnectionStatus();
186
+ const kit = this.getKitToUse();
187
+
188
+ logger.info(LOG_SYSTEM, `Attempting to connect to wallet: ${connectorId}`);
189
+
190
+ // Set the selected wallet
191
+ kit.setWallet(connectorId);
192
+
193
+ // Get the address from the wallet
194
+ const result = await kit.getAddress();
195
+
196
+ if (result.address) {
197
+ this.currentAddress = result.address;
198
+ this.currentWalletId = connectorId;
199
+
200
+ // Notify listeners of the connection change
201
+ const newStatus = this.getWalletConnectionStatus();
202
+ this.notifyConnectionListeners(newStatus, prevStatus);
203
+
204
+ logger.info(
205
+ LOG_SYSTEM,
206
+ `Successfully connected to wallet: ${connectorId}, address: ${result.address}`
207
+ );
208
+
209
+ return {
210
+ connected: true,
211
+ address: result.address,
212
+ chainId: this.networkConfig?.id,
213
+ };
214
+ } else {
215
+ return {
216
+ connected: false,
217
+ error: 'Failed to get address from wallet',
218
+ };
219
+ }
220
+ } catch (error) {
221
+ logger.error(LOG_SYSTEM, `Failed to connect to wallet ${connectorId}:`, error);
222
+ return {
223
+ connected: false,
224
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
225
+ };
226
+ }
227
+ }
228
+
229
+ /**
230
+ * Disconnects from the currently connected wallet
231
+ * @returns Promise resolving to disconnection result
232
+ */
233
+ public async disconnect(): Promise<{ disconnected: boolean; error?: string }> {
234
+ if (!this.initialized) {
235
+ return { disconnected: false, error: 'Wallet implementation not initialized' };
236
+ }
237
+
238
+ try {
239
+ const prevStatus = this.getWalletConnectionStatus();
240
+
241
+ logger.info(LOG_SYSTEM, 'Disconnecting wallet');
242
+
243
+ // Clear the current connection state
244
+ this.currentAddress = null;
245
+ this.currentWalletId = null;
246
+
247
+ // Notify listeners of the disconnection
248
+ const newStatus = this.getWalletConnectionStatus();
249
+ this.notifyConnectionListeners(newStatus, prevStatus);
250
+
251
+ // For Stellar Wallets Kit, we just clear our internal state
252
+ // The kit doesn't have a specific disconnect method
253
+
254
+ logger.info(LOG_SYSTEM, 'Successfully disconnected wallet');
255
+ return { disconnected: true };
256
+ } catch (error) {
257
+ logger.error(LOG_SYSTEM, 'Failed to disconnect wallet:', error);
258
+ return {
259
+ disconnected: false,
260
+ error: error instanceof Error ? error.message : 'Unknown error occurred',
261
+ };
262
+ }
263
+ }
264
+
265
+ /**
266
+ * Gets the current wallet connection status
267
+ * @returns The current connection status
268
+ */
269
+ public getWalletConnectionStatus(): StellarWalletConnectionStatus {
270
+ const isConnected = this.currentAddress !== null;
271
+ const chainId = this.networkConfig?.id || 'stellar-testnet';
272
+
273
+ return {
274
+ isConnected,
275
+ isConnecting: false, // We don't track intermediate connecting state yet
276
+ isDisconnected: !isConnected,
277
+ isReconnecting: false,
278
+ status: isConnected ? 'connected' : 'disconnected',
279
+ address: this.currentAddress || undefined,
280
+ walletId: this.currentWalletId || undefined,
281
+ chainId,
282
+ };
283
+ }
284
+
285
+ /**
286
+ * Subscribes to wallet connection status changes
287
+ * @param callback - Function to call when connection status changes
288
+ * @returns A function to unsubscribe from the changes
289
+ */
290
+ public onWalletConnectionChange(callback: StellarConnectionStatusListener): () => void {
291
+ if (!this.initialized) {
292
+ logger.warn(LOG_SYSTEM, 'onWalletConnectionChange called before initialization. No-op.');
293
+ return () => {};
294
+ }
295
+
296
+ this.connectionStatusListeners.add(callback);
297
+ logger.info(LOG_SYSTEM, 'Connection status listener added');
298
+
299
+ // Return unsubscribe function
300
+ return () => {
301
+ this.connectionStatusListeners.delete(callback);
302
+ logger.debug(LOG_SYSTEM, 'Connection status listener removed');
303
+ };
304
+ }
305
+
306
+ /**
307
+ * Manually updates the cached connection address and wallet ID
308
+ * This is used when the connection status is determined externally
309
+ * @param address - The wallet address or null
310
+ * @param walletId - The wallet ID or null
311
+ */
312
+ public updateConnectionStatus(address: string | null, walletId?: string | null): void {
313
+ const prevStatus = this.getWalletConnectionStatus();
314
+
315
+ this.currentAddress = address;
316
+ this.currentWalletId = walletId ?? null;
317
+
318
+ const newStatus = this.getWalletConnectionStatus();
319
+ this.notifyConnectionListeners(newStatus, prevStatus);
320
+ }
321
+
322
+ /**
323
+ * Gets the active StellarWalletsKit instance for advanced operations
324
+ * @returns The active kit or null if not available
325
+ */
326
+ public getActiveKit(): StellarWalletsKit | null {
327
+ return this.activeStellarKit || this.defaultInstanceKit;
328
+ }
329
+
330
+ /**
331
+ * Signs a transaction using the connected wallet
332
+ * @param xdr - The transaction XDR to sign
333
+ * @param address - The account address
334
+ * @returns Promise resolving to signed transaction
335
+ */
336
+ public async signTransaction(xdr: string, address: string): Promise<{ signedTxXdr: string }> {
337
+ if (!this.initialized) {
338
+ throw new Error('Wallet implementation not initialized');
339
+ }
340
+
341
+ const kit = this.getKitToUse();
342
+ const networkPassphrase = this.getWalletNetwork();
343
+
344
+ logger.info(LOG_SYSTEM, 'Signing transaction with wallet');
345
+
346
+ return await kit.signTransaction(xdr, {
347
+ address,
348
+ networkPassphrase,
349
+ });
350
+ }
351
+
352
+ /**
353
+ * Notifies all connection listeners of status changes
354
+ */
355
+ private notifyConnectionListeners(
356
+ currentStatus: StellarWalletConnectionStatus,
357
+ previousStatus: StellarWalletConnectionStatus
358
+ ): void {
359
+ this.connectionStatusListeners.forEach((listener) => {
360
+ try {
361
+ listener(currentStatus, previousStatus);
362
+ } catch (error) {
363
+ logger.error(LOG_SYSTEM, 'Error in connection status listener:', String(error));
364
+ }
365
+ });
366
+ }
367
+
368
+ /**
369
+ * Cleanup resources when implementation is no longer needed
370
+ */
371
+ public cleanup(): void {
372
+ if (this.unsubscribeFromStatusChanges) {
373
+ this.unsubscribeFromStatusChanges();
374
+ this.unsubscribeFromStatusChanges = undefined;
375
+ }
376
+ this.connectionStatusListeners.clear();
377
+ logger.info(LOG_SYSTEM, 'Cleanup completed');
378
+ }
379
+ }
@@ -0,0 +1,11 @@
1
+ // Barrel file
2
+
3
+ export * from './connection';
4
+ export * from './stellar-wallets-kit';
5
+ export * from './context';
6
+ export * from './hooks';
7
+ export * from './components';
8
+ export * from './types';
9
+ export * from './utils';
10
+ export * from './implementation/wallets-kit-implementation';
11
+ export { resolveFullUiKitConfiguration } from './services/configResolutionService';
@@ -0,0 +1,163 @@
1
+ import { describe, expect, it, vi } from 'vitest';
2
+
3
+ import type { UiKitConfiguration, UiKitName } from '@openzeppelin/ui-types';
4
+
5
+ import { resolveFullUiKitConfiguration } from '../configResolutionService';
6
+
7
+ describe('configResolutionService', () => {
8
+ describe('resolveFullUiKitConfiguration', () => {
9
+ it('should use programmatic overrides as highest priority', async () => {
10
+ const programmaticOverrides: Partial<UiKitConfiguration> = {
11
+ kitName: 'stellar-wallets-kit',
12
+ kitConfig: {
13
+ showInjectedConnector: true,
14
+ components: {
15
+ exclude: ['NetworkSwitcher'],
16
+ },
17
+ },
18
+ };
19
+
20
+ const initialAppServiceKitName = 'custom';
21
+ const currentAppServiceConfig: UiKitConfiguration = {
22
+ kitName: 'none',
23
+ kitConfig: {
24
+ showInjectedConnector: false,
25
+ },
26
+ };
27
+
28
+ const result = await resolveFullUiKitConfiguration(
29
+ programmaticOverrides,
30
+ initialAppServiceKitName,
31
+ currentAppServiceConfig
32
+ );
33
+
34
+ expect(result).toEqual({
35
+ kitName: 'stellar-wallets-kit',
36
+ kitConfig: {
37
+ showInjectedConnector: true,
38
+ components: {
39
+ exclude: ['NetworkSwitcher'],
40
+ },
41
+ },
42
+ });
43
+ });
44
+
45
+ it('should use current app service config when no programmatic overrides', async () => {
46
+ const initialAppServiceKitName = 'custom';
47
+ const currentAppServiceConfig: UiKitConfiguration = {
48
+ kitName: 'stellar-wallets-kit',
49
+ kitConfig: {
50
+ showInjectedConnector: true,
51
+ },
52
+ };
53
+
54
+ const result = await resolveFullUiKitConfiguration(
55
+ {},
56
+ initialAppServiceKitName,
57
+ currentAppServiceConfig
58
+ );
59
+
60
+ expect(result).toEqual(currentAppServiceConfig);
61
+ });
62
+
63
+ it('should use initial app service kit name as fallback', async () => {
64
+ const initialAppServiceKitName = 'stellar-wallets-kit';
65
+ const currentAppServiceConfig: UiKitConfiguration = {
66
+ kitName: undefined as unknown as UiKitName,
67
+ kitConfig: {},
68
+ };
69
+
70
+ const result = await resolveFullUiKitConfiguration(
71
+ {},
72
+ initialAppServiceKitName,
73
+ currentAppServiceConfig
74
+ );
75
+
76
+ expect(result).toEqual({
77
+ kitName: 'stellar-wallets-kit',
78
+ kitConfig: {},
79
+ });
80
+ });
81
+
82
+ it('should default to custom when no kit name is available', async () => {
83
+ const result = await resolveFullUiKitConfiguration({}, undefined, {
84
+ kitName: undefined as unknown as UiKitName,
85
+ kitConfig: {},
86
+ });
87
+
88
+ expect(result).toEqual({
89
+ kitName: 'custom',
90
+ kitConfig: {},
91
+ });
92
+ });
93
+
94
+ it('should merge kit configs with programmatic overrides taking precedence', async () => {
95
+ const programmaticOverrides: Partial<UiKitConfiguration> = {
96
+ kitConfig: {
97
+ showInjectedConnector: true,
98
+ components: {
99
+ exclude: ['AccountDisplay'],
100
+ },
101
+ },
102
+ };
103
+
104
+ const currentAppServiceConfig: UiKitConfiguration = {
105
+ kitName: 'custom',
106
+ kitConfig: {
107
+ showInjectedConnector: false,
108
+ components: {
109
+ exclude: ['NetworkSwitcher'],
110
+ },
111
+ appName: 'Test App',
112
+ },
113
+ };
114
+
115
+ const result = await resolveFullUiKitConfiguration(
116
+ programmaticOverrides,
117
+ 'custom',
118
+ currentAppServiceConfig
119
+ );
120
+
121
+ expect(result).toEqual({
122
+ kitName: 'custom',
123
+ kitConfig: {
124
+ showInjectedConnector: true,
125
+ components: {
126
+ exclude: ['AccountDisplay'],
127
+ },
128
+ appName: 'Test App',
129
+ },
130
+ });
131
+ });
132
+
133
+ it('should log debug message when native config loader is provided', async () => {
134
+ const mockNativeConfigLoader = vi.fn();
135
+ const debugSpy = vi.spyOn(console, 'debug').mockImplementation(() => {});
136
+
137
+ await resolveFullUiKitConfiguration(
138
+ {},
139
+ 'custom',
140
+ { kitName: 'custom', kitConfig: {} },
141
+ { loadUiKitNativeConfig: mockNativeConfigLoader }
142
+ );
143
+
144
+ // Verify the native config loader is acknowledged but not called
145
+ expect(mockNativeConfigLoader).not.toHaveBeenCalled();
146
+
147
+ debugSpy.mockRestore();
148
+ });
149
+
150
+ it('should handle empty kit configs gracefully', async () => {
151
+ const result = await resolveFullUiKitConfiguration(
152
+ { kitName: 'stellar-wallets-kit' },
153
+ 'custom',
154
+ { kitName: 'custom', kitConfig: undefined as Record<string, unknown> | undefined }
155
+ );
156
+
157
+ expect(result).toEqual({
158
+ kitName: 'stellar-wallets-kit',
159
+ kitConfig: {},
160
+ });
161
+ });
162
+ });
163
+ });
@@ -0,0 +1,65 @@
1
+ import type { NativeConfigLoader, UiKitConfiguration } from '@openzeppelin/ui-types';
2
+ import { logger } from '@openzeppelin/ui-utils';
3
+
4
+ /**
5
+ * Resolves the final, complete UiKitConfiguration for Stellar by merging various sources.
6
+ *
7
+ * Note: Unlike the EVM adapter, Stellar has limited UI kit options, so this is simpler.
8
+ * We don't currently support loading native TypeScript config files for Stellar.
9
+ *
10
+ * @param programmaticOverrides - Overrides passed directly to the configureUiKit call.
11
+ * @param initialAppServiceKitName - The kitName noted from AppConfigService when the adapter instance was constructed.
12
+ * @param currentAppServiceConfig - The full UiKitConfiguration from AppConfigService, re-fetched at the time of the call.
13
+ * @param options - Options, including the callback to load user's native config file (currently unused for Stellar).
14
+ * @returns A Promise resolving to the final UiKitConfiguration.
15
+ */
16
+ export async function resolveFullUiKitConfiguration(
17
+ programmaticOverrides: Partial<UiKitConfiguration>,
18
+ initialAppServiceKitName: UiKitConfiguration['kitName'],
19
+ currentAppServiceConfig: UiKitConfiguration,
20
+ options?: {
21
+ loadUiKitNativeConfig?: NativeConfigLoader;
22
+ }
23
+ ): Promise<UiKitConfiguration> {
24
+ logger.debug(
25
+ 'stellar:configResolutionService:resolveFullUiKitConfiguration',
26
+ 'Starting resolution with:',
27
+ {
28
+ programmaticOverrides,
29
+ initialAppServiceKitName,
30
+ currentAppServiceConfig,
31
+ hasLoadNativeCallback: !!options?.loadUiKitNativeConfig,
32
+ }
33
+ );
34
+
35
+ // Note: We currently don't support loading native TypeScript config files for Stellar
36
+ // This could be added in the future if needed
37
+ if (options?.loadUiKitNativeConfig) {
38
+ logger.debug(
39
+ 'stellar:configResolutionService',
40
+ 'Native config loader provided but not currently supported for Stellar adapter'
41
+ );
42
+ }
43
+
44
+ const effectiveKitName: UiKitConfiguration['kitName'] =
45
+ programmaticOverrides.kitName ||
46
+ currentAppServiceConfig.kitName ||
47
+ initialAppServiceKitName ||
48
+ 'custom';
49
+
50
+ const finalFullConfig: UiKitConfiguration = {
51
+ kitName: effectiveKitName,
52
+ kitConfig: {
53
+ ...(currentAppServiceConfig.kitConfig || {}),
54
+ ...(programmaticOverrides.kitConfig || {}),
55
+ },
56
+ };
57
+
58
+ logger.debug(
59
+ 'stellar:configResolutionService:resolveFullUiKitConfiguration',
60
+ 'Resolved finalFullConfig:',
61
+ finalFullConfig
62
+ );
63
+
64
+ return finalFullConfig;
65
+ }
@@ -0,0 +1,82 @@
1
+ import { useEffect, useRef } from 'react';
2
+
3
+ import { logger } from '@openzeppelin/ui-utils';
4
+
5
+ import { setStellarConnectedAddress } from '../connection';
6
+ import { stellarUiKitManager } from './stellarUiKitManager';
7
+
8
+ /**
9
+ * Creates a Stellar Wallets Kit ConnectButton component.
10
+ * This renders the kit's native button UI which includes built-in connected state,
11
+ * copy address functionality, and disconnect options.
12
+ *
13
+ * @returns A React component that uses Stellar Wallets Kit's native button
14
+ */
15
+ export function StellarWalletsKitConnectButton() {
16
+ const containerRef = useRef<HTMLDivElement>(null);
17
+
18
+ useEffect(() => {
19
+ const state = stellarUiKitManager.getState();
20
+ const kit = state.stellarKitProvider;
21
+
22
+ if (!kit || !containerRef.current) {
23
+ logger.error(
24
+ 'StellarWalletsKitConnectButton',
25
+ 'Kit not initialized or container not available'
26
+ );
27
+ return;
28
+ }
29
+
30
+ // Create the native button provided by Stellar Wallets Kit
31
+ kit.createButton({
32
+ container: containerRef.current,
33
+ onConnect: ({ address }) => {
34
+ logger.info('StellarWalletsKitConnectButton', `Connected to address: ${address}`);
35
+ // Inform the adapter's wallet implementation so the context updates
36
+ // This ensures the Execute Transaction button and other components
37
+ // recognize the wallet connection state
38
+ try {
39
+ setStellarConnectedAddress(address ?? null);
40
+ } catch (error) {
41
+ logger.warn(
42
+ 'StellarWalletsKitConnectButton',
43
+ 'Failed to set connected address in adapter implementation:',
44
+ error
45
+ );
46
+ }
47
+ },
48
+ onDisconnect: () => {
49
+ logger.info('StellarWalletsKitConnectButton', 'Disconnected');
50
+ // Inform the implementation we are no longer connected
51
+ try {
52
+ setStellarConnectedAddress(null);
53
+ } catch (error) {
54
+ logger.warn(
55
+ 'StellarWalletsKitConnectButton',
56
+ 'Failed to clear connected address in adapter implementation:',
57
+ error
58
+ );
59
+ }
60
+ },
61
+ buttonText: 'Connect Wallet',
62
+ });
63
+
64
+ // Cleanup: remove the button when component unmounts
65
+ return () => {
66
+ if (typeof kit.removeButton === 'function') {
67
+ try {
68
+ kit.removeButton();
69
+ } catch (error) {
70
+ logger.warn('StellarWalletsKitConnectButton', 'Error removing button:', error);
71
+ }
72
+ } else {
73
+ logger.warn(
74
+ 'StellarWalletsKitConnectButton',
75
+ 'removeButton method not available on kit instance'
76
+ );
77
+ }
78
+ };
79
+ }, []);
80
+
81
+ return <div ref={containerRef} className="stellar-native-button" />;
82
+ }