import type { WalletName } from '@solana/wallet-adapter-base'; import { BaseMessageSignerWalletAdapter, scopePollingDetectionStrategy, WalletAccountError, WalletConnectionError, WalletDisconnectionError, WalletError, WalletNotConnectedError, WalletNotReadyError, WalletPublicKeyError, WalletReadyState, WalletSignMessageError, WalletSignTransactionError, } from '@solana/wallet-adapter-base'; import type { Transaction } from '@solana/web3.js'; import { PublicKey } from '@solana/web3.js'; import bs58 from 'bs58'; interface SlopeWallet { connect(): Promise<{ msg: string; data: { publicKey?: string; }; }>; disconnect(): Promise<{ msg: string }>; signTransaction(message: string): Promise<{ msg: string; data: { publicKey?: string; signature?: string; }; }>; signAllTransactions(messages: string[]): Promise<{ msg: string; data: { publicKey?: string; signatures?: string[]; }; }>; signMessage(message: Uint8Array): Promise<{ data: { signature: string } }>; } interface SlopeWindow extends Window { Slope?: { new (): SlopeWallet; }; slopeApp?: unknown; } declare const window: SlopeWindow; export interface SlopeWalletAdapterConfig {} export const SlopeWalletName = 'Slope' as WalletName<'Slope'>; export class SlopeWalletAdapter extends BaseMessageSignerWalletAdapter { name = SlopeWalletName; url = 'https://slope.finance'; icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTI4IiBoZWlnaHQ9IjEyOCIgdmlld0JveD0iMCAwIDEyOCAxMjgiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxyZWN0IHdpZHRoPSIxMjgiIGhlaWdodD0iMTI4IiByeD0iNjQiIGZpbGw9IiM2RTY2RkEiLz4KPHBhdGggZD0iTTI3Ljk0NzUgNTIuMTU5Nkw1MS45ODI2IDI4LjA1NzJMNzIuNjA5OCA3LjY1Mzg5QzczLjg3MzQgNi40MDQwMSA3Ni4wMTc4IDcuMjk5MSA3Ni4wMTc4IDkuMDc2NDJMNzYuMDE4NyA1Mi4xNTlMNTEuOTgzNiA3Ni4xMjY4TDI3Ljk0NzUgNTIuMTU5NloiIGZpbGw9InVybCgjcGFpbnQwX2xpbmVhcl8zNzk1XzI1NTQzKSIvPgo8cGF0aCBkPSJNMTAwLjA1MyA3NS45OTNMNzYuMDE4IDUxLjk1OEw1MS45ODI5IDc1Ljk5MzFMNTEuOTgyOSAxMTguOTI0QzUxLjk4MjkgMTIwLjcwMyA1NC4xMzEyIDEyMS41OTcgNTUuMzkzNyAxMjAuMzQzTDEwMC4wNTMgNzUuOTkzWiIgZmlsbD0idXJsKCNwYWludDFfbGluZWFyXzM3OTVfMjU1NDMpIi8+CjxwYXRoIGQ9Ik0yNy45NDcgNTIuMTYwMUg0NC42ODM5QzQ4LjcxNDcgNTIuMTYwMSA1MS45ODIyIDU1LjQyNzYgNTEuOTgyMiA1OS40NTgzVjc2LjEyNjlIMzUuMjQ1M0MzMS4yMTQ2IDc2LjEyNjkgMjcuOTQ3IDcyLjg1OTQgMjcuOTQ3IDY4LjgyODdWNTIuMTYwMVoiIGZpbGw9IiNGMUYwRkYiLz4KPHBhdGggZD0iTTc2LjAxNzggNTIuMTYwMUg5Mi43NTQ3Qzk2Ljc4NTUgNTIuMTYwMSAxMDAuMDUzIDU1LjQyNzYgMTAwLjA1MyA1OS40NTgzVjc2LjEyNjlIODMuMzE2MUM3OS4yODU0IDc2LjEyNjkgNzYuMDE3OCA3Mi44NTk0IDc2LjAxNzggNjguODI4N1Y1Mi4xNjAxWiIgZmlsbD0iI0YxRjBGRiIvPgo8ZGVmcz4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDBfbGluZWFyXzM3OTVfMjU1NDMiIHgxPSI1MS45ODMxIiB5MT0iNy4wNzE1NSIgeDI9IjUxLjk4MzEiIHkyPSI3Ni4xMjY4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIHN0b3AtY29sb3I9IiNBOEFERkYiLz4KPHN0b3Agb2Zmc2V0PSIwLjY0ODU1NiIgc3RvcC1jb2xvcj0id2hpdGUiLz4KPC9saW5lYXJHcmFkaWVudD4KPGxpbmVhckdyYWRpZW50IGlkPSJwYWludDFfbGluZWFyXzM3OTVfMjU1NDMiIHgxPSI3Ni4wMTgiIHkxPSI1MS45NTgiIHgyPSI3Ni4wMTgiIHkyPSIxMjAuOTI4IiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+CjxzdG9wIG9mZnNldD0iMC4yNjA3ODQiIHN0b3AtY29sb3I9IiNCNkJBRkYiLz4KPHN0b3Agb2Zmc2V0PSIxIiBzdG9wLWNvbG9yPSIjRTRFMkZGIi8+CjwvbGluZWFyR3JhZGllbnQ+CjwvZGVmcz4KPC9zdmc+Cg=='; readonly supportedTransactionVersions = null; private _connecting: boolean; private _wallet: SlopeWallet | null; private _publicKey: PublicKey | null; private _readyState: WalletReadyState = typeof window === 'undefined' || typeof document === 'undefined' ? WalletReadyState.Unsupported : WalletReadyState.NotDetected; constructor(config: SlopeWalletAdapterConfig = {}) { super(); this._connecting = false; this._wallet = null; this._publicKey = null; if (this._readyState !== WalletReadyState.Unsupported) { scopePollingDetectionStrategy(() => { if (typeof window.Slope === 'function' || window.slopeApp) { this._readyState = WalletReadyState.Installed; this.emit('readyStateChange', this._readyState); return true; } return false; }); } } get publicKey() { return this._publicKey; } get connecting() { return this._connecting; } get readyState() { return this._readyState; } async connect(): Promise { try { if (this.connected || this.connecting) return; if (this._readyState !== WalletReadyState.Installed || typeof window.Slope !== 'function') throw new WalletNotReadyError(); this._connecting = true; const wallet = new window.Slope(); let data: { publicKey?: string | undefined }; try { ({ data } = await wallet.connect()); } catch (error: any) { throw new WalletConnectionError(error?.message, error); } if (!data.publicKey) throw new WalletAccountError(); let publicKey: PublicKey; try { publicKey = new PublicKey(data.publicKey); } catch (error: any) { throw new WalletPublicKeyError(error?.message, error); } this._wallet = wallet; this._publicKey = publicKey; this.emit('connect', publicKey); } catch (error: any) { this.emit('error', error); throw error; } finally { this._connecting = false; } } async disconnect(): Promise { const wallet = this._wallet; if (wallet) { this._wallet = null; this._publicKey = null; try { let msg: string; try { ({ msg } = await wallet.disconnect()); } catch (error: any) { throw new WalletDisconnectionError(error?.message, error); } if (msg !== 'ok') throw new WalletDisconnectionError(msg); } catch (error: any) { this.emit('error', error); } } this.emit('disconnect'); } async signTransaction(transaction: T): Promise { try { const wallet = this._wallet; if (!wallet) throw new WalletNotConnectedError(); try { const message = bs58.encode(transaction.serializeMessage()); const { msg, data } = await wallet.signTransaction(message); if (!data.publicKey || !data.signature) throw new WalletSignTransactionError(msg); const publicKey = new PublicKey(data.publicKey); const signature = bs58.decode(data.signature); transaction.addSignature(publicKey, signature); return transaction; } catch (error: any) { if (error instanceof WalletError) throw error; throw new WalletSignTransactionError(error?.message, error); } } catch (error: any) { this.emit('error', error); throw error; } } async signAllTransactions(transactions: T[]): Promise { try { const wallet = this._wallet; if (!wallet) throw new WalletNotConnectedError(); try { const messages = transactions.map((transaction) => bs58.encode(transaction.serializeMessage())); const { msg, data } = await wallet.signAllTransactions(messages); const length = transactions.length; if (!data.publicKey || data.signatures?.length !== length) throw new WalletSignTransactionError(msg); const publicKey = new PublicKey(data.publicKey); for (let i = 0; i < length; i++) { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion transactions[i]!.addSignature(publicKey, bs58.decode(data.signatures[i]!)); } return transactions; } catch (error: any) { if (error instanceof WalletError) throw error; throw new WalletSignTransactionError(error?.message, error); } } catch (error: any) { this.emit('error', error); throw error; } } async signMessage(message: Uint8Array): Promise { try { const wallet = this._wallet; if (!wallet) throw new WalletNotConnectedError(); try { const response = await wallet.signMessage(message); return bs58.decode(response.data.signature); } catch (error: any) { throw new WalletSignMessageError(error?.message, error); } } catch (error: any) { this.emit('error', error); throw error; } } }