@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,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stellar Adapter: Vite Configuration Export
|
|
3
|
+
*
|
|
4
|
+
* This module exports Vite configuration fragments for the Stellar adapter.
|
|
5
|
+
* Currently minimal, but provides a consistent interface for adapter-specific
|
|
6
|
+
* build requirements.
|
|
7
|
+
*
|
|
8
|
+
* USAGE:
|
|
9
|
+
* 1. In the main builder app: Import and merge into packages/builder/vite.config.ts
|
|
10
|
+
* 2. In exported apps: The export system injects these configs when Stellar is used
|
|
11
|
+
*
|
|
12
|
+
* See: docs/ADAPTER_ARCHITECTURE.md § "Build-Time Requirements"
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { UserConfig } from 'vite';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Returns the Vite configuration required for Stellar adapter compatibility
|
|
19
|
+
*
|
|
20
|
+
* @returns Vite configuration object to be merged with your main vite.config
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* // vite.config.ts
|
|
25
|
+
* import { getStellarViteConfig } from '@openzeppelin/adapter-stellar/vite-config';
|
|
26
|
+
*
|
|
27
|
+
* export default defineConfig(({ mode }) => {
|
|
28
|
+
* const stellarConfig = getStellarViteConfig();
|
|
29
|
+
*
|
|
30
|
+
* return {
|
|
31
|
+
* plugins: [
|
|
32
|
+
* react(),
|
|
33
|
+
* ...stellarConfig.plugins,
|
|
34
|
+
* ],
|
|
35
|
+
* resolve: {
|
|
36
|
+
* dedupe: [
|
|
37
|
+
* ...stellarConfig.resolve.dedupe,
|
|
38
|
+
* ],
|
|
39
|
+
* },
|
|
40
|
+
* };
|
|
41
|
+
* });
|
|
42
|
+
* ```
|
|
43
|
+
*/
|
|
44
|
+
export function getStellarViteConfig(): UserConfig {
|
|
45
|
+
return {
|
|
46
|
+
// Currently no Stellar-specific plugins required
|
|
47
|
+
plugins: [],
|
|
48
|
+
|
|
49
|
+
resolve: {
|
|
50
|
+
// Module Deduplication
|
|
51
|
+
// Ensure singleton instances of shared dependencies
|
|
52
|
+
dedupe: [
|
|
53
|
+
// Stellar-specific dependencies that may need deduplication
|
|
54
|
+
'@stellar/stellar-sdk',
|
|
55
|
+
'@creit.tech/stellar-wallets-kit',
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
|
|
59
|
+
optimizeDeps: {
|
|
60
|
+
// Force Pre-Bundling (CommonJS → ESM conversion)
|
|
61
|
+
// Stellar dependencies are typically already ESM, but we include them here
|
|
62
|
+
// for consistency and to ensure proper module resolution
|
|
63
|
+
include: [],
|
|
64
|
+
exclude: [],
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Stellar Adapter Wallet Module
|
|
2
|
+
|
|
3
|
+
This directory contains the wallet integration layer for the Stellar adapter, providing all wallet‑related UI, hooks, context, and utilities using `@creit.tech/stellar-wallets-kit`.
|
|
4
|
+
|
|
5
|
+
## Architectural Approach: Dual UI Support
|
|
6
|
+
|
|
7
|
+
The Stellar adapter supports two UI modes:
|
|
8
|
+
|
|
9
|
+
1. Native UI Mode (`stellar-wallets-kit`): Uses the Stellar Wallets Kit’s native button and modal with address display and account management.
|
|
10
|
+
2. Custom UI Mode (`custom`): Uses custom React components that match the Builder’s design system.
|
|
11
|
+
|
|
12
|
+
## Purpose
|
|
13
|
+
|
|
14
|
+
- UI Environment Provision: `StellarWalletUiRoot` provides a stable provider root for wallet state.
|
|
15
|
+
- Facade Hooks: `stellarFacadeHooks` expose standardized hooks for connection and account status.
|
|
16
|
+
- UI Components: Custom‑styled wallet components (`ConnectButton`, `AccountDisplay`).
|
|
17
|
+
- Configuration: Layered config via `AppConfigService` and programmatic overrides.
|
|
18
|
+
|
|
19
|
+
## Directory Structure
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
wallet/
|
|
23
|
+
├── components/ # Wallet UI components & root
|
|
24
|
+
│ ├── StellarWalletUiRoot.tsx
|
|
25
|
+
│ ├── account/
|
|
26
|
+
│ ├── connect/
|
|
27
|
+
│ └── network/ # (Not used: no network switching in Stellar)
|
|
28
|
+
├── context/
|
|
29
|
+
├── hooks/
|
|
30
|
+
├── implementation/ # Stellar Wallets Kit implementation
|
|
31
|
+
├── services/ # Wallet UI config resolution
|
|
32
|
+
├── stellar-wallets-kit/
|
|
33
|
+
├── utils/
|
|
34
|
+
└── connection.ts
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Key Components & Concepts
|
|
38
|
+
|
|
39
|
+
- Adapter UI methods:
|
|
40
|
+
- `getEcosystemReactUiContextProvider()` → `StellarWalletUiRoot`
|
|
41
|
+
- `getEcosystemReactHooks()` → `stellarFacadeHooks`
|
|
42
|
+
- `getEcosystemWalletComponents()` → components for active kit
|
|
43
|
+
- `stellarUiKitManager`: Singleton controller for kit state and network config
|
|
44
|
+
- `StellarWalletUiRoot`: Stable provider that wires the kit and context
|
|
45
|
+
- Facade hooks: `useStellarAccount`, `useStellarConnect`, `useStellarDisconnect`, `useUiKitConfig`
|
|
46
|
+
|
|
47
|
+
## Differences from EVM Wallet Module
|
|
48
|
+
|
|
49
|
+
- No network switching component (Stellar wallets do not switch networks dynamically)
|
|
50
|
+
- Single primary wallet kit (Stellar Wallets Kit) vs multiple kits on EVM
|
|
51
|
+
- No native TypeScript config files are loaded for kit setup (can be added later)
|
|
52
|
+
|
|
53
|
+
## Configuration
|
|
54
|
+
|
|
55
|
+
1. Global App Config (`app.config.json`):
|
|
56
|
+
|
|
57
|
+
Ecosystem‑namespaced configuration under `globalServiceConfigs.walletui.stellar`.
|
|
58
|
+
|
|
59
|
+
Custom UI components:
|
|
60
|
+
|
|
61
|
+
```json
|
|
62
|
+
{
|
|
63
|
+
"globalServiceConfigs": {
|
|
64
|
+
"walletui": {
|
|
65
|
+
"stellar": {
|
|
66
|
+
"kitName": "custom",
|
|
67
|
+
"kitConfig": {
|
|
68
|
+
"showInjectedConnector": false,
|
|
69
|
+
"components": { "exclude": ["NetworkSwitcher"] }
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Native kit button:
|
|
78
|
+
|
|
79
|
+
```json
|
|
80
|
+
{
|
|
81
|
+
"globalServiceConfigs": {
|
|
82
|
+
"walletui": {
|
|
83
|
+
"stellar": { "kitName": "stellar-wallets-kit", "kitConfig": {} }
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
2. Programmatic overrides: pass to `adapter.configureUiKit(overrides)`
|
|
90
|
+
|
|
91
|
+
## Usage in Application
|
|
92
|
+
|
|
93
|
+
`StellarWalletUiRoot` is returned by the adapter and used by the Builder’s `WalletStateProvider`. Use `useWalletState()` and facade hooks from `@openzeppelin/ui-react` to render the wallet UI components from `getEcosystemWalletComponents()`.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
disconnectStellarWallet,
|
|
5
|
+
getStellarWalletConnectionStatus,
|
|
6
|
+
supportsStellarWalletConnection,
|
|
7
|
+
} from '../connection';
|
|
8
|
+
|
|
9
|
+
// Mock the stellar-wallets-kit to avoid CommonJS import issues
|
|
10
|
+
vi.mock('@creit.tech/stellar-wallets-kit', () => ({
|
|
11
|
+
StellarWalletsKit: vi.fn(() => ({
|
|
12
|
+
getSupportedWallets: vi.fn().mockResolvedValue([]),
|
|
13
|
+
setWallet: vi.fn(),
|
|
14
|
+
getAddress: vi.fn(),
|
|
15
|
+
signTransaction: vi.fn(),
|
|
16
|
+
})),
|
|
17
|
+
WalletNetwork: {
|
|
18
|
+
PUBLIC: 'PUBLIC',
|
|
19
|
+
TESTNET: 'TESTNET',
|
|
20
|
+
},
|
|
21
|
+
allowAllModules: vi.fn(() => ({})),
|
|
22
|
+
}));
|
|
23
|
+
|
|
24
|
+
// Mock the stellarUiKitManager
|
|
25
|
+
vi.mock('../stellar-wallets-kit', () => ({
|
|
26
|
+
stellarUiKitManager: {
|
|
27
|
+
getState: vi.fn(() => ({
|
|
28
|
+
isConfigured: true,
|
|
29
|
+
currentFullUiKitConfig: {
|
|
30
|
+
kitName: 'custom',
|
|
31
|
+
kitConfig: {},
|
|
32
|
+
},
|
|
33
|
+
stellarKitProvider: null,
|
|
34
|
+
networkConfig: {
|
|
35
|
+
type: 'testnet',
|
|
36
|
+
horizon: 'https://horizon-testnet.stellar.org',
|
|
37
|
+
passphrase: 'Test SDF Network ; September 2015',
|
|
38
|
+
},
|
|
39
|
+
hasConfigError: false,
|
|
40
|
+
lastConfigError: null,
|
|
41
|
+
})),
|
|
42
|
+
},
|
|
43
|
+
}));
|
|
44
|
+
|
|
45
|
+
describe('Stellar Wallet Connection', () => {
|
|
46
|
+
describe('supportsStellarWalletConnection', () => {
|
|
47
|
+
it('should return true', () => {
|
|
48
|
+
expect(supportsStellarWalletConnection()).toBe(true);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
describe('disconnectStellarWallet', () => {
|
|
53
|
+
it('should disconnect wallet successfully', async () => {
|
|
54
|
+
await disconnectStellarWallet();
|
|
55
|
+
|
|
56
|
+
// Since disconnectStellarWallet only clears internal state and doesn't
|
|
57
|
+
// call any methods on the kit, we just verify it doesn't throw
|
|
58
|
+
expect(true).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
describe('getStellarWalletConnectionStatus', () => {
|
|
63
|
+
it('should return disconnected status when no address', () => {
|
|
64
|
+
const status = getStellarWalletConnectionStatus();
|
|
65
|
+
|
|
66
|
+
expect(status).toHaveProperty('isConnected', false);
|
|
67
|
+
expect(status).toHaveProperty('address');
|
|
68
|
+
expect(status).toHaveProperty('walletId');
|
|
69
|
+
expect(status).toHaveProperty('chainId');
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import type { ISupportedWallet } from '@creit.tech/stellar-wallets-kit';
|
|
2
|
+
import { ReactNode, useCallback, useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import type { UiKitConfiguration } from '@openzeppelin/ui-types';
|
|
5
|
+
import { logger } from '@openzeppelin/ui-utils';
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
connectStellarWallet,
|
|
9
|
+
disconnectStellarWallet,
|
|
10
|
+
getStellarAvailableConnectors,
|
|
11
|
+
getStellarWalletConnectionStatus,
|
|
12
|
+
onStellarWalletConnectionChange,
|
|
13
|
+
} from '../connection';
|
|
14
|
+
import {
|
|
15
|
+
StellarWalletContext,
|
|
16
|
+
type StellarWalletContextType,
|
|
17
|
+
} from '../context/StellarWalletContext';
|
|
18
|
+
import { stellarUiKitManager, type StellarUiKitManagerState } from '../stellar-wallets-kit';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Props for the StellarWalletUiRoot provider
|
|
22
|
+
*/
|
|
23
|
+
interface StellarWalletUiRootProps {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
/** UI kit configuration */
|
|
26
|
+
uiKitConfig?: UiKitConfiguration;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Stellar wallet UI root provider component
|
|
31
|
+
* This component manages the wallet connection state and provides it to child components
|
|
32
|
+
*/
|
|
33
|
+
export function StellarWalletUiRoot({ children, uiKitConfig }: StellarWalletUiRootProps) {
|
|
34
|
+
// UI Kit manager state
|
|
35
|
+
const [uiKitManagerState, setUiKitManagerState] = useState<StellarUiKitManagerState>(
|
|
36
|
+
stellarUiKitManager.getState()
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
// Connection state
|
|
40
|
+
const [address, setAddress] = useState<string | null>(null);
|
|
41
|
+
const [isConnecting, setIsConnecting] = useState(false);
|
|
42
|
+
const [availableWallets, setAvailableWallets] = useState<ISupportedWallet[]>([]);
|
|
43
|
+
|
|
44
|
+
// Initialize UI kit on mount
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
// Only configure if a specific UI kit config is provided or if not already configured
|
|
47
|
+
const currentState = stellarUiKitManager.getState();
|
|
48
|
+
|
|
49
|
+
if (uiKitConfig || !currentState.currentFullUiKitConfig) {
|
|
50
|
+
const configToUse = uiKitConfig || { kitName: 'custom' as const, kitConfig: {} };
|
|
51
|
+
|
|
52
|
+
logger.debug('StellarWalletUiRoot', 'Configuring UI kit with:', configToUse);
|
|
53
|
+
|
|
54
|
+
stellarUiKitManager.configure(configToUse).catch((error) => {
|
|
55
|
+
logger.error('Failed to configure Stellar UI kit:', error);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}, [uiKitConfig]);
|
|
59
|
+
|
|
60
|
+
// Subscribe to UI kit state changes
|
|
61
|
+
useEffect(() => {
|
|
62
|
+
const unsubscribe = stellarUiKitManager.subscribe(() => {
|
|
63
|
+
setUiKitManagerState(stellarUiKitManager.getState());
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
return unsubscribe;
|
|
67
|
+
}, []);
|
|
68
|
+
|
|
69
|
+
// Event-driven connection status updates
|
|
70
|
+
useEffect(() => {
|
|
71
|
+
const unsubscribeFromConnectionChanges = onStellarWalletConnectionChange(
|
|
72
|
+
(currentStatus, _previousStatus) => {
|
|
73
|
+
setAddress(currentStatus.address || null);
|
|
74
|
+
logger.debug(
|
|
75
|
+
'StellarWalletUiRoot',
|
|
76
|
+
`Connection status changed: ${currentStatus.isConnected ? 'connected' : 'disconnected'}`,
|
|
77
|
+
currentStatus.address
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// Initial update to sync current state
|
|
83
|
+
const initialStatus = getStellarWalletConnectionStatus();
|
|
84
|
+
setAddress(initialStatus.address || null);
|
|
85
|
+
|
|
86
|
+
return () => {
|
|
87
|
+
unsubscribeFromConnectionChanges();
|
|
88
|
+
};
|
|
89
|
+
}, [address]);
|
|
90
|
+
|
|
91
|
+
// Load available wallets
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
const loadWallets = async () => {
|
|
94
|
+
try {
|
|
95
|
+
const connectors = await getStellarAvailableConnectors();
|
|
96
|
+
setAvailableWallets(connectors as unknown as ISupportedWallet[]);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
logger.error('Failed to load available wallets:', String(error));
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
if (!uiKitManagerState.isInitializing && uiKitManagerState.stellarKitProvider) {
|
|
103
|
+
loadWallets();
|
|
104
|
+
}
|
|
105
|
+
}, [uiKitManagerState.isInitializing, uiKitManagerState.stellarKitProvider]);
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Connect to a wallet
|
|
109
|
+
*/
|
|
110
|
+
const connect = useCallback(async (walletId: string) => {
|
|
111
|
+
setIsConnecting(true);
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
const result = await connectStellarWallet(walletId);
|
|
115
|
+
|
|
116
|
+
if (result.connected && result.address) {
|
|
117
|
+
setAddress(result.address);
|
|
118
|
+
} else {
|
|
119
|
+
throw new Error(result.error || 'Failed to connect wallet');
|
|
120
|
+
}
|
|
121
|
+
} catch (error) {
|
|
122
|
+
logger.error('Failed to connect:', String(error));
|
|
123
|
+
throw error;
|
|
124
|
+
} finally {
|
|
125
|
+
setIsConnecting(false);
|
|
126
|
+
}
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Disconnect from the current wallet
|
|
131
|
+
*/
|
|
132
|
+
const disconnect = useCallback(async () => {
|
|
133
|
+
try {
|
|
134
|
+
const result = await disconnectStellarWallet();
|
|
135
|
+
|
|
136
|
+
if (result.disconnected) {
|
|
137
|
+
setAddress(null);
|
|
138
|
+
} else {
|
|
139
|
+
throw new Error(result.error || 'Failed to disconnect wallet');
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
logger.error('Failed to disconnect:', String(error));
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}, []);
|
|
146
|
+
|
|
147
|
+
const contextValue: StellarWalletContextType = {
|
|
148
|
+
address,
|
|
149
|
+
isConnected: address !== null,
|
|
150
|
+
isConnecting,
|
|
151
|
+
availableWallets,
|
|
152
|
+
connect,
|
|
153
|
+
disconnect,
|
|
154
|
+
uiKitManagerState,
|
|
155
|
+
kit: uiKitManagerState.stellarKitProvider,
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<StellarWalletContext.Provider value={contextValue}>{children}</StellarWalletContext.Provider>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { LogOut } from 'lucide-react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
|
|
4
|
+
import { Button } from '@openzeppelin/ui-components';
|
|
5
|
+
import type { BaseComponentProps } from '@openzeppelin/ui-types';
|
|
6
|
+
import { cn, getWalletAccountDisplaySizeProps, truncateMiddle } from '@openzeppelin/ui-utils';
|
|
7
|
+
|
|
8
|
+
import { useStellarAccount, useStellarDisconnect } from '../../hooks';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A component that displays the connected account address.
|
|
12
|
+
* Also includes a disconnect button.
|
|
13
|
+
*/
|
|
14
|
+
export const CustomAccountDisplay: React.FC<BaseComponentProps> = ({
|
|
15
|
+
className,
|
|
16
|
+
size,
|
|
17
|
+
variant,
|
|
18
|
+
fullWidth,
|
|
19
|
+
}) => {
|
|
20
|
+
const { isConnected, address } = useStellarAccount();
|
|
21
|
+
const { disconnect } = useStellarDisconnect();
|
|
22
|
+
|
|
23
|
+
const sizeProps = getWalletAccountDisplaySizeProps(size);
|
|
24
|
+
|
|
25
|
+
if (!isConnected || !address || !disconnect) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<div className={cn('flex items-center gap-2', fullWidth && 'w-full', className)}>
|
|
31
|
+
<div className={cn('flex flex-col', fullWidth && 'flex-1')}>
|
|
32
|
+
<span className={cn(sizeProps.textSize, 'font-medium')}>
|
|
33
|
+
{truncateMiddle(address, 4, 4)}
|
|
34
|
+
</span>
|
|
35
|
+
<span className={cn(sizeProps.subTextSize, 'text-muted-foreground -mt-0.5')}>
|
|
36
|
+
Stellar Account
|
|
37
|
+
</span>
|
|
38
|
+
</div>
|
|
39
|
+
<Button
|
|
40
|
+
onClick={() => disconnect()}
|
|
41
|
+
variant={variant || 'ghost'}
|
|
42
|
+
size="icon"
|
|
43
|
+
className={cn(sizeProps.iconButtonSize, 'p-0')}
|
|
44
|
+
title="Disconnect wallet"
|
|
45
|
+
>
|
|
46
|
+
<LogOut className={sizeProps.iconSize} />
|
|
47
|
+
</Button>
|
|
48
|
+
</div>
|
|
49
|
+
);
|
|
50
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { Loader2, Wallet } from 'lucide-react';
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
|
|
4
|
+
import { Button } from '@openzeppelin/ui-components';
|
|
5
|
+
import type { BaseComponentProps } from '@openzeppelin/ui-types';
|
|
6
|
+
import { cn, getWalletButtonSizeProps } from '@openzeppelin/ui-utils';
|
|
7
|
+
|
|
8
|
+
import { useStellarAccount } from '../../hooks';
|
|
9
|
+
import { ConnectorDialog } from './ConnectorDialog';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A button that allows users to connect their wallet.
|
|
13
|
+
* Opens a dialog to select from available connectors.
|
|
14
|
+
* @param hideWhenConnected - Whether to hide the button when wallet is connected (default: true)
|
|
15
|
+
*/
|
|
16
|
+
export interface ConnectButtonProps extends BaseComponentProps {
|
|
17
|
+
hideWhenConnected?: boolean;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const CustomConnectButton: React.FC<ConnectButtonProps> = ({
|
|
21
|
+
className,
|
|
22
|
+
size,
|
|
23
|
+
variant,
|
|
24
|
+
fullWidth,
|
|
25
|
+
hideWhenConnected = true,
|
|
26
|
+
}) => {
|
|
27
|
+
const [dialogOpen, setDialogOpen] = useState(false);
|
|
28
|
+
const { isConnected, isConnecting } = useStellarAccount();
|
|
29
|
+
|
|
30
|
+
// Local state to indicate the button has been clicked and dialog is open, awaiting user selection
|
|
31
|
+
const [isManuallyInitiated, setIsManuallyInitiated] = useState(false);
|
|
32
|
+
|
|
33
|
+
const sizeProps = getWalletButtonSizeProps(size);
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
if (isConnected && hideWhenConnected) {
|
|
37
|
+
setDialogOpen(false);
|
|
38
|
+
setIsManuallyInitiated(false); // Reset if dialog closes due to connection
|
|
39
|
+
}
|
|
40
|
+
}, [isConnected, hideWhenConnected]);
|
|
41
|
+
|
|
42
|
+
// If dialog is closed, reset manual initiation state
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (!dialogOpen) {
|
|
45
|
+
setIsManuallyInitiated(false);
|
|
46
|
+
}
|
|
47
|
+
}, [dialogOpen]);
|
|
48
|
+
|
|
49
|
+
// If wallet reports it's connecting, we no longer need our manual pending state
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (isConnecting) {
|
|
52
|
+
setIsManuallyInitiated(false);
|
|
53
|
+
}
|
|
54
|
+
}, [isConnecting]);
|
|
55
|
+
|
|
56
|
+
const handleConnectClick = () => {
|
|
57
|
+
if (!isConnected) {
|
|
58
|
+
setIsManuallyInitiated(true); // User clicked, show pending on button
|
|
59
|
+
setDialogOpen(true);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
if (isConnected && hideWhenConnected) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Button shows loading if either hook reports connecting OR if user just clicked to open dialog
|
|
68
|
+
const showButtonLoading = isConnecting || isManuallyInitiated;
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
<div className={cn('flex items-center', fullWidth && 'w-full', className)}>
|
|
72
|
+
<Button
|
|
73
|
+
onClick={handleConnectClick}
|
|
74
|
+
disabled={showButtonLoading || isConnected}
|
|
75
|
+
variant={variant || 'outline'}
|
|
76
|
+
size={sizeProps.size}
|
|
77
|
+
className={cn(sizeProps.className, fullWidth && 'w-full')}
|
|
78
|
+
title={isConnected ? 'Connected' : 'Connect Wallet'}
|
|
79
|
+
>
|
|
80
|
+
{showButtonLoading ? (
|
|
81
|
+
<Loader2 className={cn(sizeProps.iconSize, 'animate-spin mr-1')} />
|
|
82
|
+
) : (
|
|
83
|
+
<Wallet className={cn(sizeProps.iconSize, 'mr-1')} />
|
|
84
|
+
)}
|
|
85
|
+
{showButtonLoading ? 'Connecting...' : 'Connect Wallet'}
|
|
86
|
+
</Button>
|
|
87
|
+
|
|
88
|
+
<ConnectorDialog
|
|
89
|
+
open={dialogOpen}
|
|
90
|
+
onOpenChange={(open) => {
|
|
91
|
+
setDialogOpen(open);
|
|
92
|
+
// If dialog is closed manually by user before selection, reset manual initiation
|
|
93
|
+
if (!open) {
|
|
94
|
+
setIsManuallyInitiated(false);
|
|
95
|
+
}
|
|
96
|
+
}}
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
Button,
|
|
5
|
+
Dialog,
|
|
6
|
+
DialogContent,
|
|
7
|
+
DialogDescription,
|
|
8
|
+
DialogHeader,
|
|
9
|
+
DialogTitle,
|
|
10
|
+
} from '@openzeppelin/ui-components';
|
|
11
|
+
import type { Connector } from '@openzeppelin/ui-types';
|
|
12
|
+
import { logger } from '@openzeppelin/ui-utils';
|
|
13
|
+
|
|
14
|
+
import { getStellarAvailableConnectors } from '../../connection';
|
|
15
|
+
import { useStellarAccount, useStellarConnect } from '../../hooks';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Dialog component for selecting a wallet connector
|
|
19
|
+
*/
|
|
20
|
+
interface ConnectorDialogProps {
|
|
21
|
+
open: boolean;
|
|
22
|
+
onOpenChange: (open: boolean) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const ConnectorDialog: React.FC<ConnectorDialogProps> = ({ open, onOpenChange }) => {
|
|
26
|
+
const { connect } = useStellarConnect();
|
|
27
|
+
const { isConnected, isConnecting } = useStellarAccount();
|
|
28
|
+
const [connectingId, setConnectingId] = useState<string | null>(null);
|
|
29
|
+
const [error, setError] = useState<string | null>(null);
|
|
30
|
+
const [connectors, setConnectors] = useState<Connector[]>([]);
|
|
31
|
+
const [loadingConnectors, setLoadingConnectors] = useState(true);
|
|
32
|
+
|
|
33
|
+
// Load available connectors
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
const loadConnectors = async () => {
|
|
36
|
+
try {
|
|
37
|
+
const availableConnectors = await getStellarAvailableConnectors();
|
|
38
|
+
setConnectors(availableConnectors);
|
|
39
|
+
} catch (err) {
|
|
40
|
+
logger.error('Failed to load Stellar connectors:', String(err));
|
|
41
|
+
setError('Failed to load available wallets');
|
|
42
|
+
} finally {
|
|
43
|
+
setLoadingConnectors(false);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
if (open) {
|
|
48
|
+
loadConnectors();
|
|
49
|
+
}
|
|
50
|
+
}, [open]);
|
|
51
|
+
|
|
52
|
+
// Track connection attempts for dialog closure
|
|
53
|
+
useEffect(() => {
|
|
54
|
+
// If we're connected and there was a connection attempt, close the dialog
|
|
55
|
+
if (isConnected && connectingId) {
|
|
56
|
+
onOpenChange(false);
|
|
57
|
+
setConnectingId(null);
|
|
58
|
+
setError(null);
|
|
59
|
+
}
|
|
60
|
+
}, [isConnected, connectingId, onOpenChange]);
|
|
61
|
+
|
|
62
|
+
// If connect function itself is not available
|
|
63
|
+
if (!connect) {
|
|
64
|
+
return (
|
|
65
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
66
|
+
<DialogContent className="sm:max-w-[425px]">
|
|
67
|
+
<DialogHeader>
|
|
68
|
+
<DialogTitle>Error</DialogTitle>
|
|
69
|
+
</DialogHeader>
|
|
70
|
+
<p>Wallet connection function is not available.</p>
|
|
71
|
+
</DialogContent>
|
|
72
|
+
</Dialog>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const handleConnectorSelect = async (selectedConnector: Connector) => {
|
|
77
|
+
setConnectingId(selectedConnector.id);
|
|
78
|
+
setError(null);
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
await connect({ connector: selectedConnector });
|
|
82
|
+
} catch (err) {
|
|
83
|
+
setError(err instanceof Error ? err.message : 'Failed to connect');
|
|
84
|
+
setConnectingId(null);
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
90
|
+
<DialogContent className="sm:max-w-[425px]">
|
|
91
|
+
<DialogHeader>
|
|
92
|
+
<DialogTitle>Connect Wallet</DialogTitle>
|
|
93
|
+
<DialogDescription>
|
|
94
|
+
Select a wallet provider to connect with this application.
|
|
95
|
+
</DialogDescription>
|
|
96
|
+
</DialogHeader>
|
|
97
|
+
|
|
98
|
+
<div className="grid gap-4 py-4">
|
|
99
|
+
{loadingConnectors ? (
|
|
100
|
+
<p className="text-center text-muted-foreground">Loading available wallets...</p>
|
|
101
|
+
) : connectors.length === 0 ? (
|
|
102
|
+
<p className="text-center text-muted-foreground">No wallet connectors available.</p>
|
|
103
|
+
) : (
|
|
104
|
+
connectors.map((connector: Connector) => (
|
|
105
|
+
<Button
|
|
106
|
+
key={connector.id}
|
|
107
|
+
onClick={() => handleConnectorSelect(connector)}
|
|
108
|
+
disabled={isConnecting && connectingId === connector.id}
|
|
109
|
+
variant="outline"
|
|
110
|
+
className="flex justify-between items-center w-full py-6"
|
|
111
|
+
>
|
|
112
|
+
<span>{connector.name}</span>
|
|
113
|
+
{isConnecting && connectingId === connector.id && (
|
|
114
|
+
<span className="ml-2 text-xs">Connecting...</span>
|
|
115
|
+
)}
|
|
116
|
+
</Button>
|
|
117
|
+
))
|
|
118
|
+
)}
|
|
119
|
+
</div>
|
|
120
|
+
|
|
121
|
+
{error && <p className="text-sm text-red-500 mt-1">{error}</p>}
|
|
122
|
+
</DialogContent>
|
|
123
|
+
</Dialog>
|
|
124
|
+
);
|
|
125
|
+
};
|