tp-solana-connect/wallet-mixin.js

236 lines
6.3 KiB
JavaScript
Raw Permalink Normal View History

2024-09-23 22:46:16 +02:00
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();
}
}
}
}