236 lines
6.3 KiB
JavaScript
236 lines
6.3 KiB
JavaScript
|
import localforage from 'localforage';
|
||
|
import { WalletNotConnectedError, WalletNotReadyError, WalletReadyState } from '@solana/wallet-adapter-base';
|
||
|
|
||
|
export const SolanaWalletHandler = class {
|
||
|
constructor() {
|
||
|
this.name = null;
|
||
|
this.publicKey = null;
|
||
|
this.isConnected = false;
|
||
|
this.isConnecting = false;
|
||
|
this.isDisconnecting = false;
|
||
|
this.ready = 'Unsupported';
|
||
|
this.localStorageKey = 'walletAdapter'
|
||
|
this.adapter = null;
|
||
|
this.wallets = [];
|
||
|
this.signTransaction = undefined;
|
||
|
this.signAllTransactions = undefined;
|
||
|
this.signMessage = undefined;
|
||
|
|
||
|
/** @type {LocalForage} */
|
||
|
this.store = localforage.createInstance({
|
||
|
name: "tp-solana-connect"
|
||
|
});
|
||
|
}
|
||
|
|
||
|
async connect() {
|
||
|
if (this.isConnected || this.isConnecting || this.isDisconnecting) return;
|
||
|
|
||
|
if (!adapter) throw new WalletNotReadyError();
|
||
|
|
||
|
if (!(this.ready === WalletReadyState.Installed || this.ready === WalletReadyState.Loadable)) {
|
||
|
this.resetWallet();
|
||
|
|
||
|
if (typeof window !== 'undefined') {
|
||
|
window.open(adapter.url, '_blank');
|
||
|
}
|
||
|
|
||
|
throw new WalletNotReadyError();
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
this.isConnecting = true;
|
||
|
await adapter.connect();
|
||
|
} catch (error) {
|
||
|
walletStore.resetWallet();
|
||
|
throw error;
|
||
|
} finally {
|
||
|
this.isConnecting = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async autoConnect() {
|
||
|
try {
|
||
|
this.isConnecting = true;
|
||
|
await this.adapter.connect();
|
||
|
}
|
||
|
catch (error) {
|
||
|
this.resetWallet();
|
||
|
}
|
||
|
finally {
|
||
|
this.isConnecting = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
async disconnect() {
|
||
|
if (this.disconnecting) return;
|
||
|
if (!this.adapter) return this.resetWallet();
|
||
|
|
||
|
try {
|
||
|
this.isDisconnecting = true;
|
||
|
await this.adapter.disconnect();
|
||
|
}
|
||
|
finally {
|
||
|
this.resetWallet();
|
||
|
this.isDisconnecting = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
updateAdapter(adapter) {
|
||
|
removeAdapterEventListeners();
|
||
|
if (adapter) {
|
||
|
this.addAdapterEventListeners(adapter);
|
||
|
this.adapter = adapter;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
resetWallet() {
|
||
|
this.isConnected = false;
|
||
|
this.isConnecting = false;
|
||
|
this.isDisconnecting = false;
|
||
|
this.ready = 'Unsupported';
|
||
|
this.adapter = null;
|
||
|
this.store.setItem('wallet', null);
|
||
|
}
|
||
|
|
||
|
addAdapterEventListeners(adapter) {
|
||
|
this.wallets.forEach(({ adapter }) => {
|
||
|
adapter.on('readyStateChange', this.onReadyStateChange, adapter);
|
||
|
});
|
||
|
|
||
|
adapter.on('connect', onConnect);
|
||
|
adapter.on('disconnect', onDisconnect);
|
||
|
adapter.on('error', onError);
|
||
|
}
|
||
|
|
||
|
removeAdapterEventListeners() {
|
||
|
if (!this.adapter) return;
|
||
|
|
||
|
this.wallets.forEach(({ adapter }) => {
|
||
|
adapter.off('readyStateChange', this.onReadyStateChange, adapter);
|
||
|
});
|
||
|
|
||
|
this.adapter.off('connect', onConnect);
|
||
|
this.adapter.off('disconnect', onDisconnect);
|
||
|
this.adapter.off('error', onError);
|
||
|
}
|
||
|
|
||
|
onReadyStateChange(readyState) {
|
||
|
if (!this.adapter) return;
|
||
|
|
||
|
this.ready = adapter.readyState;
|
||
|
|
||
|
// When the wallets change, start to listen for changes to their `readyState`
|
||
|
const walletIndex = this.wallets.findIndex(({ adapter }) => adapter.name === this.name);
|
||
|
if (walletIndex === -1) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
this.wallets = [
|
||
|
...this.wallets.slice(0, walletIndex),
|
||
|
{ ...this.wallets[walletIndex], readyState },
|
||
|
...this.wallets.slice(walletIndex + 1),
|
||
|
];
|
||
|
}
|
||
|
|
||
|
async select(walletName) {
|
||
|
if (this.name === walletName) return;
|
||
|
|
||
|
if (this.adapter) {
|
||
|
await disconnect();
|
||
|
}
|
||
|
|
||
|
walletStore.updateWallet(walletName);
|
||
|
}
|
||
|
|
||
|
updateWalletName(name) {
|
||
|
var _a;
|
||
|
const adapter = (_a = this.walletsByName === null || this.walletsByName === undefined ? undefined : walletsByName[name]) !== null && _a !== void 0 ? _a : null;
|
||
|
this.store.setItem(this.localStorageKey, name);
|
||
|
updateWalletState(adapter);
|
||
|
}
|
||
|
|
||
|
updateWalletState(adapter) {
|
||
|
this.updateAdapter(adapter);
|
||
|
this.name = adapter?.name || null;
|
||
|
this.ready = adapter?.readyState || 'Unsupported';
|
||
|
this.publicKey = adapter?.publicKey || null;
|
||
|
this.connected = adapter?.connected || false;
|
||
|
}
|
||
|
|
||
|
onConnect() {
|
||
|
if (!this.adapter) return;
|
||
|
|
||
|
walletStore.updateFeatures(adapter);
|
||
|
walletStore.updateStatus({
|
||
|
publicKey: adapter.publicKey,
|
||
|
connected: adapter.connected,
|
||
|
});
|
||
|
}
|
||
|
|
||
|
updateFeatures() {
|
||
|
if (!this.adapter) return;
|
||
|
|
||
|
this.signTransaction = undefined;
|
||
|
this.signAllTransactions = undefined;
|
||
|
this.signMessage = undefined;
|
||
|
|
||
|
// Sign a transaction if the wallet supports it
|
||
|
if ('signTransaction' in this.adapter) {
|
||
|
this.signTransaction = async function (transaction) {
|
||
|
if (!this.connected) throw new WalletNotConnectedError();
|
||
|
return await this.adapter.signTransaction(transaction);
|
||
|
};
|
||
|
}
|
||
|
// Sign multiple transactions if the wallet supports it
|
||
|
if ('signAllTransactions' in this.adapter) {
|
||
|
this.signAllTransactions = async function (transactions) {
|
||
|
if (!this.connected) throw newError(new WalletNotConnectedError());
|
||
|
return await this.adapter.signAllTransactions(transactions);
|
||
|
};
|
||
|
}
|
||
|
// Sign an arbitrary message if the wallet supports it
|
||
|
if ('signMessage' in this.adapter) {
|
||
|
this.signMessage = async function (message) {
|
||
|
if (!this.connected) throw newError(new WalletNotConnectedError());
|
||
|
return await this.adapter.signMessage(message);
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
removeAdapterEventListeners() {
|
||
|
if (!this.adapter) return;
|
||
|
|
||
|
this.wallets.forEach(({ adapter }) => {
|
||
|
adapter.off('readyStateChange', this.onReadyStateChange, adapter);
|
||
|
});
|
||
|
|
||
|
this.adapter.off('connect', this.onConnect);
|
||
|
this.adapter.off('disconnect', this.onDisconnect);
|
||
|
this.adapter.off('error', this.onError);
|
||
|
}
|
||
|
|
||
|
shouldAutoConnect() {
|
||
|
const { adapter, ready, connected, connecting } = this;
|
||
|
|
||
|
const _autoConnect = (adapter && typeof autoConnect === 'function') ? this.autoConnect(adapter) : autoConnect;
|
||
|
return !(!_autoConnect ||
|
||
|
!adapter ||
|
||
|
!(ready === WalletReadyState.Installed ||
|
||
|
ready === WalletReadyState.Loadable) ||
|
||
|
connected ||
|
||
|
connecting);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export const walletMixin = function(superClass) {
|
||
|
return class extends superClass {
|
||
|
constructor() {
|
||
|
super();
|
||
|
|
||
|
if (this.walletStore === undefined) {
|
||
|
this.walletStore = new SolanaWalletHandler();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|