@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.
- package/README.md +272 -0
- package/dist/config.cjs +21 -0
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +8 -0
- package/dist/config.d.cts.map +1 -0
- package/dist/config.d.mts +8 -0
- package/dist/config.d.mts.map +1 -0
- package/dist/config.mjs +20 -0
- package/dist/config.mjs.map +1 -0
- package/dist/index.cjs +7564 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +261 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +263 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +7529 -0
- package/dist/index.mjs.map +1 -0
- package/dist/metadata.cjs +22 -0
- package/dist/metadata.cjs.map +1 -0
- package/dist/metadata.d.cts +7 -0
- package/dist/metadata.d.cts.map +1 -0
- package/dist/metadata.d.mts +7 -0
- package/dist/metadata.d.mts.map +1 -0
- package/dist/metadata.mjs +21 -0
- package/dist/metadata.mjs.map +1 -0
- package/dist/networks-BrV516-R.d.cts +15 -0
- package/dist/networks-BrV516-R.d.cts.map +1 -0
- package/dist/networks-C0MmhJcu.d.mts +15 -0
- package/dist/networks-C0MmhJcu.d.mts.map +1 -0
- package/dist/networks-DgUFSTiC.cjs +76 -0
- package/dist/networks-DgUFSTiC.cjs.map +1 -0
- package/dist/networks-QbEPbaGT.mjs +46 -0
- package/dist/networks-QbEPbaGT.mjs.map +1 -0
- package/dist/networks.cjs +8 -0
- package/dist/networks.d.cts +2 -0
- package/dist/networks.d.mts +2 -0
- package/dist/networks.mjs +3 -0
- package/dist/vite-config.cjs +43 -0
- package/dist/vite-config.cjs.map +1 -0
- package/dist/vite-config.d.cts +35 -0
- package/dist/vite-config.d.cts.map +1 -0
- package/dist/vite-config.d.mts +35 -0
- package/dist/vite-config.d.mts.map +1 -0
- package/dist/vite-config.mjs +42 -0
- package/dist/vite-config.mjs.map +1 -0
- package/package.json +114 -0
- package/src/__tests__/getDefaultServiceConfig.test.ts +105 -0
- package/src/access-control/actions.ts +214 -0
- package/src/access-control/feature-detection.ts +238 -0
- package/src/access-control/index.ts +54 -0
- package/src/access-control/indexer-client.ts +1474 -0
- package/src/access-control/onchain-reader.ts +446 -0
- package/src/access-control/service.ts +1431 -0
- package/src/access-control/validation.ts +256 -0
- package/src/adapter.ts +659 -0
- package/src/config.ts +43 -0
- package/src/configuration/__tests__/explorer.test.ts +80 -0
- package/src/configuration/__tests__/rpc.test.ts +355 -0
- package/src/configuration/execution.ts +83 -0
- package/src/configuration/explorer.ts +105 -0
- package/src/configuration/index.ts +5 -0
- package/src/configuration/network-services.ts +210 -0
- package/src/configuration/rpc.ts +270 -0
- package/src/configuration.ts +2 -0
- package/src/contract/__tests__/complete-type-coverage.test.ts +78 -0
- package/src/contract/index.ts +3 -0
- package/src/contract/loader.ts +498 -0
- package/src/contract/transformer.ts +1 -0
- package/src/contract/type.ts +65 -0
- package/src/index.ts +23 -0
- package/src/mapping/constants.ts +89 -0
- package/src/mapping/enum-metadata.ts +237 -0
- package/src/mapping/field-generator.ts +296 -0
- package/src/mapping/index.ts +5 -0
- package/src/mapping/struct-fields.ts +106 -0
- package/src/mapping/tuple-components.ts +43 -0
- package/src/mapping/type-coverage-validator.ts +151 -0
- package/src/mapping/type-mapper.ts +203 -0
- package/src/metadata.ts +16 -0
- package/src/networks/README.md +84 -0
- package/src/networks/index.ts +19 -0
- package/src/networks/mainnet.ts +20 -0
- package/src/networks/testnet.ts +20 -0
- package/src/networks.ts +2 -0
- package/src/query/handler.ts +411 -0
- package/src/query/index.ts +4 -0
- package/src/query/view-checker.ts +32 -0
- package/src/sac/spec-cache.ts +68 -0
- package/src/sac/spec-source.ts +35 -0
- package/src/sac/xdr.ts +101 -0
- package/src/transaction/components/AdvancedInfo.tsx +34 -0
- package/src/transaction/components/FeeConfiguration.tsx +41 -0
- package/src/transaction/components/StellarRelayerOptions.tsx +60 -0
- package/src/transaction/components/TransactionTiming.tsx +77 -0
- package/src/transaction/components/index.ts +5 -0
- package/src/transaction/components/useStellarRelayerOptions.ts +114 -0
- package/src/transaction/eoa.ts +229 -0
- package/src/transaction/execution-strategy.ts +33 -0
- package/src/transaction/formatter.ts +296 -0
- package/src/transaction/index.ts +4 -0
- package/src/transaction/relayer.ts +575 -0
- package/src/transaction/sender.ts +156 -0
- package/src/transform/index.ts +4 -0
- package/src/transform/input-parser.ts +9 -0
- package/src/transform/output-formatter.ts +133 -0
- package/src/transform/parsers/complex-parser.ts +157 -0
- package/src/transform/parsers/generic-parser.ts +171 -0
- package/src/transform/parsers/index.ts +86 -0
- package/src/transform/parsers/primitive-parser.ts +123 -0
- package/src/transform/parsers/scval-converter.ts +405 -0
- package/src/transform/parsers/struct-parser.ts +324 -0
- package/src/transform/parsers/types.ts +35 -0
- package/src/types/__tests__/artifacts.test.ts +89 -0
- package/src/types/artifacts.ts +19 -0
- package/src/utils/__tests__/artifacts.test.ts +77 -0
- package/src/utils/artifacts.ts +30 -0
- package/src/utils/formatting.ts +122 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/input-parsing.ts +336 -0
- package/src/utils/safe-type-parser.ts +303 -0
- package/src/utils/stellar-types.ts +35 -0
- package/src/utils/type-detection.ts +163 -0
- package/src/utils/xdr-ordering.ts +36 -0
- package/src/validation/__tests__/address.test.ts +267 -0
- package/src/validation/address.ts +136 -0
- package/src/validation/eoa.ts +33 -0
- package/src/validation/index.ts +3 -0
- package/src/validation/relayer.ts +13 -0
- package/src/vite-config.ts +67 -0
- package/src/wallet/README.md +93 -0
- package/src/wallet/__tests__/connection.test.ts +72 -0
- package/src/wallet/components/StellarWalletUiRoot.tsx +161 -0
- package/src/wallet/components/account/AccountDisplay.tsx +50 -0
- package/src/wallet/components/connect/ConnectButton.tsx +100 -0
- package/src/wallet/components/connect/ConnectorDialog.tsx +125 -0
- package/src/wallet/components/index.ts +3 -0
- package/src/wallet/connection.ts +151 -0
- package/src/wallet/context/StellarWalletContext.ts +32 -0
- package/src/wallet/context/index.ts +4 -0
- package/src/wallet/context/useStellarWalletContext.ts +17 -0
- package/src/wallet/hooks/facade-hooks.ts +31 -0
- package/src/wallet/hooks/index.ts +7 -0
- package/src/wallet/hooks/useStellarAccount.ts +27 -0
- package/src/wallet/hooks/useStellarConnect.ts +60 -0
- package/src/wallet/hooks/useStellarDisconnect.ts +47 -0
- package/src/wallet/hooks/useUiKitConfig.ts +40 -0
- package/src/wallet/implementation/wallets-kit-implementation.ts +379 -0
- package/src/wallet/index.ts +11 -0
- package/src/wallet/services/__tests__/configResolutionService.test.ts +163 -0
- package/src/wallet/services/configResolutionService.ts +65 -0
- package/src/wallet/stellar-wallets-kit/StellarWalletsKitConnectButton.tsx +82 -0
- package/src/wallet/stellar-wallets-kit/__mocks__/@creit.tech/stellar-wallets-kit.ts +48 -0
- package/src/wallet/stellar-wallets-kit/__tests__/export-service.test.ts +93 -0
- package/src/wallet/stellar-wallets-kit/__tests__/stellarUiKitManager.test.ts +0 -0
- package/src/wallet/stellar-wallets-kit/config-generator.ts +75 -0
- package/src/wallet/stellar-wallets-kit/export-service.ts +19 -0
- package/src/wallet/stellar-wallets-kit/index.ts +3 -0
- package/src/wallet/stellar-wallets-kit/stellarUiKitManager.ts +235 -0
- package/src/wallet/types.ts +19 -0
- package/src/wallet/utils/__tests__/filterWalletComponents.test.ts +150 -0
- package/src/wallet/utils/__tests__/uiKitService.test.ts +189 -0
- package/src/wallet/utils/filterWalletComponents.ts +89 -0
- package/src/wallet/utils/index.ts +3 -0
- package/src/wallet/utils/stellarWalletImplementationManager.ts +118 -0
- 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
|
+
}
|