@etherplay/connect 0.0.35 → 0.0.36

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/src/index.ts CHANGED
@@ -68,6 +68,8 @@ export type Mechanism = AlchemyMechanism | WalletMechanism<string | undefined, `
68
68
 
69
69
  export type FullfilledMechanism = AlchemyMechanism | WalletMechanism<string, `0x${string}`>;
70
70
 
71
+ export type TargetStep = 'WalletConnected' | 'SignedIn';
72
+
71
73
  export type WalletState<WalletProviderType> = {
72
74
  provider: WalletProvider<WalletProviderType>;
73
75
  accounts: `0x${string}`[];
@@ -77,12 +79,18 @@ export type WalletState<WalletProviderType> = {
77
79
  switchingChain: 'addingChain' | 'switchingChain' | false;
78
80
  } & ({status: 'connected'} | {status: 'locked'; unlocking: boolean} | {status: 'disconnected'; connecting: boolean});
79
81
 
80
- type WalletConnected<WalletProviderType> = {
82
+ type WaitingForSignature<WalletProviderType> = {
81
83
  step: 'WaitingForSignature';
82
84
  mechanism: WalletMechanism<string, `0x${string}`>;
83
85
  wallet: WalletState<WalletProviderType>;
84
86
  };
85
87
 
88
+ type WalletConnected<WalletProviderType> = {
89
+ step: 'WalletConnected';
90
+ mechanism: WalletMechanism<string, `0x${string}`>;
91
+ wallet: WalletState<WalletProviderType>;
92
+ };
93
+
86
94
  type SignedIn<WalletProviderType> =
87
95
  | {
88
96
  step: 'SignedIn';
@@ -144,13 +152,9 @@ export type Connection<WalletProviderType> = {
144
152
  }
145
153
  // Once the wallet is connected, the system will need a signature
146
154
  // this state represent the fact and require another user interaction to request the signature
147
- | {
148
- step: 'WalletConnected';
149
- mechanism: WalletMechanism<string, `0x${string}`>;
150
- wallet: WalletState<WalletProviderType>;
151
- }
152
- // This state is triggered once the signature is requested, the user will have to confirm with its wallet
153
155
  | WalletConnected<WalletProviderType>
156
+ // This state is triggered once the signature is requested, the user will have to confirm with its wallet
157
+ | WaitingForSignature<WalletProviderType>
154
158
  // Finally the user is fully signed in
155
159
  // wallet?.accountChanged if set, represent the fact that the user has changed its web3-wallet accounnt.
156
160
  // wallet?.invalidChainId if set, represent the fact that the wallet is connected to a different chain.
@@ -159,6 +163,56 @@ export type Connection<WalletProviderType> = {
159
163
  | SignedIn<WalletProviderType>
160
164
  );
161
165
 
166
+ // Type for SignedIn state that was reached via wallet authentication (not popup-based auth)
167
+ // This variant always has wallet and WalletMechanism
168
+ export type SignedInWithWallet<WalletProviderType> = Extract<
169
+ Connection<WalletProviderType>,
170
+ {step: 'SignedIn'; wallet: WalletState<WalletProviderType>}
171
+ >;
172
+
173
+ // Full WalletConnected type from Connection
174
+ export type WalletConnectedState<WalletProviderType> = Extract<
175
+ Connection<WalletProviderType>,
176
+ {step: 'WalletConnected'}
177
+ >;
178
+
179
+ // Type representing wallet-connected states (both WalletConnected and SignedIn-via-wallet)
180
+ // This is what you get when targetStep is 'WalletConnected' and target is reached
181
+ // Both variants have WalletMechanism and wallet
182
+ export type ConnectedWithWallet<WalletProviderType> =
183
+ | WalletConnectedState<WalletProviderType>
184
+ | SignedInWithWallet<WalletProviderType>;
185
+
186
+ // Full SignedIn type from Connection (includes both popup-based and wallet-based variants)
187
+ export type SignedInState<WalletProviderType> = Extract<Connection<WalletProviderType>, {step: 'SignedIn'}>;
188
+
189
+ // Type guard - narrows Connection based on targetStep and walletOnly
190
+ // For 'WalletConnected' target: narrows to ConnectedWithWallet (WalletConnected | SignedIn-with-wallet)
191
+ // For 'SignedIn' target with walletOnly: narrows to SignedInWithWallet
192
+ // For 'SignedIn' target (default): narrows to SignedIn
193
+ export function isTargetStepReached<WalletProviderType, Target extends TargetStep, WalletOnly extends boolean = false>(
194
+ connection: Connection<WalletProviderType>,
195
+ targetStep: Target,
196
+ walletOnly?: WalletOnly,
197
+ ): connection is Target extends 'WalletConnected'
198
+ ? ConnectedWithWallet<WalletProviderType>
199
+ : WalletOnly extends true
200
+ ? SignedInWithWallet<WalletProviderType>
201
+ : SignedInState<WalletProviderType> {
202
+ if (targetStep === 'WalletConnected') {
203
+ // For WalletConnected target, accept WalletConnected OR SignedIn-with-wallet
204
+ return connection.step === 'WalletConnected' || (connection.step === 'SignedIn' && connection.wallet !== undefined);
205
+ }
206
+ // For SignedIn target (regardless of walletOnly), only accept SignedIn
207
+ // walletOnly affects the return type narrowing, not the step check
208
+ if (walletOnly) {
209
+ // For SignedIn + walletOnly, only accept SignedIn-with-wallet
210
+ return connection.step === 'SignedIn' && connection.wallet !== undefined;
211
+ }
212
+ // For SignedIn target, accept any SignedIn variant
213
+ return connection.step === 'SignedIn';
214
+ }
215
+
162
216
  function viemChainInfoToSwitchChainInfo(chainInfo: BasicChainInfo): {
163
217
  chainId: `0x${string}`;
164
218
  readonly rpcUrls?: readonly string[];
@@ -183,15 +237,25 @@ function viemChainInfoToSwitchChainInfo(chainInfo: BasicChainInfo): {
183
237
  const storageKeyAccount = '__origin_account';
184
238
  const storageKeyLastWallet = '__last_wallet';
185
239
 
186
- export type ConnectionStore<WalletProviderType> = {
240
+ export type ConnectionOptions = {
241
+ requireUserConfirmationBeforeSignatureRequest?: boolean;
242
+ doNotStoreLocally?: boolean;
243
+ requestSignatureRightAway?: boolean;
244
+ };
245
+
246
+ export type ConnectionStore<
247
+ WalletProviderType,
248
+ Target extends TargetStep = 'SignedIn',
249
+ WalletOnly extends boolean = false,
250
+ > = {
187
251
  subscribe: (run: (value: Connection<WalletProviderType>) => void) => () => void;
188
252
  connect: (
189
- mechanism?: Mechanism,
190
- options?: {
191
- requireUserConfirmationBeforeSignatureRequest?: boolean;
192
- doNotStoreLocally?: boolean;
193
- requestSignatureRightAway?: boolean;
194
- },
253
+ mechanism?: Target extends 'WalletConnected'
254
+ ? WalletMechanism<string | undefined, `0x${string}` | undefined>
255
+ : WalletOnly extends true
256
+ ? WalletMechanism<string | undefined, `0x${string}` | undefined>
257
+ : Mechanism,
258
+ options?: ConnectionOptions,
195
259
  ) => Promise<void>;
196
260
  cancel: () => void;
197
261
  back: (step: 'MechanismToChoose' | 'Idle' | 'WalletToChoose') => void;
@@ -204,78 +268,161 @@ export type ConnectionStore<WalletProviderType> = {
204
268
  getSignatureForPublicKeyPublication: () => Promise<`0x${string}`>;
205
269
  switchWalletChain: (chainInfo?: BasicChainInfo) => Promise<void>;
206
270
  unlock: () => Promise<void>;
207
- ensureConnected: {
208
- (
209
- step: 'WalletConnected',
210
- mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
211
- options?: {
212
- requireUserConfirmationBeforeSignatureRequest?: boolean;
213
- doNotStoreLocally?: boolean;
214
- requestSignatureRightAway?: boolean;
215
- },
216
- ): Promise<WalletConnected<WalletProviderType>>;
217
- (
218
- step: 'SignedIn',
219
- mechanism?: Mechanism,
220
- options?: {
221
- requireUserConfirmationBeforeSignatureRequest?: boolean;
222
- doNotStoreLocally?: boolean;
223
- requestSignatureRightAway?: boolean;
224
- },
225
- ): Promise<SignedIn<WalletProviderType>>;
226
- (
227
- mechanism?: Mechanism,
228
- options?: {
229
- requireUserConfirmationBeforeSignatureRequest?: boolean;
230
- doNotStoreLocally?: boolean;
231
- requestSignatureRightAway?: boolean;
232
- },
233
- ): Promise<SignedIn<WalletProviderType>>;
234
- };
271
+
272
+ // ensureConnected signature depends on target and walletOnly
273
+ ensureConnected: Target extends 'WalletConnected'
274
+ ? {
275
+ (options?: ConnectionOptions): Promise<WalletConnected<WalletProviderType>>;
276
+ (
277
+ step: 'WalletConnected',
278
+ mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
279
+ options?: ConnectionOptions,
280
+ ): Promise<WalletConnected<WalletProviderType>>;
281
+ }
282
+ : WalletOnly extends true
283
+ ? {
284
+ // walletOnly: true for SignedIn - returns SignedInWithWallet (not full SignedIn union)
285
+ (options?: ConnectionOptions): Promise<SignedInWithWallet<WalletProviderType>>;
286
+ (
287
+ step: 'WalletConnected',
288
+ mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
289
+ options?: ConnectionOptions,
290
+ ): Promise<WalletConnected<WalletProviderType>>;
291
+ (
292
+ step: 'SignedIn',
293
+ mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
294
+ options?: ConnectionOptions,
295
+ ): Promise<SignedInWithWallet<WalletProviderType>>;
296
+ }
297
+ : {
298
+ (options?: ConnectionOptions): Promise<SignedIn<WalletProviderType>>;
299
+ (
300
+ step: 'WalletConnected',
301
+ mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
302
+ options?: ConnectionOptions,
303
+ ): Promise<WalletConnected<WalletProviderType>>;
304
+ (step: 'SignedIn', mechanism?: Mechanism, options?: ConnectionOptions): Promise<SignedIn<WalletProviderType>>;
305
+ };
306
+
307
+ // Method to check if target step is reached with proper type narrowing
308
+ isTargetStepReached: (
309
+ connection: Connection<WalletProviderType>,
310
+ ) => connection is Target extends 'WalletConnected'
311
+ ? ConnectedWithWallet<WalletProviderType>
312
+ : WalletOnly extends true
313
+ ? SignedInWithWallet<WalletProviderType>
314
+ : SignedInState<WalletProviderType>;
315
+
316
+ // New properties
317
+ targetStep: Target;
318
+ walletOnly: WalletOnly;
319
+
320
+ // Existing properties
235
321
  provider: WalletProviderType;
236
322
  chainId: string;
237
323
  chainInfo: ChainInfo<WalletProviderType>;
238
324
  };
239
325
 
240
326
  // Function overloads for proper typing
327
+
328
+ // WalletConnected target with custom wallet connector - walletHost optional
241
329
  export function createConnection<WalletProviderType>(settings: {
242
- signingOrigin?: string;
243
- walletHost: string;
330
+ targetStep: 'WalletConnected';
331
+ walletHost?: string;
332
+ chainInfo: ChainInfo<WalletProviderType>;
333
+ walletConnector: WalletConnector<WalletProviderType>;
244
334
  autoConnect?: boolean;
245
- autoConnectWallet?: boolean;
335
+ alwaysUseCurrentAccount?: boolean;
336
+ prioritizeWalletProvider?: boolean;
337
+ requestsPerSecond?: number;
338
+ }): ConnectionStore<WalletProviderType, 'WalletConnected'>;
339
+
340
+ // WalletConnected target with default Ethereum connector - walletHost optional
341
+ export function createConnection(settings: {
342
+ targetStep: 'WalletConnected';
343
+ walletHost?: string;
344
+ chainInfo: ChainInfo<UnderlyingEthereumProvider>;
345
+ walletConnector?: undefined;
346
+ autoConnect?: boolean;
347
+ alwaysUseCurrentAccount?: boolean;
348
+ prioritizeWalletProvider?: boolean;
349
+ requestsPerSecond?: number;
350
+ }): ConnectionStore<UnderlyingEthereumProvider, 'WalletConnected', true>;
351
+
352
+ // SignedIn target with walletOnly: true (custom wallet connector) - walletHost optional
353
+ export function createConnection<WalletProviderType>(settings: {
354
+ targetStep?: 'SignedIn';
355
+ walletOnly: true;
356
+ walletHost?: string;
357
+ chainInfo: ChainInfo<WalletProviderType>;
246
358
  walletConnector: WalletConnector<WalletProviderType>;
359
+ signingOrigin?: string;
360
+ autoConnect?: boolean;
247
361
  requestSignatureAutomaticallyIfPossible?: boolean;
248
362
  alwaysUseCurrentAccount?: boolean;
249
- chainInfo: ChainInfo<WalletProviderType>;
250
363
  prioritizeWalletProvider?: boolean;
251
364
  requestsPerSecond?: number;
252
- }): ConnectionStore<WalletProviderType>;
365
+ }): ConnectionStore<WalletProviderType, 'SignedIn', true>;
253
366
 
367
+ // SignedIn target with walletOnly: true (default Ethereum connector) - walletHost optional
254
368
  export function createConnection(settings: {
369
+ targetStep?: 'SignedIn';
370
+ walletOnly: true;
371
+ walletHost?: string;
372
+ chainInfo: ChainInfo<UnderlyingEthereumProvider>;
373
+ walletConnector?: undefined;
255
374
  signingOrigin?: string;
375
+ autoConnect?: boolean;
376
+ requestSignatureAutomaticallyIfPossible?: boolean;
377
+ alwaysUseCurrentAccount?: boolean;
378
+ prioritizeWalletProvider?: boolean;
379
+ requestsPerSecond?: number;
380
+ }): ConnectionStore<UnderlyingEthereumProvider, 'SignedIn', true>;
381
+
382
+ // SignedIn target (explicit) with custom wallet connector - walletHost required
383
+ export function createConnection<WalletProviderType>(settings: {
384
+ targetStep?: 'SignedIn';
385
+ walletOnly?: false;
256
386
  walletHost: string;
387
+ chainInfo: ChainInfo<WalletProviderType>;
388
+ walletConnector: WalletConnector<WalletProviderType>;
389
+ signingOrigin?: string;
257
390
  autoConnect?: boolean;
258
- autoConnectWallet?: boolean;
259
- walletConnector?: undefined;
260
391
  requestSignatureAutomaticallyIfPossible?: boolean;
261
392
  alwaysUseCurrentAccount?: boolean;
393
+ prioritizeWalletProvider?: boolean;
394
+ requestsPerSecond?: number;
395
+ }): ConnectionStore<WalletProviderType, 'SignedIn', false>;
396
+
397
+ // SignedIn target (default) with default Ethereum connector - walletHost required
398
+ export function createConnection(settings: {
399
+ targetStep?: 'SignedIn';
400
+ walletOnly?: false;
401
+ walletHost: string;
262
402
  chainInfo: ChainInfo<UnderlyingEthereumProvider>;
403
+ walletConnector?: undefined;
404
+ signingOrigin?: string;
405
+ autoConnect?: boolean;
406
+ requestSignatureAutomaticallyIfPossible?: boolean;
407
+ alwaysUseCurrentAccount?: boolean;
263
408
  prioritizeWalletProvider?: boolean;
264
409
  requestsPerSecond?: number;
265
- }): ConnectionStore<UnderlyingEthereumProvider>;
410
+ }): ConnectionStore<UnderlyingEthereumProvider, 'SignedIn', false>;
266
411
 
412
+ // Implementation signature
267
413
  export function createConnection<WalletProviderType = UnderlyingEthereumProvider>(settings: {
414
+ targetStep?: TargetStep;
415
+ walletOnly?: boolean;
268
416
  signingOrigin?: string;
269
- walletHost: string;
417
+ walletHost?: string;
270
418
  autoConnect?: boolean;
271
- autoConnectWallet?: boolean;
272
419
  walletConnector?: WalletConnector<WalletProviderType>;
273
420
  requestSignatureAutomaticallyIfPossible?: boolean;
274
421
  alwaysUseCurrentAccount?: boolean;
275
422
  chainInfo: ChainInfo<WalletProviderType>;
276
423
  prioritizeWalletProvider?: boolean;
277
424
  requestsPerSecond?: number;
278
- }) {
425
+ }): ConnectionStore<WalletProviderType, TargetStep, boolean> {
279
426
  function originToSignWith() {
280
427
  return settings.signingOrigin || origin;
281
428
  }
@@ -290,15 +437,18 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
290
437
  prioritizeWalletProvider: settings.prioritizeWalletProvider,
291
438
  requestsPerSecond: settings.requestsPerSecond,
292
439
  });
440
+ // Determine target step (defaults to 'SignedIn')
441
+ const targetStep: TargetStep = settings.targetStep || 'SignedIn';
442
+
293
443
  let autoConnect = true;
294
444
  if (typeof settings.autoConnect !== 'undefined') {
295
445
  autoConnect = settings.autoConnect;
296
446
  }
297
- let autoConnectWallet = true;
298
- if (typeof settings.autoConnectWallet !== 'undefined') {
299
- autoConnectWallet = settings.autoConnectWallet;
300
- }
301
- const requestSignatureAutomaticallyIfPossible = settings.requestSignatureAutomaticallyIfPossible || false;
447
+
448
+ // For SignedIn target, we can auto-request signature if configured
449
+ // For WalletConnected target, this is always false (we never auto-request signature)
450
+ const requestSignatureAutomaticallyIfPossible =
451
+ targetStep === 'SignedIn' ? settings.requestSignatureAutomaticallyIfPossible || false : false;
302
452
 
303
453
  let $connection: Connection<WalletProviderType> = {step: 'Idle', loading: true, wallet: undefined, wallets: []};
304
454
  const _store = writable<Connection<WalletProviderType>>($connection);
@@ -351,13 +501,18 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
351
501
  });
352
502
  }
353
503
 
504
+ // Auto-connect logic based on targetStep
505
+ // When targetStep: 'WalletConnected' - only check lastWallet
506
+ // When targetStep: 'SignedIn' - check originAccount first, then fallback to lastWallet
507
+ let autoConnectHandled = false;
354
508
  if (autoConnect) {
355
509
  if (typeof window !== 'undefined') {
356
- // set({step: 'Idle', loading: true, wallets: $connection.wallets});
357
510
  try {
358
- const existingAccount = getOriginAccount();
359
- if (existingAccount) {
360
- if (existingAccount.signer) {
511
+ // For SignedIn target, check for existing account first
512
+ if (targetStep === 'SignedIn') {
513
+ const existingAccount = getOriginAccount();
514
+ if (existingAccount && existingAccount.signer) {
515
+ autoConnectHandled = true;
361
516
  const mechanismUsed = existingAccount.mechanismUsed as
362
517
  | AlchemyMechanism
363
518
  | WalletMechanism<string, `0x${string}`>;
@@ -369,15 +524,11 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
369
524
  const chainIdAsHex = await withTimeout(walletProvider.getChainId());
370
525
  const chainId = Number(chainIdAsHex).toString();
371
526
  _wallet = {provider: walletProvider, chainId};
372
- // TODO
373
527
  alwaysOnProviderWrapper.setWalletProvider(walletProvider.underlyingProvider);
374
528
  watchForChainIdChange(_wallet.provider);
375
529
  let accounts: `0x${string}`[] = [];
376
- // try {
377
530
  accounts = await withTimeout(walletProvider.getAccounts());
378
531
  accounts = accounts.map((v) => v.toLowerCase() as `0x${string}`);
379
- // } catch {}
380
- // // TODO try catch ? and use logic of onAccountChanged
381
532
  set({
382
533
  step: 'SignedIn',
383
534
  account: existingAccount,
@@ -394,7 +545,6 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
394
545
  },
395
546
  });
396
547
  alwaysOnProviderWrapper.setWalletStatus('connected');
397
- // TODO use the same logic before hand
398
548
  onAccountChanged(accounts);
399
549
  watchForAccountChange(walletProvider);
400
550
  })
@@ -410,54 +560,46 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
410
560
  wallet: undefined,
411
561
  });
412
562
  }
413
- } else {
414
- set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
415
563
  }
416
- } else {
417
- if (autoConnectWallet) {
418
- const lastWallet = getLastWallet();
419
- if (lastWallet) {
420
- waitForWallet(lastWallet.name)
421
- .then(async (walletDetails: WalletHandle<WalletProviderType>) => {
422
- const walletProvider = walletDetails.walletProvider;
423
- const chainIdAsHex = await withTimeout(walletProvider.getChainId());
424
- const chainId = Number(chainIdAsHex).toString();
425
- _wallet = {provider: walletProvider, chainId};
426
- // TODO
427
- alwaysOnProviderWrapper.setWalletProvider(walletProvider.underlyingProvider);
428
- watchForChainIdChange(_wallet.provider);
564
+ }
429
565
 
430
- let accounts: `0x${string}`[] = [];
431
- // try {
432
- accounts = await withTimeout(walletProvider.getAccounts());
433
- accounts = accounts.map((v) => v.toLowerCase() as `0x${string}`);
434
- // } catch {}
435
- // // TODO try catch ? and use logic of onAccountChanged
436
- set({
437
- step: 'WalletConnected',
438
- mechanism: lastWallet,
439
- wallets: $connection.wallets,
440
- wallet: {
441
- provider: walletProvider,
442
- accounts,
443
- status: 'connected',
444
- accountChanged: undefined,
445
- chainId,
446
- invalidChainId: alwaysOnChainId != chainId,
447
- switchingChain: false,
448
- },
449
- });
450
- alwaysOnProviderWrapper.setWalletStatus('connected');
451
- // TODO use the same logic before hand
452
- onAccountChanged(accounts);
453
- watchForAccountChange(walletProvider);
454
- })
455
- .catch((err) => {
456
- set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
566
+ // For both targets, fallback to lastWallet if no account found (or WalletConnected target)
567
+ if (!autoConnectHandled) {
568
+ const lastWallet = getLastWallet();
569
+ if (lastWallet) {
570
+ waitForWallet(lastWallet.name)
571
+ .then(async (walletDetails: WalletHandle<WalletProviderType>) => {
572
+ const walletProvider = walletDetails.walletProvider;
573
+ const chainIdAsHex = await withTimeout(walletProvider.getChainId());
574
+ const chainId = Number(chainIdAsHex).toString();
575
+ _wallet = {provider: walletProvider, chainId};
576
+ alwaysOnProviderWrapper.setWalletProvider(walletProvider.underlyingProvider);
577
+ watchForChainIdChange(_wallet.provider);
578
+
579
+ let accounts: `0x${string}`[] = [];
580
+ accounts = await withTimeout(walletProvider.getAccounts());
581
+ accounts = accounts.map((v) => v.toLowerCase() as `0x${string}`);
582
+ set({
583
+ step: 'WalletConnected',
584
+ mechanism: lastWallet,
585
+ wallets: $connection.wallets,
586
+ wallet: {
587
+ provider: walletProvider,
588
+ accounts,
589
+ status: 'connected',
590
+ accountChanged: undefined,
591
+ chainId,
592
+ invalidChainId: alwaysOnChainId != chainId,
593
+ switchingChain: false,
594
+ },
457
595
  });
458
- } else {
459
- set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
460
- }
596
+ alwaysOnProviderWrapper.setWalletStatus('connected');
597
+ onAccountChanged(accounts);
598
+ watchForAccountChange(walletProvider);
599
+ })
600
+ .catch((err) => {
601
+ set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
602
+ });
461
603
  } else {
462
604
  set({step: 'Idle', loading: false, wallet: undefined, wallets: $connection.wallets});
463
605
  }
@@ -984,6 +1126,10 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
984
1126
  });
985
1127
  }
986
1128
  } else {
1129
+ // Popup-based auth requires walletHost
1130
+ if (!settings.walletHost) {
1131
+ throw new Error('walletHost is required for popup-based authentication (email, oauth, mnemonic)');
1132
+ }
987
1133
  popup = connectViaPopup({
988
1134
  mechanism,
989
1135
  walletHost: settings.walletHost,
@@ -1035,6 +1181,10 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
1035
1181
  }
1036
1182
  }
1037
1183
 
1184
+ // ensureConnected overloads - the default step depends on targetStep
1185
+ function ensureConnected(
1186
+ options?: ConnectionOptions,
1187
+ ): Promise<WalletConnected<WalletProviderType> | SignedIn<WalletProviderType>>;
1038
1188
  function ensureConnected(
1039
1189
  step: 'WalletConnected',
1040
1190
  mechanism?: WalletMechanism<string | undefined, `0x${string}` | undefined>,
@@ -1045,51 +1195,70 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
1045
1195
  mechanism?: Mechanism,
1046
1196
  options?: ConnectionOptions,
1047
1197
  ): Promise<SignedIn<WalletProviderType>>;
1048
- function ensureConnected(mechanism?: Mechanism, options?: ConnectionOptions): Promise<SignedIn<WalletProviderType>>;
1049
- async function ensureConnected<Step extends 'WalletConnected' | 'SignedIn' = 'SignedIn'>(
1050
- stepOrMechanism?: Step | Mechanism,
1198
+ async function ensureConnected<Step extends 'WalletConnected' | 'SignedIn'>(
1199
+ stepOrMechanismOrOptions?: Step | Mechanism | ConnectionOptions,
1051
1200
  mechanismOrOptions?: Mechanism | ConnectionOptions,
1052
1201
  options?: ConnectionOptions,
1053
- ) {
1054
- const step = typeof stepOrMechanism === 'string' ? stepOrMechanism : 'SignedIn';
1055
- let mechanism = typeof stepOrMechanism === 'string' ? (mechanismOrOptions as Mechanism) : stepOrMechanism;
1202
+ ): Promise<WalletConnected<WalletProviderType> | SignedIn<WalletProviderType>> {
1203
+ // Determine if first arg is a step string, mechanism, or options
1204
+ let step: 'WalletConnected' | 'SignedIn';
1205
+ let mechanism: Mechanism | undefined;
1206
+ let opts: ConnectionOptions | undefined;
1207
+
1208
+ if (typeof stepOrMechanismOrOptions === 'string') {
1209
+ // First arg is a step
1210
+ step = stepOrMechanismOrOptions as 'WalletConnected' | 'SignedIn';
1211
+ mechanism = mechanismOrOptions as Mechanism | undefined;
1212
+ opts = options;
1213
+ } else if (stepOrMechanismOrOptions && 'type' in (stepOrMechanismOrOptions as any)) {
1214
+ // First arg is a mechanism
1215
+ step = targetStep; // Use configured target as default
1216
+ mechanism = stepOrMechanismOrOptions as Mechanism;
1217
+ opts = mechanismOrOptions as ConnectionOptions | undefined;
1218
+ } else {
1219
+ // First arg is options or undefined
1220
+ step = targetStep; // Use configured target as default
1221
+ mechanism = undefined;
1222
+ opts = stepOrMechanismOrOptions as ConnectionOptions | undefined;
1223
+ }
1224
+
1225
+ // For WalletConnected step, default to wallet mechanism
1056
1226
  if (!mechanism && step === 'WalletConnected') {
1057
1227
  mechanism = {type: 'wallet'};
1058
1228
  }
1059
- options = typeof stepOrMechanism === 'string' ? options : (mechanismOrOptions as ConnectionOptions);
1060
- const promise = new Promise<
1061
- Step extends 'WalletConnected' ? WalletConnected<WalletProviderType> : SignedIn<WalletProviderType>
1062
- >((resolve, reject) => {
1063
- let forceConnect = false;
1064
- if (
1065
- $connection.step == 'WalletConnected' &&
1066
- ($connection.wallet.status == 'locked' || $connection.wallet.status === 'disconnected')
1067
- ) {
1068
- // console.log(`locked / disconnected : we assume it needs reconnection`);
1069
- forceConnect = true;
1070
- mechanism = $connection.mechanism; // we reuse existing mechanism as we just want to reconnect
1071
- } else if ($connection.step == step) {
1072
- resolve($connection as any);
1073
- return;
1074
- }
1075
- let idlePassed = $connection.step != 'Idle';
1076
- if (!idlePassed || forceConnect) {
1077
- connect(mechanism, options);
1078
- }
1079
- const unsubscribe = _store.subscribe((connection) => {
1080
- if (connection.step === 'Idle' && idlePassed) {
1081
- unsubscribe();
1082
- reject();
1083
- }
1084
- if (!idlePassed && connection.step !== 'Idle') {
1085
- idlePassed = true;
1229
+
1230
+ const promise = new Promise<WalletConnected<WalletProviderType> | SignedIn<WalletProviderType>>(
1231
+ (resolve, reject) => {
1232
+ let forceConnect = false;
1233
+ if (
1234
+ $connection.step == 'WalletConnected' &&
1235
+ ($connection.wallet.status == 'locked' || $connection.wallet.status === 'disconnected')
1236
+ ) {
1237
+ forceConnect = true;
1238
+ mechanism = $connection.mechanism; // we reuse existing mechanism as we just want to reconnect
1239
+ } else if ($connection.step == step) {
1240
+ resolve($connection as any);
1241
+ return;
1086
1242
  }
1087
- if (connection.step === step) {
1088
- unsubscribe();
1089
- resolve(connection as any);
1243
+ let idlePassed = $connection.step != 'Idle';
1244
+ if (!idlePassed || forceConnect) {
1245
+ connect(mechanism, opts);
1090
1246
  }
1091
- });
1092
- });
1247
+ const unsubscribe = _store.subscribe((connection) => {
1248
+ if (connection.step === 'Idle' && idlePassed) {
1249
+ unsubscribe();
1250
+ reject();
1251
+ }
1252
+ if (!idlePassed && connection.step !== 'Idle') {
1253
+ idlePassed = true;
1254
+ }
1255
+ if (connection.step === step) {
1256
+ unsubscribe();
1257
+ resolve(connection as any);
1258
+ }
1259
+ });
1260
+ },
1261
+ );
1093
1262
 
1094
1263
  return promise;
1095
1264
  }
@@ -1393,7 +1562,27 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
1393
1562
  }
1394
1563
  }
1395
1564
 
1396
- return {
1565
+ // Determine walletOnly (defaults to false, but true implies WalletConnected target behavior for mechanism)
1566
+ const walletOnly = settings.walletOnly || targetStep === 'WalletConnected';
1567
+
1568
+ // Method on the store to check if target step is reached
1569
+ function storeIsTargetStepReached(connection: Connection<WalletProviderType>): boolean {
1570
+ if (targetStep === 'WalletConnected') {
1571
+ // For WalletConnected target, accept WalletConnected OR SignedIn-with-wallet
1572
+ return (
1573
+ connection.step === 'WalletConnected' || (connection.step === 'SignedIn' && connection.wallet !== undefined)
1574
+ );
1575
+ }
1576
+ // For SignedIn target
1577
+ if (walletOnly) {
1578
+ // With walletOnly, only accept SignedIn-with-wallet
1579
+ return connection.step === 'SignedIn' && connection.wallet !== undefined;
1580
+ }
1581
+ // Accept any SignedIn variant
1582
+ return connection.step === 'SignedIn';
1583
+ }
1584
+
1585
+ const store = {
1397
1586
  subscribe: _store.subscribe,
1398
1587
  connect,
1399
1588
  cancel,
@@ -1404,9 +1593,14 @@ export function createConnection<WalletProviderType = UnderlyingEthereumProvider
1404
1593
  getSignatureForPublicKeyPublication,
1405
1594
  switchWalletChain,
1406
1595
  unlock,
1407
- ensureConnected,
1596
+ ensureConnected: ensureConnected as any, // Cast to bypass complex conditional typing
1597
+ isTargetStepReached: storeIsTargetStepReached as any, // Cast for type guard
1598
+ targetStep,
1599
+ walletOnly,
1408
1600
  provider: alwaysOnProviderWrapper.provider,
1409
1601
  chainId: '' + settings.chainInfo.id,
1410
1602
  chainInfo: settings.chainInfo,
1411
1603
  };
1604
+
1605
+ return store as ConnectionStore<WalletProviderType, TargetStep, boolean>;
1412
1606
  }