@phantom/embedded-provider-core 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,349 @@
1
+ # @phantom/embedded-provider-core
2
+
3
+ Platform-agnostic embedded provider core logic for Phantom Wallet SDK.
4
+
5
+ ## Overview
6
+
7
+ This package contains the core business logic for Phantom's embedded wallet provider, designed to be shared across different platforms (browser, React Native, etc.). It provides a unified interface for wallet operations while allowing platform-specific implementations through adapters.
8
+
9
+ ## Architecture
10
+
11
+ The embedded provider core follows a platform adapter pattern:
12
+
13
+ ```
14
+ ┌─────────────────────────────────────┐
15
+ │ Platform SDK │
16
+ │ (browser-sdk, react-native) │
17
+ └─────────────┬───────────────────────┘
18
+
19
+ ┌─────────────▼───────────────────────┐
20
+ │ EmbeddedProvider (Core) │
21
+ │ - Authentication flows │
22
+ │ - Session management │
23
+ │ - Wallet operations │
24
+ │ - Business logic │
25
+ └─────────────┬───────────────────────┘
26
+
27
+ ┌─────────────▼───────────────────────┐
28
+ │ Platform Adapters │
29
+ │ - Storage (IndexedDB/AsyncStorage) │
30
+ │ - Auth (redirects/deep links) │
31
+ │ - URL params (window/linking) │
32
+ │ - Logger (debug/console) │
33
+ └─────────────────────────────────────┘
34
+ ```
35
+
36
+ ## Key Features
37
+
38
+ - **Cross-platform compatibility**: Share 90%+ of wallet logic across platforms
39
+ - **Clean separation of concerns**: Platform-specific code isolated in adapters
40
+ - **Multiple auth flows**: JWT, Google OAuth, Apple OAuth, app-wallet creation
41
+ - **Session management**: Persistent sessions with validation and recovery
42
+ - **Retry logic**: Built-in retry with exponential backoff for network operations
43
+ - **Type safety**: Full TypeScript support with comprehensive interfaces
44
+
45
+ ## Usage
46
+
47
+ ### Basic Setup
48
+
49
+ ```typescript
50
+ import {
51
+ EmbeddedProvider,
52
+ EmbeddedProviderConfig,
53
+ PlatformAdapter,
54
+ DebugLogger,
55
+ } from "@phantom/embedded-provider-core";
56
+
57
+ // 1. Define your configuration
58
+ const config: EmbeddedProviderConfig = {
59
+ apiBaseUrl: "https://api.phantom.app",
60
+ organizationId: "your-org-id",
61
+ embeddedWalletType: "user-wallet", // or 'app-wallet'
62
+ addressTypes: ["solana", "ethereum"],
63
+ solanaProvider: "web3js",
64
+ authOptions: {
65
+ authUrl: "https://auth.phantom.app",
66
+ redirectUrl: "https://your-app.com/callback",
67
+ },
68
+ };
69
+
70
+ // 2. Implement platform adapters (see examples below)
71
+ const platform: PlatformAdapter = {
72
+ storage: new YourStorageAdapter(),
73
+ authProvider: new YourAuthProvider(),
74
+ urlParamsAccessor: new YourURLParamsAccessor(),
75
+ };
76
+
77
+ // 3. Implement logger
78
+ const logger: DebugLogger = new YourLogger();
79
+
80
+ // 4. Create provider instance
81
+ const provider = new EmbeddedProvider(config, platform, logger);
82
+
83
+ // 5. Use the provider
84
+ const result = await provider.connect();
85
+ console.log("Connected addresses:", result.addresses);
86
+ ```
87
+
88
+ ## Platform Adapter Interfaces
89
+
90
+ ### Storage Adapter
91
+
92
+ Handles persistent session storage:
93
+
94
+ ```typescript
95
+ import { EmbeddedStorage, Session } from "@phantom/embedded-provider-core";
96
+
97
+ export class YourStorageAdapter implements EmbeddedStorage {
98
+ async getSession(): Promise<Session | null> {
99
+ // Platform-specific session retrieval
100
+ // Browser: IndexedDB, React Native: AsyncStorage
101
+ }
102
+
103
+ async saveSession(session: Session): Promise<void> {
104
+ // Platform-specific session storage
105
+ }
106
+
107
+ async clearSession(): Promise<void> {
108
+ // Platform-specific session cleanup
109
+ }
110
+ }
111
+ ```
112
+
113
+ ### Auth Provider
114
+
115
+ Handles authentication flows:
116
+
117
+ ```typescript
118
+ import { AuthProvider, AuthResult, PhantomConnectOptions, JWTAuthOptions } from "@phantom/embedded-provider-core";
119
+
120
+ export class YourAuthProvider implements AuthProvider {
121
+ async authenticate(options: PhantomConnectOptions | JWTAuthOptions): Promise<void | AuthResult> {
122
+ // Platform-specific authentication
123
+ // Browser: window redirects, React Native: deep links
124
+ }
125
+
126
+ resumeAuthFromRedirect?(): AuthResult | null {
127
+ // Resume authentication after redirect/deep link
128
+ }
129
+ }
130
+ ```
131
+
132
+ ### URL Params Accessor
133
+
134
+ Handles URL parameter access:
135
+
136
+ ```typescript
137
+ import { URLParamsAccessor } from "@phantom/embedded-provider-core";
138
+
139
+ export class YourURLParamsAccessor implements URLParamsAccessor {
140
+ getParam(key: string): string | null {
141
+ // Platform-specific URL parameter access
142
+ // Browser: window.location.search, React Native: Linking.getInitialURL()
143
+ }
144
+ }
145
+ ```
146
+
147
+ ### Debug Logger
148
+
149
+ Handles logging:
150
+
151
+ ```typescript
152
+ import { DebugLogger } from "@phantom/embedded-provider-core";
153
+
154
+ export class YourLogger implements DebugLogger {
155
+ info(category: string, message: string, data?: any): void {
156
+ // Platform-specific logging
157
+ }
158
+
159
+ warn(category: string, message: string, data?: any): void {
160
+ // Platform-specific warning
161
+ }
162
+
163
+ error(category: string, message: string, data?: any): void {
164
+ // Platform-specific error logging
165
+ }
166
+
167
+ log(category: string, message: string, data?: any): void {
168
+ // Platform-specific debug logging
169
+ }
170
+ }
171
+ ```
172
+
173
+ ## Authentication Flows
174
+
175
+ ### JWT Authentication
176
+
177
+ For server-side authenticated users:
178
+
179
+ ```typescript
180
+ const authOptions = {
181
+ provider: "jwt",
182
+ jwtToken: "your-jwt-token",
183
+ customAuthData: { userId: "user123" },
184
+ };
185
+
186
+ const result = await provider.connect(authOptions);
187
+ ```
188
+
189
+ ### OAuth Authentication
190
+
191
+ For social login flows:
192
+
193
+ ```typescript
194
+ // Google OAuth
195
+ const authOptions = {
196
+ provider: "google",
197
+ customAuthData: { referrer: "landing-page" },
198
+ };
199
+
200
+ await provider.connect(authOptions); // Will redirect/deep link
201
+
202
+ // Apple OAuth
203
+ const authOptions = {
204
+ provider: "apple",
205
+ };
206
+
207
+ await provider.connect(authOptions); // Will redirect/deep link
208
+ ```
209
+
210
+ ### App Wallet
211
+
212
+ For application-controlled wallets:
213
+
214
+ ```typescript
215
+ const config = {
216
+ // ... other config
217
+ embeddedWalletType: "app-wallet",
218
+ };
219
+
220
+ const result = await provider.connect(); // No auth needed
221
+ ```
222
+
223
+ ## Session Management
224
+
225
+ Sessions are automatically managed by the core provider:
226
+
227
+ ```typescript
228
+ // Check connection status
229
+ if (provider.isConnected()) {
230
+ const addresses = provider.getAddresses();
231
+ console.log("Available addresses:", addresses);
232
+ }
233
+
234
+ // Sign a message
235
+ const signature = await provider.signMessage({
236
+ message: "Hello, world!",
237
+ networkId: "solana:mainnet",
238
+ });
239
+
240
+ // Sign and send transaction
241
+ const result = await provider.signAndSendTransaction({
242
+ transaction: transactionBytes,
243
+ networkId: "solana:mainnet",
244
+ });
245
+
246
+ // Disconnect
247
+ await provider.disconnect();
248
+ ```
249
+
250
+ ## Error Handling
251
+
252
+ The core provider provides detailed error messages:
253
+
254
+ ```typescript
255
+ try {
256
+ await provider.connect();
257
+ } catch (error) {
258
+ if (error.message.includes("JWT")) {
259
+ // Handle JWT authentication errors
260
+ } else if (error.message.includes("Storage")) {
261
+ // Handle storage errors
262
+ } else if (error.message.includes("Network")) {
263
+ // Handle network errors
264
+ }
265
+ }
266
+ ```
267
+
268
+ ## Configuration Options
269
+
270
+ ```typescript
271
+ interface EmbeddedProviderConfig {
272
+ // Required
273
+ apiBaseUrl: string; // Phantom API base URL
274
+ organizationId: string; // Your organization ID
275
+ embeddedWalletType: "app-wallet" | "user-wallet";
276
+ addressTypes: AddressType[]; // Supported blockchain addresses
277
+
278
+ // Optional
279
+ solanaProvider?: "web3js" | "kit"; // Solana library preference
280
+ authOptions?: {
281
+ authUrl?: string; // Custom auth URL
282
+ redirectUrl?: string; // OAuth redirect URL
283
+ };
284
+ }
285
+ ```
286
+
287
+ ## Session Object
288
+
289
+ ```typescript
290
+ interface Session {
291
+ sessionId: string; // Unique session identifier
292
+ walletId: string; // Phantom wallet ID
293
+ organizationId: string; // Organization ID
294
+ keypair: {
295
+ // Cryptographic keypair
296
+ publicKey: string;
297
+ secretKey: string;
298
+ };
299
+ authProvider: string; // Auth method used
300
+ userInfo: Record<string, any>; // User information
301
+ status: "pending" | "completed"; // Session status
302
+ createdAt: number; // Creation timestamp
303
+ lastUsed: number; // Last access timestamp
304
+ }
305
+ ```
306
+
307
+ ## Best Practices
308
+
309
+ 1. **Platform Adapters**: Keep platform-specific code in adapters only
310
+ 2. **Error Handling**: Always handle authentication and network errors
311
+ 3. **Session Validation**: The core automatically validates sessions
312
+ 4. **Logging**: Use structured logging with categories for debugging
313
+ 5. **Security**: Never log sensitive data like private keys
314
+
315
+ ## Examples
316
+
317
+ See the `@phantom/browser-sdk` package for a complete implementation example using this core package with browser-specific adapters.
318
+
319
+ ## Development
320
+
321
+ ```bash
322
+ # Install dependencies
323
+ yarn install
324
+
325
+ # Build the package
326
+ yarn build
327
+
328
+ # Run tests
329
+ yarn test
330
+
331
+ # Run linting
332
+ yarn lint
333
+
334
+ # Format code
335
+ yarn prettier
336
+ ```
337
+
338
+ ## Contributing
339
+
340
+ This package is designed to be extended for new platforms. When adding support for a new platform:
341
+
342
+ 1. Implement all required platform adapter interfaces
343
+ 2. Add comprehensive tests for your adapters
344
+ 3. Update documentation with platform-specific examples
345
+ 4. Ensure error handling covers platform-specific edge cases
346
+
347
+ ## License
348
+
349
+ MIT
@@ -0,0 +1,154 @@
1
+ import { AddressType, NetworkId } from '@phantom/client';
2
+
3
+ interface Keypair {
4
+ publicKey: string;
5
+ secretKey: string;
6
+ }
7
+ interface Session {
8
+ sessionId: string;
9
+ walletId: string;
10
+ organizationId: string;
11
+ keypair: Keypair;
12
+ authProvider?: string;
13
+ userInfo?: Record<string, any>;
14
+ status: "pending" | "completed" | "failed";
15
+ createdAt: number;
16
+ lastUsed: number;
17
+ }
18
+ interface EmbeddedStorage {
19
+ getSession(): Promise<Session | null>;
20
+ saveSession(session: Session): Promise<void>;
21
+ clearSession(): Promise<void>;
22
+ }
23
+
24
+ /**
25
+ * URL parameter accessor abstraction
26
+ *
27
+ * This allows us to abstract URL parameter access for different environments:
28
+ * - Browser: window.location.search via URLSearchParams
29
+ * - React Native: deep links or webview callback URLs
30
+ * - Mobile WebView: in-app browser navigation state
31
+ *
32
+ * This abstraction makes the auth flow code portable across platforms
33
+ * without needing to mock window.location directly in tests.
34
+ */
35
+ interface URLParamsAccessor {
36
+ /**
37
+ * Get a URL parameter value by key
38
+ * @param key - The parameter key to retrieve
39
+ * @returns The parameter value or null if not found
40
+ */
41
+ getParam(key: string): string | null;
42
+ }
43
+
44
+ interface AuthResult {
45
+ walletId: string;
46
+ provider?: string;
47
+ userInfo?: Record<string, any>;
48
+ }
49
+ interface PhantomConnectOptions {
50
+ organizationId: string;
51
+ parentOrganizationId: string;
52
+ provider?: "google" | "apple";
53
+ redirectUrl?: string;
54
+ customAuthData?: Record<string, any>;
55
+ authUrl?: string;
56
+ sessionId: string;
57
+ }
58
+ interface JWTAuthOptions {
59
+ organizationId: string;
60
+ parentOrganizationId: string;
61
+ jwtToken: string;
62
+ customAuthData?: Record<string, any>;
63
+ }
64
+ interface AuthProvider {
65
+ authenticate(options: PhantomConnectOptions | JWTAuthOptions): Promise<void | AuthResult>;
66
+ resumeAuthFromRedirect?(): AuthResult | null;
67
+ }
68
+
69
+ interface PlatformAdapter {
70
+ storage: EmbeddedStorage;
71
+ authProvider: AuthProvider;
72
+ urlParamsAccessor: URLParamsAccessor;
73
+ }
74
+ interface DebugLogger {
75
+ info(category: string, message: string, data?: any): void;
76
+ warn(category: string, message: string, data?: any): void;
77
+ error(category: string, message: string, data?: any): void;
78
+ log(category: string, message: string, data?: any): void;
79
+ }
80
+
81
+ interface WalletAddress {
82
+ addressType: AddressType;
83
+ address: string;
84
+ }
85
+ interface ConnectResult {
86
+ walletId?: string;
87
+ addresses: WalletAddress[];
88
+ status?: "pending" | "completed";
89
+ }
90
+ interface SignMessageParams {
91
+ message: string;
92
+ networkId: NetworkId;
93
+ }
94
+ interface SignAndSendTransactionParams {
95
+ transaction: any;
96
+ networkId: NetworkId;
97
+ }
98
+ interface SignedTransaction {
99
+ rawTransaction: string;
100
+ }
101
+ interface AuthOptions {
102
+ provider?: "google" | "apple" | "jwt";
103
+ jwtToken?: string;
104
+ customAuthData?: Record<string, any>;
105
+ }
106
+ interface EmbeddedProviderConfig {
107
+ apiBaseUrl: string;
108
+ organizationId: string;
109
+ authOptions?: {
110
+ authUrl?: string;
111
+ redirectUrl?: string;
112
+ };
113
+ embeddedWalletType: "app-wallet" | "user-wallet";
114
+ addressTypes: AddressType[];
115
+ solanaProvider: "web3js" | "kit";
116
+ }
117
+
118
+ declare class EmbeddedProvider {
119
+ private config;
120
+ private storage;
121
+ private authProvider;
122
+ private urlParamsAccessor;
123
+ private logger;
124
+ private client;
125
+ private walletId;
126
+ private addresses;
127
+ private jwtAuth;
128
+ constructor(config: EmbeddedProviderConfig, platform: PlatformAdapter, logger: DebugLogger);
129
+ private getAndFilterWalletAddresses;
130
+ private validateAndCleanSession;
131
+ private validateAuthOptions;
132
+ private createOrganizationAndKeypair;
133
+ connect(authOptions?: AuthOptions): Promise<ConnectResult>;
134
+ disconnect(): Promise<void>;
135
+ signMessage(params: SignMessageParams): Promise<string>;
136
+ signAndSendTransaction(params: SignAndSendTransactionParams): Promise<SignedTransaction>;
137
+ getAddresses(): WalletAddress[];
138
+ isConnected(): boolean;
139
+ private handleAuthFlow;
140
+ private handleJWTAuth;
141
+ private handleRedirectAuth;
142
+ private completeAuthConnection;
143
+ private initializeClientFromSession;
144
+ }
145
+
146
+ declare class JWTAuth {
147
+ authenticate(options: JWTAuthOptions): Promise<AuthResult>;
148
+ }
149
+
150
+ declare function generateSessionId(): string;
151
+
152
+ declare function retryWithBackoff<T>(operation: () => Promise<T>, operationName: string, logger: DebugLogger, maxRetries?: number, baseDelay?: number): Promise<T>;
153
+
154
+ export { AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedStorage, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignedTransaction, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };
@@ -0,0 +1,154 @@
1
+ import { AddressType, NetworkId } from '@phantom/client';
2
+
3
+ interface Keypair {
4
+ publicKey: string;
5
+ secretKey: string;
6
+ }
7
+ interface Session {
8
+ sessionId: string;
9
+ walletId: string;
10
+ organizationId: string;
11
+ keypair: Keypair;
12
+ authProvider?: string;
13
+ userInfo?: Record<string, any>;
14
+ status: "pending" | "completed" | "failed";
15
+ createdAt: number;
16
+ lastUsed: number;
17
+ }
18
+ interface EmbeddedStorage {
19
+ getSession(): Promise<Session | null>;
20
+ saveSession(session: Session): Promise<void>;
21
+ clearSession(): Promise<void>;
22
+ }
23
+
24
+ /**
25
+ * URL parameter accessor abstraction
26
+ *
27
+ * This allows us to abstract URL parameter access for different environments:
28
+ * - Browser: window.location.search via URLSearchParams
29
+ * - React Native: deep links or webview callback URLs
30
+ * - Mobile WebView: in-app browser navigation state
31
+ *
32
+ * This abstraction makes the auth flow code portable across platforms
33
+ * without needing to mock window.location directly in tests.
34
+ */
35
+ interface URLParamsAccessor {
36
+ /**
37
+ * Get a URL parameter value by key
38
+ * @param key - The parameter key to retrieve
39
+ * @returns The parameter value or null if not found
40
+ */
41
+ getParam(key: string): string | null;
42
+ }
43
+
44
+ interface AuthResult {
45
+ walletId: string;
46
+ provider?: string;
47
+ userInfo?: Record<string, any>;
48
+ }
49
+ interface PhantomConnectOptions {
50
+ organizationId: string;
51
+ parentOrganizationId: string;
52
+ provider?: "google" | "apple";
53
+ redirectUrl?: string;
54
+ customAuthData?: Record<string, any>;
55
+ authUrl?: string;
56
+ sessionId: string;
57
+ }
58
+ interface JWTAuthOptions {
59
+ organizationId: string;
60
+ parentOrganizationId: string;
61
+ jwtToken: string;
62
+ customAuthData?: Record<string, any>;
63
+ }
64
+ interface AuthProvider {
65
+ authenticate(options: PhantomConnectOptions | JWTAuthOptions): Promise<void | AuthResult>;
66
+ resumeAuthFromRedirect?(): AuthResult | null;
67
+ }
68
+
69
+ interface PlatformAdapter {
70
+ storage: EmbeddedStorage;
71
+ authProvider: AuthProvider;
72
+ urlParamsAccessor: URLParamsAccessor;
73
+ }
74
+ interface DebugLogger {
75
+ info(category: string, message: string, data?: any): void;
76
+ warn(category: string, message: string, data?: any): void;
77
+ error(category: string, message: string, data?: any): void;
78
+ log(category: string, message: string, data?: any): void;
79
+ }
80
+
81
+ interface WalletAddress {
82
+ addressType: AddressType;
83
+ address: string;
84
+ }
85
+ interface ConnectResult {
86
+ walletId?: string;
87
+ addresses: WalletAddress[];
88
+ status?: "pending" | "completed";
89
+ }
90
+ interface SignMessageParams {
91
+ message: string;
92
+ networkId: NetworkId;
93
+ }
94
+ interface SignAndSendTransactionParams {
95
+ transaction: any;
96
+ networkId: NetworkId;
97
+ }
98
+ interface SignedTransaction {
99
+ rawTransaction: string;
100
+ }
101
+ interface AuthOptions {
102
+ provider?: "google" | "apple" | "jwt";
103
+ jwtToken?: string;
104
+ customAuthData?: Record<string, any>;
105
+ }
106
+ interface EmbeddedProviderConfig {
107
+ apiBaseUrl: string;
108
+ organizationId: string;
109
+ authOptions?: {
110
+ authUrl?: string;
111
+ redirectUrl?: string;
112
+ };
113
+ embeddedWalletType: "app-wallet" | "user-wallet";
114
+ addressTypes: AddressType[];
115
+ solanaProvider: "web3js" | "kit";
116
+ }
117
+
118
+ declare class EmbeddedProvider {
119
+ private config;
120
+ private storage;
121
+ private authProvider;
122
+ private urlParamsAccessor;
123
+ private logger;
124
+ private client;
125
+ private walletId;
126
+ private addresses;
127
+ private jwtAuth;
128
+ constructor(config: EmbeddedProviderConfig, platform: PlatformAdapter, logger: DebugLogger);
129
+ private getAndFilterWalletAddresses;
130
+ private validateAndCleanSession;
131
+ private validateAuthOptions;
132
+ private createOrganizationAndKeypair;
133
+ connect(authOptions?: AuthOptions): Promise<ConnectResult>;
134
+ disconnect(): Promise<void>;
135
+ signMessage(params: SignMessageParams): Promise<string>;
136
+ signAndSendTransaction(params: SignAndSendTransactionParams): Promise<SignedTransaction>;
137
+ getAddresses(): WalletAddress[];
138
+ isConnected(): boolean;
139
+ private handleAuthFlow;
140
+ private handleJWTAuth;
141
+ private handleRedirectAuth;
142
+ private completeAuthConnection;
143
+ private initializeClientFromSession;
144
+ }
145
+
146
+ declare class JWTAuth {
147
+ authenticate(options: JWTAuthOptions): Promise<AuthResult>;
148
+ }
149
+
150
+ declare function generateSessionId(): string;
151
+
152
+ declare function retryWithBackoff<T>(operation: () => Promise<T>, operationName: string, logger: DebugLogger, maxRetries?: number, baseDelay?: number): Promise<T>;
153
+
154
+ export { AuthOptions, AuthProvider, AuthResult, ConnectResult, DebugLogger, EmbeddedProvider, EmbeddedProviderConfig, EmbeddedStorage, JWTAuth, JWTAuthOptions, Keypair, PhantomConnectOptions, PlatformAdapter, Session, SignAndSendTransactionParams, SignMessageParams, SignedTransaction, URLParamsAccessor, WalletAddress, generateSessionId, retryWithBackoff };