@abstraxn/signer-react 3.3.8 → 3.3.9
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/CHANGELOG.md +13 -0
- package/dist/src/hooks.d.ts +40 -40
- package/dist/src/hooks.js +115 -55
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +1 -0
- package/dist/src/walletConnectMobile.d.ts +52 -4
- package/dist/src/walletConnectMobile.js +286 -49
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,19 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.3.9] - 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
- **Mobile WalletConnect `writeContract` loader stuck after MetaMask confirm** – On iOS/Android the browser often drops the WalletConnect relay while MetaMask is open, so `eth_sendTransaction` never returns even though the tx is on-chain. `useWriteContract` now races the wallet call against an RPC scan for the matching outgoing contract tx (`from` / `to` / function selector) and clears pending WC mobile flags when the hash is recovered that way.
|
|
12
|
+
- **Stacked “Open in MetaMask?” / “All Wallets” after an already-connected session** – Auto-deeplink and WalletConnect modal restore are skipped when a WC session already exists; only the connect flow may schedule a single wallet open. Connected sessions hide the picker instead of reopening it.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- **Chain-poll recovery is gated** – The on-chain hash poll runs only for WalletConnect when the device is mobile or the document is `hidden`. Desktop injected wallets and idle desktop WC keep the plain `writeContract` path (no always-on poll).
|
|
16
|
+
- **`gasLimit` on `useWriteContract`** – Mapped to viem’s `gas` option on the direct `walletClient.writeContract` path so callers that pass `gasLimit` are honored.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- Public helpers: `pollForOutgoingTx`, `clearWalletConnectPendingState`, `waitUntilDocumentVisible`, `isWalletConnectConnector`, `isMobileDevice`.
|
|
20
|
+
|
|
8
21
|
## [3.3.8] - 2026-09-16
|
|
9
22
|
|
|
10
23
|
### Fixed
|
package/dist/src/hooks.d.ts
CHANGED
|
@@ -360,15 +360,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
360
360
|
transactions: includeTransactions extends true ? ({
|
|
361
361
|
input: import("viem").Hex;
|
|
362
362
|
s: import("viem").Hex;
|
|
363
|
-
|
|
363
|
+
from: Address;
|
|
364
|
+
to: Address | null;
|
|
364
365
|
value: bigint;
|
|
366
|
+
hash: import("viem").Hash;
|
|
367
|
+
type: "legacy";
|
|
365
368
|
yParity?: undefined | undefined;
|
|
366
|
-
from: Address;
|
|
367
369
|
gas: bigint;
|
|
368
|
-
hash: import("viem").Hash;
|
|
369
370
|
nonce: number;
|
|
370
371
|
r: import("viem").Hex;
|
|
371
|
-
to: Address | null;
|
|
372
372
|
typeHex: import("viem").Hex | null;
|
|
373
373
|
v: bigint;
|
|
374
374
|
accessList?: undefined | undefined;
|
|
@@ -385,15 +385,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
385
385
|
} | {
|
|
386
386
|
input: import("viem").Hex;
|
|
387
387
|
s: import("viem").Hex;
|
|
388
|
-
|
|
388
|
+
from: Address;
|
|
389
|
+
to: Address | null;
|
|
389
390
|
value: bigint;
|
|
391
|
+
hash: import("viem").Hash;
|
|
392
|
+
type: "eip2930";
|
|
390
393
|
yParity: number;
|
|
391
|
-
from: Address;
|
|
392
394
|
gas: bigint;
|
|
393
|
-
hash: import("viem").Hash;
|
|
394
395
|
nonce: number;
|
|
395
396
|
r: import("viem").Hex;
|
|
396
|
-
to: Address | null;
|
|
397
397
|
typeHex: import("viem").Hex | null;
|
|
398
398
|
v: bigint;
|
|
399
399
|
accessList: import("viem").AccessList;
|
|
@@ -410,15 +410,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
410
410
|
} | {
|
|
411
411
|
input: import("viem").Hex;
|
|
412
412
|
s: import("viem").Hex;
|
|
413
|
-
|
|
413
|
+
from: Address;
|
|
414
|
+
to: Address | null;
|
|
414
415
|
value: bigint;
|
|
416
|
+
hash: import("viem").Hash;
|
|
417
|
+
type: "eip1559";
|
|
415
418
|
yParity: number;
|
|
416
|
-
from: Address;
|
|
417
419
|
gas: bigint;
|
|
418
|
-
hash: import("viem").Hash;
|
|
419
420
|
nonce: number;
|
|
420
421
|
r: import("viem").Hex;
|
|
421
|
-
to: Address | null;
|
|
422
422
|
typeHex: import("viem").Hex | null;
|
|
423
423
|
v: bigint;
|
|
424
424
|
accessList: import("viem").AccessList;
|
|
@@ -435,15 +435,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
435
435
|
} | {
|
|
436
436
|
input: import("viem").Hex;
|
|
437
437
|
s: import("viem").Hex;
|
|
438
|
-
|
|
438
|
+
from: Address;
|
|
439
|
+
to: Address | null;
|
|
439
440
|
value: bigint;
|
|
441
|
+
hash: import("viem").Hash;
|
|
442
|
+
type: "eip4844";
|
|
440
443
|
yParity: number;
|
|
441
|
-
from: Address;
|
|
442
444
|
gas: bigint;
|
|
443
|
-
hash: import("viem").Hash;
|
|
444
445
|
nonce: number;
|
|
445
446
|
r: import("viem").Hex;
|
|
446
|
-
to: Address | null;
|
|
447
447
|
typeHex: import("viem").Hex | null;
|
|
448
448
|
v: bigint;
|
|
449
449
|
accessList: import("viem").AccessList;
|
|
@@ -460,15 +460,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
460
460
|
} | {
|
|
461
461
|
input: import("viem").Hex;
|
|
462
462
|
s: import("viem").Hex;
|
|
463
|
-
|
|
463
|
+
from: Address;
|
|
464
|
+
to: Address | null;
|
|
464
465
|
value: bigint;
|
|
466
|
+
hash: import("viem").Hash;
|
|
467
|
+
type: "eip7702";
|
|
465
468
|
yParity: number;
|
|
466
|
-
from: Address;
|
|
467
469
|
gas: bigint;
|
|
468
|
-
hash: import("viem").Hash;
|
|
469
470
|
nonce: number;
|
|
470
471
|
r: import("viem").Hex;
|
|
471
|
-
to: Address | null;
|
|
472
472
|
typeHex: import("viem").Hex | null;
|
|
473
473
|
v: bigint;
|
|
474
474
|
accessList: import("viem").AccessList;
|
|
@@ -510,15 +510,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
510
510
|
getTransaction: <blockTag extends import("viem").BlockTag = "latest">(args: import("viem").GetTransactionParameters<blockTag>) => Promise<{
|
|
511
511
|
input: import("viem").Hex;
|
|
512
512
|
s: import("viem").Hex;
|
|
513
|
-
|
|
513
|
+
from: Address;
|
|
514
|
+
to: Address | null;
|
|
514
515
|
value: bigint;
|
|
516
|
+
hash: import("viem").Hash;
|
|
517
|
+
type: "legacy";
|
|
515
518
|
yParity?: undefined | undefined;
|
|
516
|
-
from: Address;
|
|
517
519
|
gas: bigint;
|
|
518
|
-
hash: import("viem").Hash;
|
|
519
520
|
nonce: number;
|
|
520
521
|
r: import("viem").Hex;
|
|
521
|
-
to: Address | null;
|
|
522
522
|
typeHex: import("viem").Hex | null;
|
|
523
523
|
v: bigint;
|
|
524
524
|
accessList?: undefined | undefined;
|
|
@@ -535,15 +535,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
535
535
|
} | {
|
|
536
536
|
input: import("viem").Hex;
|
|
537
537
|
s: import("viem").Hex;
|
|
538
|
-
|
|
538
|
+
from: Address;
|
|
539
|
+
to: Address | null;
|
|
539
540
|
value: bigint;
|
|
541
|
+
hash: import("viem").Hash;
|
|
542
|
+
type: "eip2930";
|
|
540
543
|
yParity: number;
|
|
541
|
-
from: Address;
|
|
542
544
|
gas: bigint;
|
|
543
|
-
hash: import("viem").Hash;
|
|
544
545
|
nonce: number;
|
|
545
546
|
r: import("viem").Hex;
|
|
546
|
-
to: Address | null;
|
|
547
547
|
typeHex: import("viem").Hex | null;
|
|
548
548
|
v: bigint;
|
|
549
549
|
accessList: import("viem").AccessList;
|
|
@@ -560,15 +560,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
560
560
|
} | {
|
|
561
561
|
input: import("viem").Hex;
|
|
562
562
|
s: import("viem").Hex;
|
|
563
|
-
|
|
563
|
+
from: Address;
|
|
564
|
+
to: Address | null;
|
|
564
565
|
value: bigint;
|
|
566
|
+
hash: import("viem").Hash;
|
|
567
|
+
type: "eip1559";
|
|
565
568
|
yParity: number;
|
|
566
|
-
from: Address;
|
|
567
569
|
gas: bigint;
|
|
568
|
-
hash: import("viem").Hash;
|
|
569
570
|
nonce: number;
|
|
570
571
|
r: import("viem").Hex;
|
|
571
|
-
to: Address | null;
|
|
572
572
|
typeHex: import("viem").Hex | null;
|
|
573
573
|
v: bigint;
|
|
574
574
|
accessList: import("viem").AccessList;
|
|
@@ -585,15 +585,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
585
585
|
} | {
|
|
586
586
|
input: import("viem").Hex;
|
|
587
587
|
s: import("viem").Hex;
|
|
588
|
-
|
|
588
|
+
from: Address;
|
|
589
|
+
to: Address | null;
|
|
589
590
|
value: bigint;
|
|
591
|
+
hash: import("viem").Hash;
|
|
592
|
+
type: "eip4844";
|
|
590
593
|
yParity: number;
|
|
591
|
-
from: Address;
|
|
592
594
|
gas: bigint;
|
|
593
|
-
hash: import("viem").Hash;
|
|
594
595
|
nonce: number;
|
|
595
596
|
r: import("viem").Hex;
|
|
596
|
-
to: Address | null;
|
|
597
597
|
typeHex: import("viem").Hex | null;
|
|
598
598
|
v: bigint;
|
|
599
599
|
accessList: import("viem").AccessList;
|
|
@@ -610,15 +610,15 @@ export declare function usePublicClient(chain: Chain, rpcUrl: string): {
|
|
|
610
610
|
} | {
|
|
611
611
|
input: import("viem").Hex;
|
|
612
612
|
s: import("viem").Hex;
|
|
613
|
-
|
|
613
|
+
from: Address;
|
|
614
|
+
to: Address | null;
|
|
614
615
|
value: bigint;
|
|
616
|
+
hash: import("viem").Hash;
|
|
617
|
+
type: "eip7702";
|
|
615
618
|
yParity: number;
|
|
616
|
-
from: Address;
|
|
617
619
|
gas: bigint;
|
|
618
|
-
hash: import("viem").Hash;
|
|
619
620
|
nonce: number;
|
|
620
621
|
r: import("viem").Hex;
|
|
621
|
-
to: Address | null;
|
|
622
622
|
typeHex: import("viem").Hex | null;
|
|
623
623
|
v: bigint;
|
|
624
624
|
accessList: import("viem").AccessList;
|
package/dist/src/hooks.js
CHANGED
|
@@ -9,10 +9,10 @@ import { toAccount } from 'viem/accounts';
|
|
|
9
9
|
import { Connection as SolanaConnection, Transaction as SolanaTransaction, SystemProgram, PublicKey, LAMPORTS_PER_SOL, TransactionInstruction, } from '@solana/web3.js';
|
|
10
10
|
import { SolanaRelayer } from '@abstraxn/solana-relayer';
|
|
11
11
|
import { useWalletClient as useWagmiWalletClient, useAccount, useConfig, useChainId as useWagmiChainId, useSwitchChain as useWagmiSwitchChain, useSignMessage as useWagmiSignMessage, useReconnect as useWagmiReconnect } from 'wagmi';
|
|
12
|
-
import { getWalletClient, switchChain } from '@wagmi/core';
|
|
12
|
+
import { getWalletClient, switchChain, getPublicClient } from '@wagmi/core';
|
|
13
13
|
import { getConnectorMeta } from './connectors';
|
|
14
14
|
import { getChainById } from './chains';
|
|
15
|
-
import { waitUntilDocumentVisible } from './walletConnectMobile';
|
|
15
|
+
import { waitUntilDocumentVisible, pollForOutgoingTx, clearWalletConnectPendingState, isWalletConnectConnector, isMobileDevice, } from './walletConnectMobile';
|
|
16
16
|
/**
|
|
17
17
|
* Hook to check if wallet is connected
|
|
18
18
|
*/
|
|
@@ -2430,69 +2430,129 @@ export function useWriteContract() {
|
|
|
2430
2430
|
}
|
|
2431
2431
|
// Cast to any to avoid type issues with different viem versions
|
|
2432
2432
|
const walletClientAny = activeClient;
|
|
2433
|
-
|
|
2434
|
-
|
|
2435
|
-
|
|
2433
|
+
const fromAddress = account ||
|
|
2434
|
+
walletClientAny?.account?.address;
|
|
2435
|
+
const resolvedGas = gas !== undefined ? gas : gasLimit !== undefined ? gasLimit : undefined;
|
|
2436
|
+
const sendWrite = async () => {
|
|
2437
|
+
// Try using writeContract method directly if available
|
|
2438
|
+
if (walletClientAny && typeof walletClientAny.writeContract === 'function') {
|
|
2439
|
+
return walletClientAny.writeContract({
|
|
2440
|
+
address,
|
|
2441
|
+
abi: abi,
|
|
2442
|
+
functionName: functionName,
|
|
2443
|
+
args: args,
|
|
2444
|
+
value,
|
|
2445
|
+
account,
|
|
2446
|
+
gas: resolvedGas,
|
|
2447
|
+
gasPrice,
|
|
2448
|
+
maxFeePerGas,
|
|
2449
|
+
maxPriorityFeePerGas,
|
|
2450
|
+
nonce,
|
|
2451
|
+
chain: null, // Let the client determine the chain (or use current)
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
// If writeContract is not available, use getContract to create a contract instance
|
|
2455
|
+
// This works with wagmi's connectorClient
|
|
2456
|
+
const contract = getContract({
|
|
2436
2457
|
address,
|
|
2458
|
+
abi: abi,
|
|
2459
|
+
client: walletClientAny,
|
|
2460
|
+
});
|
|
2461
|
+
// Get the write function from the contract
|
|
2462
|
+
const writeFunction = contract.write?.[functionName];
|
|
2463
|
+
if (!writeFunction || typeof writeFunction !== 'function') {
|
|
2464
|
+
throw new Error(`Function "${functionName}" not found in contract ABI or is not a write function. ` +
|
|
2465
|
+
'Make sure the function exists in the ABI and is a state-changing function.');
|
|
2466
|
+
}
|
|
2467
|
+
// Prepare the call options (viem contract write methods accept options as the last parameter)
|
|
2468
|
+
const hasOptions = value !== undefined || account !== undefined || resolvedGas !== undefined ||
|
|
2469
|
+
gasPrice !== undefined || maxFeePerGas !== undefined ||
|
|
2470
|
+
maxPriorityFeePerGas !== undefined || nonce !== undefined;
|
|
2471
|
+
// Viem contract write methods expect args as an array, not spread
|
|
2472
|
+
const functionArgs = args || [];
|
|
2473
|
+
if (hasOptions) {
|
|
2474
|
+
const callOptions = {};
|
|
2475
|
+
if (value !== undefined)
|
|
2476
|
+
callOptions.value = value;
|
|
2477
|
+
if (account !== undefined)
|
|
2478
|
+
callOptions.account = account;
|
|
2479
|
+
if (resolvedGas !== undefined)
|
|
2480
|
+
callOptions.gas = resolvedGas;
|
|
2481
|
+
if (gasPrice !== undefined)
|
|
2482
|
+
callOptions.gasPrice = gasPrice;
|
|
2483
|
+
if (maxFeePerGas !== undefined)
|
|
2484
|
+
callOptions.maxFeePerGas = maxFeePerGas;
|
|
2485
|
+
if (maxPriorityFeePerGas !== undefined)
|
|
2486
|
+
callOptions.maxPriorityFeePerGas = maxPriorityFeePerGas;
|
|
2487
|
+
if (nonce !== undefined)
|
|
2488
|
+
callOptions.nonce = nonce;
|
|
2489
|
+
return writeFunction(functionArgs, callOptions);
|
|
2490
|
+
}
|
|
2491
|
+
return writeFunction(functionArgs);
|
|
2492
|
+
};
|
|
2493
|
+
// Mobile WalletConnect: MetaMask confirms on-chain but eth_sendTransaction
|
|
2494
|
+
// never resolves if the relay WS died while the browser was backgrounded.
|
|
2495
|
+
// Only race an RPC scan in that scenario — not for desktop / injected wallets.
|
|
2496
|
+
const publicClient = getPublicClient(config, {
|
|
2497
|
+
chainId: currentChainId,
|
|
2498
|
+
});
|
|
2499
|
+
const documentHidden = typeof document !== "undefined" &&
|
|
2500
|
+
document.visibilityState === "hidden";
|
|
2501
|
+
const shouldPollForWcHang = isWalletConnectConnector(connector) &&
|
|
2502
|
+
(isMobileDevice() || documentHidden);
|
|
2503
|
+
if (!shouldPollForWcHang || !publicClient || !fromAddress) {
|
|
2504
|
+
return sendWrite();
|
|
2505
|
+
}
|
|
2506
|
+
let startBlock = 0n;
|
|
2507
|
+
try {
|
|
2508
|
+
startBlock = await publicClient.getBlockNumber();
|
|
2509
|
+
}
|
|
2510
|
+
catch {
|
|
2511
|
+
// ignore — poll will use a recent window
|
|
2512
|
+
}
|
|
2513
|
+
let dataPrefix;
|
|
2514
|
+
try {
|
|
2515
|
+
const data = encodeFunctionData({
|
|
2437
2516
|
abi: abi,
|
|
2438
2517
|
functionName: functionName,
|
|
2439
2518
|
args: args,
|
|
2440
|
-
value,
|
|
2441
|
-
account,
|
|
2442
|
-
gas,
|
|
2443
|
-
gasPrice,
|
|
2444
|
-
maxFeePerGas,
|
|
2445
|
-
maxPriorityFeePerGas,
|
|
2446
|
-
nonce,
|
|
2447
|
-
chain: null, // Let the client determine the chain (or use current)
|
|
2448
2519
|
});
|
|
2520
|
+
dataPrefix = data.slice(0, 10);
|
|
2449
2521
|
}
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
2522
|
+
catch {
|
|
2523
|
+
// Matching without selector is still useful (from + to).
|
|
2524
|
+
}
|
|
2525
|
+
const cancelPoll = { cancelled: false };
|
|
2526
|
+
const sendPromise = sendWrite();
|
|
2527
|
+
const pollPromise = pollForOutgoingTx({
|
|
2528
|
+
publicClient,
|
|
2529
|
+
from: fromAddress,
|
|
2530
|
+
to: address,
|
|
2531
|
+
dataPrefix,
|
|
2532
|
+
value,
|
|
2533
|
+
startBlock,
|
|
2534
|
+
signal: cancelPoll,
|
|
2535
|
+
initialDelayMs: 10_000,
|
|
2536
|
+
timeoutMs: 90_000,
|
|
2456
2537
|
});
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2538
|
+
try {
|
|
2539
|
+
const hash = await Promise.race([sendPromise, pollPromise]);
|
|
2540
|
+
// Poll may win while WC is still hanging — ignore a late reject.
|
|
2541
|
+
void sendPromise.then(() => undefined, () => undefined);
|
|
2542
|
+
return hash;
|
|
2462
2543
|
}
|
|
2463
|
-
|
|
2464
|
-
|
|
2465
|
-
|
|
2466
|
-
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
if (value !== undefined)
|
|
2472
|
-
callOptions.value = value;
|
|
2473
|
-
if (account !== undefined)
|
|
2474
|
-
callOptions.account = account;
|
|
2475
|
-
// `gas` in viem is the gas *limit*. Allow either `gas` or `gasLimit` from the hook caller.
|
|
2476
|
-
if (gas !== undefined) {
|
|
2477
|
-
callOptions.gas = gas;
|
|
2544
|
+
finally {
|
|
2545
|
+
cancelPoll.cancelled = true;
|
|
2546
|
+
try {
|
|
2547
|
+
const getProvider = connector?.getProvider;
|
|
2548
|
+
if (typeof getProvider === 'function') {
|
|
2549
|
+
const provider = await getProvider();
|
|
2550
|
+
clearWalletConnectPendingState(provider);
|
|
2551
|
+
}
|
|
2478
2552
|
}
|
|
2479
|
-
|
|
2480
|
-
|
|
2553
|
+
catch {
|
|
2554
|
+
// ignore
|
|
2481
2555
|
}
|
|
2482
|
-
if (gasPrice !== undefined)
|
|
2483
|
-
callOptions.gasPrice = gasPrice;
|
|
2484
|
-
if (maxFeePerGas !== undefined)
|
|
2485
|
-
callOptions.maxFeePerGas = maxFeePerGas;
|
|
2486
|
-
if (maxPriorityFeePerGas !== undefined)
|
|
2487
|
-
callOptions.maxPriorityFeePerGas = maxPriorityFeePerGas;
|
|
2488
|
-
if (nonce !== undefined)
|
|
2489
|
-
callOptions.nonce = nonce;
|
|
2490
|
-
// Call the write function with args array and options object
|
|
2491
|
-
return writeFunction(functionArgs, callOptions);
|
|
2492
|
-
}
|
|
2493
|
-
else {
|
|
2494
|
-
// Call the write function with args array only
|
|
2495
|
-
return writeFunction(functionArgs);
|
|
2496
2556
|
}
|
|
2497
2557
|
})();
|
|
2498
2558
|
}, [hookClient, isPending, isError, error, isConnected, config, currentChainId, reconnect, connector]);
|
package/dist/src/index.d.ts
CHANGED
|
@@ -16,3 +16,4 @@ export type { OnboardingUIComponentProps, OnboardingUIComponentRef } from './Onb
|
|
|
16
16
|
export { ALL_CHAINS, EVM_CHAINS, SOLANA_CHAINS, getChainById, getChainsByType, getDefaultChains, toCoreChain, type ChainData, } from './chains';
|
|
17
17
|
export { CONNECTORS, getConnectorMeta, isOAuthConnector, isWalletConnector, isAuthConnector, type ConnectorType, type ConnectorMeta, } from './connectors';
|
|
18
18
|
export { createWagmiConfig } from './wagmiConfig';
|
|
19
|
+
export { pollForOutgoingTx, clearWalletConnectPendingState, waitUntilDocumentVisible, isWalletConnectConnector, isMobileDevice, } from './walletConnectMobile';
|
package/dist/src/index.js
CHANGED
|
@@ -11,4 +11,5 @@ export { OnboardingUIComponent as OnboardingUI } from './OnboardingUI';
|
|
|
11
11
|
export { ALL_CHAINS, EVM_CHAINS, SOLANA_CHAINS, getChainById, getChainsByType, getDefaultChains, toCoreChain, } from './chains';
|
|
12
12
|
export { CONNECTORS, getConnectorMeta, isOAuthConnector, isWalletConnector, isAuthConnector, } from './connectors';
|
|
13
13
|
export { createWagmiConfig } from './wagmiConfig';
|
|
14
|
+
export { pollForOutgoingTx, clearWalletConnectPendingState, waitUntilDocumentVisible, isWalletConnectConnector, isMobileDevice, } from './walletConnectMobile';
|
|
14
15
|
//# sourceMappingURL=index.js.map
|
|
@@ -34,12 +34,34 @@ export declare function getDappReturnUrl(): string;
|
|
|
34
34
|
* navigating Safari to a new URL (which reloads and drops in-memory state).
|
|
35
35
|
*/
|
|
36
36
|
export declare function getSourceBrowserNativeRedirect(): string | undefined;
|
|
37
|
+
export declare function isMobileDevice(): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Undo the inline `display: none !important` we apply after connect so the
|
|
40
|
+
* WalletConnect modal can show "Continue in wallet" and process the response.
|
|
41
|
+
* Prefer hideWalletConnectModal() once a session already exists — restoring
|
|
42
|
+
* during a later tx reopens the "All Wallets" picker on top of MetaMask.
|
|
43
|
+
*/
|
|
44
|
+
export declare function restoreWalletConnectModal(): void;
|
|
45
|
+
/** Force-close / hide Reown AppKit + WalletConnect modal DOM after connect or tx. */
|
|
46
|
+
export declare function hideWalletConnectModal(provider?: any): void;
|
|
37
47
|
/**
|
|
38
48
|
* Open the already-connected wallet app so the user sees the confirmation UI.
|
|
39
49
|
* WalletConnect often sends the session_request but does not deeplink on a
|
|
40
50
|
* later tx if the QR modal is hidden — Create Proposal then sits on a loader.
|
|
51
|
+
*
|
|
52
|
+
* When a session already exists, do NOT reopen the WC "All Wallets" modal —
|
|
53
|
+
* that stacks under the iOS "Open in MetaMask?" prompt and stays after confirm.
|
|
54
|
+
*
|
|
55
|
+
* Returns false if skipped (already backgrounded / already opened once).
|
|
56
|
+
*/
|
|
57
|
+
export declare function openConnectedWalletApp(provider: any): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* At most one MetaMask deeplink per approval request.
|
|
60
|
+
* Extra retries queue iOS "Open in MetaMask?" prompts that stay on screen
|
|
61
|
+
* after the tx is already confirmed (JS cannot dismiss that system dialog).
|
|
62
|
+
* Cancel remaining work as soon as the tab is backgrounded (user opened MM).
|
|
41
63
|
*/
|
|
42
|
-
export declare function
|
|
64
|
+
export declare function scheduleOpenConnectedWalletApp(provider: any, delaysMs?: number[]): () => void;
|
|
43
65
|
/**
|
|
44
66
|
* Lazy metadata so url/redirect are read at connect/request time, not at
|
|
45
67
|
* provider mount (which is often the homepage in Next.js layouts).
|
|
@@ -81,10 +103,36 @@ export declare function resumeWalletConnectRelayer(provider: any, options?: {
|
|
|
81
103
|
/** Resolves when the dapp tab is in the foreground again (mobile wallet return). */
|
|
82
104
|
export declare function waitUntilDocumentVisible(): Promise<void>;
|
|
83
105
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
106
|
+
* Clear mobile WC pending flags after we recover the tx hash from chain
|
|
107
|
+
* (poll won while eth_sendTransaction is still hanging).
|
|
86
108
|
*/
|
|
87
|
-
export declare function
|
|
109
|
+
export declare function clearWalletConnectPendingState(provider: any): void;
|
|
110
|
+
/**
|
|
111
|
+
* When WalletConnect never returns eth_sendTransaction after MetaMask approve,
|
|
112
|
+
* scan recent blocks for a matching outgoing tx (from/to + optional calldata prefix).
|
|
113
|
+
*/
|
|
114
|
+
export declare function pollForOutgoingTx(params: {
|
|
115
|
+
publicClient: {
|
|
116
|
+
getBlockNumber: () => Promise<bigint>;
|
|
117
|
+
getBlock: (args: {
|
|
118
|
+
blockNumber: bigint;
|
|
119
|
+
includeTransactions: boolean;
|
|
120
|
+
}) => Promise<{
|
|
121
|
+
transactions?: unknown[];
|
|
122
|
+
} | null>;
|
|
123
|
+
};
|
|
124
|
+
from: string;
|
|
125
|
+
to: string;
|
|
126
|
+
/** Match tx.input prefix (e.g. 0x + 4-byte selector). */
|
|
127
|
+
dataPrefix?: string;
|
|
128
|
+
value?: bigint;
|
|
129
|
+
startBlock: bigint;
|
|
130
|
+
signal: {
|
|
131
|
+
cancelled: boolean;
|
|
132
|
+
};
|
|
133
|
+
timeoutMs?: number;
|
|
134
|
+
initialDelayMs?: number;
|
|
135
|
+
}): Promise<`0x${string}`>;
|
|
88
136
|
/**
|
|
89
137
|
* Patch the EIP-1193 provider so signing/tx requests resume the relay first.
|
|
90
138
|
* Covers wagmi hooks, viem walletClient, and direct provider.request calls.
|
|
@@ -128,7 +128,7 @@ function isAndroid() {
|
|
|
128
128
|
return false;
|
|
129
129
|
return /Android/i.test(navigator.userAgent || "");
|
|
130
130
|
}
|
|
131
|
-
function isMobileDevice() {
|
|
131
|
+
export function isMobileDevice() {
|
|
132
132
|
return isIOS() || isAndroid();
|
|
133
133
|
}
|
|
134
134
|
function isWalletHttpDeepLink(url) {
|
|
@@ -151,50 +151,185 @@ function canOpenAsWalletApp(url) {
|
|
|
151
151
|
return isWalletHttpDeepLink(url);
|
|
152
152
|
return true;
|
|
153
153
|
}
|
|
154
|
+
function navigateToWalletDeepLink(target) {
|
|
155
|
+
if (!target || typeof window === "undefined")
|
|
156
|
+
return;
|
|
157
|
+
try {
|
|
158
|
+
// Prefer an invisible <a> click — more reliable than location.href behind
|
|
159
|
+
// iOS "open another application?" prompts and after a prior soft navigation.
|
|
160
|
+
const a = document.createElement("a");
|
|
161
|
+
a.href = target;
|
|
162
|
+
a.rel = "noreferrer";
|
|
163
|
+
a.style.display = "none";
|
|
164
|
+
document.body.appendChild(a);
|
|
165
|
+
a.click();
|
|
166
|
+
a.remove();
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
try {
|
|
170
|
+
window.location.href = target;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
// Ignore
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Undo the inline `display: none !important` we apply after connect so the
|
|
179
|
+
* WalletConnect modal can show "Continue in wallet" and process the response.
|
|
180
|
+
* Prefer hideWalletConnectModal() once a session already exists — restoring
|
|
181
|
+
* during a later tx reopens the "All Wallets" picker on top of MetaMask.
|
|
182
|
+
*/
|
|
183
|
+
export function restoreWalletConnectModal() {
|
|
184
|
+
if (typeof document === "undefined")
|
|
185
|
+
return;
|
|
186
|
+
WALLETCONNECT_MODAL_SELECTORS.forEach((sel) => {
|
|
187
|
+
try {
|
|
188
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
189
|
+
const htmlEl = el;
|
|
190
|
+
if (htmlEl?.style) {
|
|
191
|
+
htmlEl.style.removeProperty("display");
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
catch {
|
|
196
|
+
// Ignore invalid selectors in older browsers.
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/** Force-close / hide Reown AppKit + WalletConnect modal DOM after connect or tx. */
|
|
201
|
+
export function hideWalletConnectModal(provider) {
|
|
202
|
+
try {
|
|
203
|
+
if (typeof provider?.modal?.close === "function") {
|
|
204
|
+
provider.modal.close();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// Ignore
|
|
209
|
+
}
|
|
210
|
+
if (typeof document === "undefined")
|
|
211
|
+
return;
|
|
212
|
+
WALLETCONNECT_MODAL_SELECTORS.forEach((sel) => {
|
|
213
|
+
try {
|
|
214
|
+
document.querySelectorAll(sel).forEach((el) => {
|
|
215
|
+
try {
|
|
216
|
+
if (typeof el.close === "function") {
|
|
217
|
+
el.close();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
// Ignore
|
|
222
|
+
}
|
|
223
|
+
const htmlEl = el;
|
|
224
|
+
if (htmlEl?.style) {
|
|
225
|
+
htmlEl.style.setProperty("display", "none", "important");
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
catch {
|
|
230
|
+
// Ignore invalid selectors
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
}
|
|
154
234
|
/**
|
|
155
235
|
* Open the already-connected wallet app so the user sees the confirmation UI.
|
|
156
236
|
* WalletConnect often sends the session_request but does not deeplink on a
|
|
157
237
|
* later tx if the QR modal is hidden — Create Proposal then sits on a loader.
|
|
238
|
+
*
|
|
239
|
+
* When a session already exists, do NOT reopen the WC "All Wallets" modal —
|
|
240
|
+
* that stacks under the iOS "Open in MetaMask?" prompt and stays after confirm.
|
|
241
|
+
*
|
|
242
|
+
* Returns false if skipped (already backgrounded / already opened once).
|
|
158
243
|
*/
|
|
159
244
|
export function openConnectedWalletApp(provider) {
|
|
160
245
|
if (typeof window === "undefined" || !isMobileDevice())
|
|
161
|
-
return;
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
246
|
+
return false;
|
|
247
|
+
// Never queue another metamask:// while the tab is already leaving / hidden —
|
|
248
|
+
// those become stuck "Open in MetaMask?" dialogs after the user returns.
|
|
249
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (provider?.__abstraxnWcDeeplinkOpened) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
const hasSession = hasActiveWalletConnectSession(provider);
|
|
256
|
+
// Only touch the WC modal when there is no session yet (connect flow).
|
|
257
|
+
// For later txs, deeplink straight into the peer wallet app.
|
|
258
|
+
if (!hasSession) {
|
|
259
|
+
restoreWalletConnectModal();
|
|
260
|
+
try {
|
|
261
|
+
if (typeof provider?.modal?.open === "function") {
|
|
262
|
+
void provider.modal.open();
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// Modal may not exist after we force-hid it post-connect.
|
|
166
267
|
}
|
|
167
268
|
}
|
|
168
|
-
|
|
169
|
-
//
|
|
269
|
+
else {
|
|
270
|
+
// Keep the picker closed while MetaMask opens.
|
|
271
|
+
hideWalletConnectModal(provider);
|
|
170
272
|
}
|
|
171
273
|
const peer = provider?.session?.peer?.metadata ??
|
|
172
274
|
provider?.signer?.session?.peer?.metadata;
|
|
173
275
|
const native = String(peer?.redirect?.native || "");
|
|
174
276
|
const universal = String(peer?.redirect?.universal || "");
|
|
175
277
|
const name = String(peer?.name || "").toLowerCase();
|
|
176
|
-
|
|
278
|
+
const candidates = [];
|
|
177
279
|
if (canOpenAsWalletApp(native))
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
280
|
+
candidates.push(native);
|
|
281
|
+
if (canOpenAsWalletApp(universal))
|
|
282
|
+
candidates.push(universal);
|
|
283
|
+
if (name.includes("metamask") || !name) {
|
|
284
|
+
candidates.push(isIOS() ? "metamask://" : "https://metamask.app.link/wc", "https://metamask.app.link/wc");
|
|
183
285
|
}
|
|
184
286
|
else if (name.includes("rainbow")) {
|
|
185
|
-
|
|
287
|
+
candidates.push("rainbow://");
|
|
186
288
|
}
|
|
187
289
|
else if (name.includes("trust")) {
|
|
188
|
-
|
|
189
|
-
}
|
|
190
|
-
if (!target)
|
|
191
|
-
return;
|
|
192
|
-
try {
|
|
193
|
-
window.location.href = target;
|
|
194
|
-
}
|
|
195
|
-
catch {
|
|
196
|
-
// Ignore
|
|
290
|
+
candidates.push("trust://");
|
|
197
291
|
}
|
|
292
|
+
const unique = [...new Set(candidates.filter(Boolean))];
|
|
293
|
+
if (!unique.length)
|
|
294
|
+
return false;
|
|
295
|
+
provider.__abstraxnWcDeeplinkOpened = true;
|
|
296
|
+
navigateToWalletDeepLink(unique[0]);
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* At most one MetaMask deeplink per approval request.
|
|
301
|
+
* Extra retries queue iOS "Open in MetaMask?" prompts that stay on screen
|
|
302
|
+
* after the tx is already confirmed (JS cannot dismiss that system dialog).
|
|
303
|
+
* Cancel remaining work as soon as the tab is backgrounded (user opened MM).
|
|
304
|
+
*/
|
|
305
|
+
export function scheduleOpenConnectedWalletApp(provider, delaysMs = [150]) {
|
|
306
|
+
if (typeof window === "undefined" || !isMobileDevice())
|
|
307
|
+
return () => { };
|
|
308
|
+
provider.__abstraxnWcDeeplinkOpened = false;
|
|
309
|
+
const timers = [];
|
|
310
|
+
let cancelled = false;
|
|
311
|
+
const cancel = () => {
|
|
312
|
+
cancelled = true;
|
|
313
|
+
timers.splice(0).forEach((id) => window.clearTimeout(id));
|
|
314
|
+
if (typeof document !== "undefined") {
|
|
315
|
+
document.removeEventListener("visibilitychange", onHidden);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
const onHidden = () => {
|
|
319
|
+
if (document.visibilityState === "hidden") {
|
|
320
|
+
// User is in (or opening) MetaMask — stop any further deeplinks.
|
|
321
|
+
cancel();
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
document.addEventListener("visibilitychange", onHidden);
|
|
325
|
+
delaysMs.forEach((ms) => {
|
|
326
|
+
timers.push(window.setTimeout(() => {
|
|
327
|
+
if (cancelled)
|
|
328
|
+
return;
|
|
329
|
+
openConnectedWalletApp(provider);
|
|
330
|
+
}, ms));
|
|
331
|
+
});
|
|
332
|
+
return cancel;
|
|
198
333
|
}
|
|
199
334
|
function getWalletConnectRedirect() {
|
|
200
335
|
const native = getSourceBrowserNativeRedirect();
|
|
@@ -418,25 +553,83 @@ export function waitUntilDocumentVisible() {
|
|
|
418
553
|
});
|
|
419
554
|
}
|
|
420
555
|
/**
|
|
421
|
-
*
|
|
422
|
-
*
|
|
556
|
+
* Clear mobile WC pending flags after we recover the tx hash from chain
|
|
557
|
+
* (poll won while eth_sendTransaction is still hanging).
|
|
423
558
|
*/
|
|
424
|
-
export function
|
|
425
|
-
if (
|
|
559
|
+
export function clearWalletConnectPendingState(provider) {
|
|
560
|
+
if (!provider)
|
|
426
561
|
return;
|
|
427
|
-
|
|
562
|
+
try {
|
|
563
|
+
provider[PENDING_FLAG] = 0;
|
|
564
|
+
provider[NEEDS_RESUME_FLAG] = false;
|
|
565
|
+
provider.__abstraxnWcDeeplinkOpened = true;
|
|
566
|
+
hideWalletConnectModal(provider);
|
|
567
|
+
}
|
|
568
|
+
catch {
|
|
569
|
+
// ignore
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* When WalletConnect never returns eth_sendTransaction after MetaMask approve,
|
|
574
|
+
* scan recent blocks for a matching outgoing tx (from/to + optional calldata prefix).
|
|
575
|
+
*/
|
|
576
|
+
export async function pollForOutgoingTx(params) {
|
|
577
|
+
const { publicClient, from, to, dataPrefix, value, startBlock, signal, timeoutMs = 90_000, initialDelayMs = 10_000, } = params;
|
|
578
|
+
if (initialDelayMs > 0) {
|
|
579
|
+
await new Promise((r) => setTimeout(r, initialDelayMs));
|
|
580
|
+
}
|
|
581
|
+
if (signal.cancelled) {
|
|
582
|
+
throw new Error("cancelled");
|
|
583
|
+
}
|
|
584
|
+
await waitUntilDocumentVisible();
|
|
585
|
+
const fromLc = from.toLowerCase();
|
|
586
|
+
const toLc = to.toLowerCase();
|
|
587
|
+
const prefixLc = dataPrefix ? dataPrefix.toLowerCase() : undefined;
|
|
588
|
+
const started = Date.now();
|
|
589
|
+
while (!signal.cancelled && Date.now() - started < timeoutMs) {
|
|
428
590
|
try {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
591
|
+
const latest = await publicClient.getBlockNumber();
|
|
592
|
+
const fromBlock = startBlock > 0n
|
|
593
|
+
? startBlock
|
|
594
|
+
: latest > 12n
|
|
595
|
+
? latest - 12n
|
|
596
|
+
: 0n;
|
|
597
|
+
for (let b = fromBlock; b <= latest; b++) {
|
|
598
|
+
if (signal.cancelled)
|
|
599
|
+
break;
|
|
600
|
+
const block = await publicClient.getBlock({
|
|
601
|
+
blockNumber: b,
|
|
602
|
+
includeTransactions: true,
|
|
603
|
+
});
|
|
604
|
+
const txs = (block?.transactions || []);
|
|
605
|
+
for (const tx of txs) {
|
|
606
|
+
if (!tx || typeof tx === "string")
|
|
607
|
+
continue;
|
|
608
|
+
if (String(tx.from || "").toLowerCase() !== fromLc)
|
|
609
|
+
continue;
|
|
610
|
+
if (String(tx.to || "").toLowerCase() !== toLc)
|
|
611
|
+
continue;
|
|
612
|
+
if (value !== undefined && BigInt(tx.value ?? 0) !== value) {
|
|
613
|
+
continue;
|
|
614
|
+
}
|
|
615
|
+
if (prefixLc) {
|
|
616
|
+
const input = String(tx.input || tx.data || "").toLowerCase();
|
|
617
|
+
if (!input.startsWith(prefixLc))
|
|
618
|
+
continue;
|
|
619
|
+
}
|
|
620
|
+
const hash = tx.hash;
|
|
621
|
+
if (typeof hash === "string" && hash.startsWith("0x")) {
|
|
622
|
+
return hash;
|
|
623
|
+
}
|
|
433
624
|
}
|
|
434
|
-
}
|
|
625
|
+
}
|
|
435
626
|
}
|
|
436
627
|
catch {
|
|
437
|
-
//
|
|
628
|
+
// RPC blip — keep polling
|
|
438
629
|
}
|
|
439
|
-
|
|
630
|
+
await new Promise((r) => setTimeout(r, 2_000));
|
|
631
|
+
}
|
|
632
|
+
throw new Error("No WalletConnect response and no matching on-chain transaction found. If MetaMask showed confirmed, refresh and check the explorer.");
|
|
440
633
|
}
|
|
441
634
|
function getRequestMethod(args) {
|
|
442
635
|
const payload = args[0];
|
|
@@ -475,10 +668,20 @@ export function patchWalletConnectProviderForMobile(provider) {
|
|
|
475
668
|
}
|
|
476
669
|
return originalRequest(...args);
|
|
477
670
|
}
|
|
671
|
+
let cancelDeeplinks;
|
|
478
672
|
if (isApproval) {
|
|
479
673
|
provider[PENDING_FLAG] = (provider[PENDING_FLAG] || 0) + 1;
|
|
480
|
-
|
|
674
|
+
// Already connected: never re-open "All Wallets". Only restore modal
|
|
675
|
+
// during connect (no session yet).
|
|
676
|
+
if (hasActiveWalletConnectSession(provider)) {
|
|
677
|
+
hideWalletConnectModal(provider);
|
|
678
|
+
}
|
|
679
|
+
else {
|
|
680
|
+
restoreWalletConnectModal();
|
|
681
|
+
}
|
|
481
682
|
syncWalletConnectDappRedirect(provider);
|
|
683
|
+
// Mark before deeplink so a fast backgrounding still triggers force-resume.
|
|
684
|
+
provider[NEEDS_RESUME_FLAG] = true;
|
|
482
685
|
}
|
|
483
686
|
try {
|
|
484
687
|
// Never force-restart right before sending a new tx — that is what
|
|
@@ -488,14 +691,31 @@ export function patchWalletConnectProviderForMobile(provider) {
|
|
|
488
691
|
new Promise((resolve) => setTimeout(resolve, 1000)),
|
|
489
692
|
]);
|
|
490
693
|
const resultPromise = originalRequest(...args);
|
|
491
|
-
if (
|
|
492
|
-
|
|
694
|
+
if (isApproval) {
|
|
695
|
+
// Do NOT auto-deeplink when a session already exists.
|
|
696
|
+
// WC ethereum-provider already opens the peer wallet; our extra
|
|
697
|
+
// metamask:// calls queue iOS "Open in MetaMask?" dialogs that stay
|
|
698
|
+
// on screen after the tx is confirmed (JS cannot dismiss them).
|
|
699
|
+
// Connect flow (no session) may still use a single scheduled open.
|
|
700
|
+
if (!hasActiveWalletConnectSession(provider)) {
|
|
701
|
+
cancelDeeplinks = scheduleOpenConnectedWalletApp(provider);
|
|
702
|
+
}
|
|
703
|
+
else {
|
|
704
|
+
hideWalletConnectModal(provider);
|
|
705
|
+
}
|
|
493
706
|
}
|
|
494
707
|
return await resultPromise;
|
|
495
708
|
}
|
|
496
709
|
finally {
|
|
710
|
+
cancelDeeplinks?.();
|
|
497
711
|
if (isApproval) {
|
|
498
712
|
provider[PENDING_FLAG] = Math.max(0, (provider[PENDING_FLAG] || 1) - 1);
|
|
713
|
+
provider.__abstraxnWcDeeplinkOpened = false;
|
|
714
|
+
// Close picker that may have been left open after approve/reject.
|
|
715
|
+
hideWalletConnectModal(provider);
|
|
716
|
+
if (!hasPendingWalletConnectApproval(provider)) {
|
|
717
|
+
provider[NEEDS_RESUME_FLAG] = false;
|
|
718
|
+
}
|
|
499
719
|
}
|
|
500
720
|
}
|
|
501
721
|
};
|
|
@@ -507,10 +727,9 @@ export function patchWalletConnectProviderForMobile(provider) {
|
|
|
507
727
|
export function subscribeWalletConnectForegroundResume(provider) {
|
|
508
728
|
if (typeof window === "undefined" || !provider)
|
|
509
729
|
return () => { };
|
|
510
|
-
|
|
730
|
+
const timers = [];
|
|
511
731
|
const resume = () => {
|
|
512
|
-
|
|
513
|
-
clearTimeout(timer);
|
|
732
|
+
timers.splice(0).forEach((t) => clearTimeout(t));
|
|
514
733
|
const pending = hasPendingWalletConnectApproval(provider);
|
|
515
734
|
const needsResume = pending || !!provider[NEEDS_RESUME_FLAG];
|
|
516
735
|
// Do not restart after connect-only backgrounding — that broke the next
|
|
@@ -519,11 +738,30 @@ export function subscribeWalletConnectForegroundResume(provider) {
|
|
|
519
738
|
clearWalletReturnIfSameDocument();
|
|
520
739
|
return;
|
|
521
740
|
}
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
741
|
+
// iOS often reports "visible" before networking is ready; retry force-resume.
|
|
742
|
+
const delays = [50, 300, 1000, 2000, 4000];
|
|
743
|
+
delays.forEach((ms) => {
|
|
744
|
+
timers.push(setTimeout(() => {
|
|
745
|
+
if (!hasPendingWalletConnectApproval(provider) &&
|
|
746
|
+
!provider[NEEDS_RESUME_FLAG]) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
clearWalletReturnIfSameDocument();
|
|
750
|
+
void resumeWalletConnectRelayer(provider, { force: true }).then(() => {
|
|
751
|
+
if (!hasPendingWalletConnectApproval(provider)) {
|
|
752
|
+
provider[NEEDS_RESUME_FLAG] = false;
|
|
753
|
+
hideWalletConnectModal(provider);
|
|
754
|
+
}
|
|
755
|
+
});
|
|
756
|
+
// Do not restoreWalletConnectModal here — that reopens "All Wallets".
|
|
757
|
+
if (!hasActiveWalletConnectSession(provider)) {
|
|
758
|
+
restoreWalletConnectModal();
|
|
759
|
+
}
|
|
760
|
+
else {
|
|
761
|
+
hideWalletConnectModal(provider);
|
|
762
|
+
}
|
|
763
|
+
}, ms));
|
|
764
|
+
});
|
|
527
765
|
};
|
|
528
766
|
const onVisibility = () => {
|
|
529
767
|
if (document.visibilityState === "hidden") {
|
|
@@ -539,8 +777,7 @@ export function subscribeWalletConnectForegroundResume(provider) {
|
|
|
539
777
|
window.addEventListener("pageshow", resume);
|
|
540
778
|
window.addEventListener("focus", resume);
|
|
541
779
|
return () => {
|
|
542
|
-
|
|
543
|
-
clearTimeout(timer);
|
|
780
|
+
timers.splice(0).forEach((t) => clearTimeout(t));
|
|
544
781
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
545
782
|
window.removeEventListener("pageshow", resume);
|
|
546
783
|
window.removeEventListener("focus", resume);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@abstraxn/signer-react",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.9",
|
|
4
4
|
"description": "React SDK for Abstraxn Wallet - React components, hooks, and providers for seamless Web3 wallet integration",
|
|
5
5
|
"main": "./dist/src/index.js",
|
|
6
6
|
"module": "./dist/src/index.js",
|