|
|
@@ -1,36 +1,52 @@
|
|
|
import Wallet from '@project-serum/sol-wallet-adapter';
|
|
|
import {
|
|
|
BaseSignerWalletAdapter,
|
|
|
+ pollUntilReady,
|
|
|
WalletAdapterNetwork,
|
|
|
WalletConnectionError,
|
|
|
WalletDisconnectedError,
|
|
|
WalletDisconnectionError,
|
|
|
+ WalletError,
|
|
|
WalletNotConnectedError,
|
|
|
+ WalletNotFoundError,
|
|
|
WalletSignTransactionError,
|
|
|
+ WalletTimeoutError,
|
|
|
WalletWindowBlockedError,
|
|
|
WalletWindowClosedError,
|
|
|
} from '@solana/wallet-adapter-base';
|
|
|
import { PublicKey, Transaction } from '@solana/web3.js';
|
|
|
|
|
|
-type SolletProvider = string | { postMessage(...args: unknown[]): unknown };
|
|
|
+interface SolletWallet {
|
|
|
+ postMessage(...args: unknown[]): unknown;
|
|
|
+}
|
|
|
+
|
|
|
+interface SolletWindow extends Window {
|
|
|
+ sollet?: SolletWallet;
|
|
|
+}
|
|
|
+
|
|
|
+declare const window: SolletWindow;
|
|
|
|
|
|
export interface SolletWalletAdapterConfig {
|
|
|
- provider?: SolletProvider;
|
|
|
+ provider?: string | SolletWallet;
|
|
|
network?: WalletAdapterNetwork;
|
|
|
+ pollInterval?: number;
|
|
|
+ pollCount?: number;
|
|
|
}
|
|
|
|
|
|
export class SolletWalletAdapter extends BaseSignerWalletAdapter {
|
|
|
- private _provider: SolletProvider;
|
|
|
+ private _provider: string | SolletWallet | undefined;
|
|
|
private _network: WalletAdapterNetwork;
|
|
|
private _connecting: boolean;
|
|
|
private _wallet: Wallet | null;
|
|
|
|
|
|
constructor(config: SolletWalletAdapterConfig = {}) {
|
|
|
super();
|
|
|
- this._provider = config.provider || 'https://www.sollet.io';
|
|
|
+ this._provider = config.provider || window.sollet;
|
|
|
this._network = config.network || WalletAdapterNetwork.Mainnet;
|
|
|
this._connecting = false;
|
|
|
this._wallet = null;
|
|
|
+
|
|
|
+ if (!this.ready) pollUntilReady(this, config.pollInterval || 1000, config.pollCount || 3);
|
|
|
}
|
|
|
|
|
|
get publicKey(): PublicKey | null {
|
|
|
@@ -38,8 +54,10 @@ export class SolletWalletAdapter extends BaseSignerWalletAdapter {
|
|
|
}
|
|
|
|
|
|
get ready(): boolean {
|
|
|
- // @FIXME
|
|
|
- return typeof window !== 'undefined';
|
|
|
+ return (
|
|
|
+ typeof window !== 'undefined' &&
|
|
|
+ (typeof this._provider === 'string' || typeof window.sollet?.postMessage === 'function')
|
|
|
+ );
|
|
|
}
|
|
|
|
|
|
get connecting(): boolean {
|
|
|
@@ -59,34 +77,63 @@ export class SolletWalletAdapter extends BaseSignerWalletAdapter {
|
|
|
if (this.connected || this.connecting) return;
|
|
|
this._connecting = true;
|
|
|
|
|
|
+ const provider = this._provider || window.sollet;
|
|
|
+ if (!provider) throw new WalletNotFoundError();
|
|
|
+
|
|
|
let wallet: Wallet;
|
|
|
- let interval: NodeJS.Timer | undefined;
|
|
|
try {
|
|
|
- wallet = new Wallet(this._provider, this._network);
|
|
|
-
|
|
|
- // HACK: sol-wallet-adapter doesn't reject or emit an event if the popup is closed or blocked
|
|
|
- await new Promise<void>((resolve, reject) => {
|
|
|
- wallet.connect().then(resolve, reject);
|
|
|
-
|
|
|
- if (typeof this._provider === 'string') {
|
|
|
- let count = 0;
|
|
|
-
|
|
|
- interval = setInterval(() => {
|
|
|
- const popup = (wallet as any)._popup;
|
|
|
- if (popup) {
|
|
|
- if (popup.closed) reject(new WalletWindowClosedError());
|
|
|
- } else {
|
|
|
- if (count > 50) reject(new WalletWindowBlockedError());
|
|
|
- }
|
|
|
-
|
|
|
- count++;
|
|
|
- }, 100);
|
|
|
- }
|
|
|
- });
|
|
|
+ wallet = new Wallet(provider, this._network);
|
|
|
+
|
|
|
+ // HACK: sol-wallet-adapter doesn't reject or emit an event if the popup or extension is closed or blocked
|
|
|
+ const handleDisconnect: (...args: unknown[]) => unknown = (wallet as any).handleDisconnect;
|
|
|
+ let timeout: NodeJS.Timer | undefined;
|
|
|
+ let interval: NodeJS.Timer | undefined;
|
|
|
+ try {
|
|
|
+ await new Promise<void>((resolve, reject) => {
|
|
|
+ const connect = () => {
|
|
|
+ if (timeout) clearTimeout(timeout);
|
|
|
+ wallet.off('connect', connect);
|
|
|
+ resolve();
|
|
|
+ };
|
|
|
+
|
|
|
+ (wallet as any).handleDisconnect = (...args: unknown[]): unknown => {
|
|
|
+ wallet.off('connect', connect);
|
|
|
+ reject(new WalletWindowClosedError());
|
|
|
+ return handleDisconnect.apply(wallet, args);
|
|
|
+ };
|
|
|
+
|
|
|
+ wallet.on('connect', connect);
|
|
|
+
|
|
|
+ wallet.connect().catch((reason: any) => {
|
|
|
+ wallet.off('connect', connect);
|
|
|
+ reject(reason);
|
|
|
+ });
|
|
|
+
|
|
|
+ if (typeof provider === 'string') {
|
|
|
+ let count = 0;
|
|
|
+
|
|
|
+ interval = setInterval(() => {
|
|
|
+ const popup = (wallet as any)._popup;
|
|
|
+ if (popup) {
|
|
|
+ if (popup.closed) reject(new WalletWindowClosedError());
|
|
|
+ } else {
|
|
|
+ if (count > 50) reject(new WalletWindowBlockedError());
|
|
|
+ }
|
|
|
+
|
|
|
+ count++;
|
|
|
+ }, 100);
|
|
|
+ } else {
|
|
|
+ // HACK: sol-wallet-adapter doesn't reject or emit an event if the extension is closed or ignored
|
|
|
+ timeout = setTimeout(() => reject(new WalletTimeoutError()), 10000);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ } finally {
|
|
|
+ (wallet as any).handleDisconnect = handleDisconnect;
|
|
|
+ if (interval) clearInterval(interval);
|
|
|
+ }
|
|
|
} catch (error: any) {
|
|
|
+ if (error instanceof WalletError) throw error;
|
|
|
throw new WalletConnectionError(error?.message, error);
|
|
|
- } finally {
|
|
|
- if (interval) clearInterval(interval);
|
|
|
}
|
|
|
|
|
|
wallet.on('disconnect', this._disconnected);
|
|
|
@@ -109,10 +156,38 @@ export class SolletWalletAdapter extends BaseSignerWalletAdapter {
|
|
|
|
|
|
this._wallet = null;
|
|
|
|
|
|
+ // HACK: sol-wallet-adapter doesn't reliably fulfill its promise or emit an event on disconnect
|
|
|
+ const handleDisconnect: (...args: unknown[]) => unknown = (wallet as any).handleDisconnect;
|
|
|
try {
|
|
|
- await wallet.disconnect();
|
|
|
+ await new Promise<void>((resolve, reject) => {
|
|
|
+ const timeout = setTimeout(() => resolve(), 250);
|
|
|
+
|
|
|
+ (wallet as any).handleDisconnect = (...args: unknown[]): unknown => {
|
|
|
+ clearTimeout(timeout);
|
|
|
+ resolve();
|
|
|
+ return handleDisconnect.apply(wallet, args);
|
|
|
+ };
|
|
|
+
|
|
|
+ wallet.disconnect().then(
|
|
|
+ () => {
|
|
|
+ clearTimeout(timeout);
|
|
|
+ resolve();
|
|
|
+ },
|
|
|
+ (error) => {
|
|
|
+ clearTimeout(timeout);
|
|
|
+ // HACK: sol-wallet-adapter rejects with an error on disconnect
|
|
|
+ if (error.message === 'Wallet disconnected') {
|
|
|
+ resolve();
|
|
|
+ } else {
|
|
|
+ reject(error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ );
|
|
|
+ });
|
|
|
} catch (error: any) {
|
|
|
this.emit('error', new WalletDisconnectionError(error?.message, error));
|
|
|
+ } finally {
|
|
|
+ (wallet as any).handleDisconnect = handleDisconnect;
|
|
|
}
|
|
|
|
|
|
this.emit('disconnect');
|