import type { EventEmitter, SendTransactionOptions, WalletName } from '@solana/wallet-adapter-base' import { BaseMessageSignerWalletAdapter, isVersionedTransaction, scopePollingDetectionStrategy, WalletAccountError, WalletConnectionError, WalletDisconnectedError, WalletDisconnectionError, WalletError, WalletNotConnectedError, WalletNotReadyError, WalletPublicKeyError, WalletReadyState, WalletSendTransactionError, WalletSignTransactionError, WalletSignMessageError } from '@solana/wallet-adapter-base' import type { Connection, SendOptions, Transaction, VersionedTransaction, TransactionSignature, TransactionVersion } from '@solana/web3.js' import { PublicKey } from '@solana/web3.js' interface CoinbaseWalletEvents { connect(...args: unknown[]): unknown disconnect(...args: unknown[]): unknown } interface CoinbaseWallet extends EventEmitter { publicKey?: PublicKey signTransaction( transaction: T ): Promise signAllTransactions( transactions: T[] ): Promise signAndSendTransaction( transaction: T, options?: SendOptions ): Promise<{ signature: TransactionSignature }> signMessage(message: Uint8Array): Promise<{ signature: Uint8Array }> connect(): Promise disconnect(): Promise } interface CoinbaseWindow extends Window { coinbaseSolana?: CoinbaseWallet } declare const window: CoinbaseWindow export const BaseWalletName = 'Base Wallet' as WalletName<'Base Wallet'> export class BaseWalletAdapter extends BaseMessageSignerWalletAdapter { name = BaseWalletName url = 'https://chrome.google.com/webstore/detail/coinbase-wallet-extension/hnfanknocfeofbddgcijnmhnfnkdnaad' icon = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiBmaWxsPSJub25lIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8Y2lyY2xlIGN4PSI1MTIiIGN5PSI1MTIiIHI9IjUxMiIgZmlsbD0iIzAwNTJGRiIvPgo8cGF0aCBmaWxsLXJ1bGU9ImV2ZW5vZGQiIGNsaXAtcnVsZT0iZXZlbm9kZCIgZD0iTTE1MiA1MTJDMTUyIDcxMC44MjMgMzEzLjE3NyA4NzIgNTEyIDg3MkM3MTAuODIzIDg3MiA4NzIgNzEwLjgyMyA4NzIgNTEyQzg3MiAzMTMuMTc3IDcxMC44MjMgMTUyIDUxMiAxNTJDMzEzLjE3NyAxNTIgMTUyIDMxMy4xNzcgMTUyIDUxMlpNNDIwIDM5NkM0MDYuNzQ1IDM5NiAzOTYgNDA2Ljc0NSAzOTYgNDIwVjYwNEMzOTYgNjE3LjI1NSA0MDYuNzQ1IDYyOCA0MjAgNjI4SDYwNEM2MTcuMjU1IDYyOCA2MjggNjE3LjI1NSA2MjggNjA0VjQyMEM2MjggNDA2Ljc0NSA2MTcuMjU1IDM5NiA2MDQgMzk2SDQyMFoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=' supportedTransactionVersions: ReadonlySet = new Set([ 'legacy', 0 ]) private _connecting: boolean private _wallet: CoinbaseWallet | null private _publicKey: PublicKey | null private _readyState: WalletReadyState = typeof window === 'undefined' || typeof document === 'undefined' ? WalletReadyState.Unsupported : WalletReadyState.NotDetected constructor() { super() this._connecting = false this._wallet = null this._publicKey = null if (this._readyState !== WalletReadyState.Unsupported) { scopePollingDetectionStrategy(() => { if (window?.coinbaseSolana) { 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) throw new WalletNotReadyError() this._connecting = true const wallet = window.coinbaseSolana! try { await wallet.connect() // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new WalletConnectionError(error?.message, error) } if (!wallet.publicKey) throw new WalletAccountError() let publicKey: PublicKey try { publicKey = new PublicKey(wallet.publicKey.toBytes()) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new WalletPublicKeyError(error?.message, error) } wallet.on('disconnect', this._disconnected) this._wallet = wallet this._publicKey = publicKey this.emit('connect', publicKey) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { this.emit('error', error) throw error } finally { this._connecting = false } } async disconnect(): Promise { const wallet = this._wallet if (wallet) { wallet.off('disconnect', this._disconnected) this._wallet = null this._publicKey = null try { await wallet.disconnect() // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { this.emit('error', new WalletDisconnectionError(error?.message, error)) } } this.emit('disconnect') } override async sendTransaction( transaction: T, connection: Connection, options: SendTransactionOptions = {} ): Promise { try { const wallet = this._wallet if (!wallet) throw new WalletNotConnectedError() try { const { signers, ...sendOptions } = options if (isVersionedTransaction(transaction)) { if (signers?.length) transaction.sign(signers) } else { if (!(transaction as Transaction).recentBlockhash) { transaction = (await this.prepareTransaction( transaction, connection, sendOptions )) as T } if (signers?.length) (transaction as Transaction).partialSign(...signers) } if (!sendOptions.preflightCommitment && connection.commitment) { sendOptions.preflightCommitment = connection.commitment } const { signature } = await wallet.signAndSendTransaction( transaction, sendOptions ) return signature // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { if (error instanceof WalletError) throw error throw new WalletSendTransactionError(error?.message, error) } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { this.emit('error', error) throw error } } async signTransaction( transaction: T ): Promise { try { const wallet = this._wallet if (!wallet) throw new WalletNotConnectedError() try { return ((await wallet.signTransaction(transaction)) as T) || transaction // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new WalletSignTransactionError(error?.message, error) } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { this.emit('error', error) throw error } } override async signAllTransactions< T extends Transaction | VersionedTransaction >(transactions: T[]): Promise { try { const wallet = this._wallet if (!wallet) throw new WalletNotConnectedError() try { return ( ((await wallet.signAllTransactions(transactions)) as T[]) || transactions ) // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new WalletSignTransactionError(error?.message, error) } // eslint-disable-next-line @typescript-eslint/no-explicit-any } 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 { signature } = await wallet.signMessage(message) return signature // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { throw new WalletSignMessageError(error?.message, error) } // eslint-disable-next-line @typescript-eslint/no-explicit-any } catch (error: any) { this.emit('error', error) throw error } } private _disconnected = () => { const wallet = this._wallet if (wallet) { wallet.off('disconnect', this._disconnected) this._wallet = null this._publicKey = null this.emit('error', new WalletDisconnectedError()) this.emit('disconnect') } } }