import type { SendTransactionOptions, TransactionOrVersionedTransaction, WalletName, } from "@solana/wallet-adapter-base"; import { WalletAdapterNetwork } from "@solana/wallet-adapter-base"; import { BaseSignInMessageSignerWalletAdapter, WalletConfigError, WalletConnectionError, WalletDisconnectionError, WalletNotConnectedError, WalletNotReadyError, WalletPublicKeyError, WalletReadyState, WalletSignInError, WalletSignMessageError, WalletSignTransactionError, } from "@solana/wallet-adapter-base"; import type { Connection, Transaction, TransactionSignature, TransactionVersion, VersionedTransaction, } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js"; import { TipLinkEmbed } from "./embed.js"; import { TIPLINK_BUILD_ENV, type TIPLINK_BUILD_ENV_TYPE, } from "./interfaces.js"; import type { SolanaSignInInput, SolanaSignInOutput, } from "@solana/wallet-standard-features"; import { checkAndAttachTipLinkInstance, getTipLinkUrl, iFrameUrl, removePreviousWindowRef, TipLinkInstanceKey, } from "./utils.js"; import { SOLANA_MAINNET_CHAIN } from "@solana/wallet-standard-chains"; import { registerWalletAdapter } from "./wallet-standard.js"; import { showDialog } from "./dialog.js"; import { v4 as uuid } from "uuid"; import { Buffer } from "buffer"; export type { TIPLINK_BUILD_ENV_TYPE }; interface TipLinkWindow extends Window {} declare const window: TipLinkWindow; export enum EmbeddedWalletPage { OVERVIEW = "Overview", ADD_FUNDS = "AddFunds", SWAP = "Swap", WITHDRAW = "Withdraw", } export const GoogleViaTipLinkWalletName = "Google via TipLink" as WalletName<"Google via TipLink">; export type TipLinkWalletAdapterTheme = "system" | "light" | "dark"; export const NPM_VERSION = "2.1.21"; export type CustomSolanaSignInInput = | SolanaSignInInput | (() => Promise); type ConnectOutput = { siwsOutput?: SolanaSignInOutput; }; type WalletAdapterConfig = { // Reach out to the TipLink team for a clientId clientId: string; title: string; theme: TipLinkWalletAdapterTheme; installedOnDesktop?: boolean; installedOnIos?: boolean; installedOnAndroid?: boolean; hideDraggableWidget?: boolean; hideWalletOnboard?: boolean; walletAdapterNetwork?: | WalletAdapterNetwork.Mainnet | WalletAdapterNetwork.Devnet; }; // preload for iframe doesn't work https://bugs.chromium.org/p/chromium/issues/detail?id=593267 async function preLoadIframe( buildEnv: TIPLINK_BUILD_ENV_TYPE, clientId: string, walletAdapterNetwork: | WalletAdapterNetwork.Mainnet | WalletAdapterNetwork.Devnet, theme?: TipLinkWalletAdapterTheme ) { try { if (typeof document === "undefined" || typeof window === "undefined") { return; } const tipLinkIframeHtml = document.createElement("link"); const tipLinkUrl = iFrameUrl({ buildEnv, clientId, walletAdapterNetwork, theme: theme !== "system" ? theme : undefined, }); tipLinkIframeHtml.href = tipLinkUrl; tipLinkIframeHtml.crossOrigin = "anonymous"; tipLinkIframeHtml.type = "text/html"; tipLinkIframeHtml.rel = "prefetch"; if (tipLinkIframeHtml.relList && tipLinkIframeHtml.relList.supports) { if (tipLinkIframeHtml.relList.supports("prefetch")) { document.head.appendChild(tipLinkIframeHtml); } } } catch (error) { console.warn(error); } } // This will register the TipLink Wallet as a standard wallet (as part of the wallet standard). // It can be called outside of a React component. export const registerTipLinkWallet = ({ title, clientId, theme, rpcUrl, installedOnAndroid, installedOnDesktop, installedOnIos, walletAdapterNetwork, }: WalletAdapterConfig & { rpcUrl: string; }) => { if (typeof window === "undefined") { return () => { return; }; } return registerWalletAdapter( new TipLinkWalletAdapter({ clientId, theme, title, // buildEnv: TIPLINK_BUILD_ENV.PRODUCTION, installedOnAndroid, installedOnDesktop, installedOnIos, walletAdapterNetwork, }), SOLANA_MAINNET_CHAIN, rpcUrl ); }; function sanitizeUrlForAllowList(urlString: string): string { const url = new URL(urlString); const { protocol, hostname, port } = url; return `${protocol}//${hostname}${port ? `:${port}` : ""}`; } async function checkIfAllowListed( buildEnv: TIPLINK_BUILD_ENV_TYPE, clientId: string, url: string, failCallback: () => void ) { const referrerUrl = sanitizeUrlForAllowList(url); const data = JSON.stringify({ clientId, referrerUrl }); const b64Referrer = Buffer.from(data).toString("base64"); const configUrl = `${getTipLinkUrl( buildEnv )}/api/wallet_adapter_ancestors/${b64Referrer}`; const response = await fetch(configUrl); const { ancestor } = (await response.json()) as { ancestor: string; }; if (response.ok && response.status === 200 && ancestor) { return; } failCallback(); } function isPWA() { return ( // @ts-ignore globalThis.navigator?.standalone === true || globalThis.matchMedia?.("(display-mode: standalone)").matches || globalThis.matchMedia?.("(display-mode: fullscreen)").matches || globalThis.matchMedia?.("(display-mode: minimal-ui)").matches ); } let _userAgent: string | null | undefined; function getUserAgent(): string | null { if (_userAgent === undefined) { _userAgent = globalThis.navigator?.userAgent ?? null; } return _userAgent; } function isWebView() { const userAgentString = getUserAgent(); if (!userAgentString) { return false; } return /(WebView|Version\/.+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+)|; wv\).+(Chrome)\/(\d+)\.(\d+)\.(\d+)\.(\d+))/i.test( userAgentString ); } function isMobileAndroid(): boolean { const userAgentString = getUserAgent(); if (userAgentString && /android/i.test(userAgentString) && !isWebView()) { return true; } return false; } function isMobileiOS(): boolean { const userAgentString = getUserAgent(); if ( userAgentString && /iPad|iPhone|iPod/.test(navigator.userAgent) && !isWebView() ) { return true; } return false; } const wvRules = ["WebView", "(iPhone|iPod|iPad)(?!.*Safari/)", "Android.*(wv)"]; const wvRegex = new RegExp(`(${wvRules.join("|")})`, "ig"); let _isInApp: boolean | undefined; function isInApp(): boolean { if (_isInApp !== undefined) { return _isInApp; } const userAgentString = getUserAgent(); if (!userAgentString) { return false; } _isInApp = Boolean(userAgentString.match(wvRegex)); return _isInApp; } let _isPhantomBrowser: boolean | undefined; function isPhantomBrowser(): boolean { if (_isPhantomBrowser !== undefined) { return _isPhantomBrowser; } const userAgentString = getUserAgent(); if (!userAgentString) { return false; } _isPhantomBrowser = userAgentString.includes("Phantom") && isInApp(); return _isPhantomBrowser; } function installedToWalletReadyState(installed: boolean): WalletReadyState { return installed ? WalletReadyState.Installed : WalletReadyState.Loadable; } export class TipLinkWalletAdapter extends BaseSignInMessageSignerWalletAdapter { name = GoogleViaTipLinkWalletName; url = "https://tiplink.io"; icon = "data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTIwIiBoZWlnaHQ9IjEyMCIgdmlld0JveD0iMCAwIDEyMCAxMjAiIGZpbGw9Im5vbmUiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+CjxwYXRoIGQ9Ik02MCA4MEM2MCA2OC45NTQzIDY4Ljk1NDMgNjAgODAgNjBIMTAwQzExMS4wNDYgNjAgMTIwIDY4Ljk1NDMgMTIwIDgwVjEwMEMxMjAgMTExLjA0NiAxMTEuMDQ2IDEyMCAxMDAgMTIwSDgwQzY4Ljk1NDMgMTIwIDYwIDExMS4wNDYgNjAgMTAwVjgwWiIgZmlsbD0iI0Y1RjdGOCIvPgo8cGF0aCBkPSJNMjAgMEM4Ljk1NDMxIDAgMCA4Ljk1NDMgMCAyMFY4MEMwIDkxLjA0NTcgOC45NTQzIDEwMCAyMCAxMDBINTBWODBDNTAgNjMuNDMxNSA2My40MzE1IDUwIDgwIDUwSDEwMFYyMEMxMDAgOC45NTQzMSA5MS4wNDU3IDAgODAgMEgyMFoiIGZpbGw9IiMwMDdDQkYiLz4KPHBhdGggZD0iTTM1LjI1OCAzOC45ODg2QzMwLjkxNDIgMzQuNjQ0NyAzMC45MTQyIDI3LjYwMTggMzUuMjU4IDIzLjI1NzlDMzkuNjAxOSAxOC45MTQgNDYuNjQ0OCAxOC45MTQgNTAuOTg4NyAyMy4yNTc5TDY5Ljg2NTUgNDIuMTM0N0M3Mi4yNDA2IDQ0LjUwOTkgNzMuMzE3MSA0Ny42OTIgNzMuMDk0OSA1MC43OTg1QzcxLjQxNDUgNTEuMTk0NCA2OS43ODg3IDUxLjczMTUgNjguMjMwNyA1Mi4zOTY2QzY5LjE0NjkgNTAuMDEwNyA2OC42NDMxIDQ3LjIwNDYgNjYuNzE5MyA0NS4yODA4TDQ3Ljg0MjYgMjYuNDA0QzQ1LjIzNjIgMjMuNzk3NyA0MS4wMTA1IDIzLjc5NzcgMzguNDA0MiAyNi40MDRDMzUuNzk3OCAyOS4wMTA0IDM1Ljc5NzggMzMuMjM2MSAzOC40MDQyIDM1Ljg0MjRMNDcuNTIwNCA0NC45NTg3TDQ0LjM3NDMgNDguMTA0OEwzNS4yNTggMzguOTg4NloiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik02MS4xOTM0IDU2LjYyNTNDNTkuNzYzMyA1Ni40NTIyIDU4LjM3ODUgNTUuODE2OCA1Ny4yODA5IDU0LjcxOTJMNTQuNDEzNiA1MS44NTE4TDUxLjI2NzQgNTQuOTk3OUw1NC4xMzQ4IDU3Ljg2NTNDNTUuMTMyIDU4Ljg2MjUgNTYuMjcxNCA1OS42MzA4IDU3LjQ4NzggNjAuMTcwMkM1OC42MTk2IDU4Ljg4NjIgNTkuODU5NiA1Ny42OTk4IDYxLjE5MzQgNTYuNjI1M1oiIGZpbGw9IndoaXRlIi8+CjxwYXRoIGQ9Ik01MS44NTk0IDY5LjU3OTJMNDcuODQyNiA3My41OTZDNDUuMjM2MiA3Ni4yMDIzIDQxLjAxMDUgNzYuMjAyMyAzOC40MDQyIDczLjU5NkMzNS43OTc4IDcwLjk4OTYgMzUuNzk3OCA2Ni43NjM5IDM4LjQwNDIgNjQuMTU3Nkw1Ny4yODA5IDQ1LjI4MDhDNTguMjA2MyA0NC4zNTU1IDU5LjMzNTcgNDMuNzU4NyA2MC41MjQzIDQzLjQ5MDRMNTcuMDYzMSA0MC4wMjkxQzU2LjAwNjEgNDAuNTUyNiA1NS4wMTUgNDEuMjU0NSA1NC4xMzQ4IDQyLjEzNDdMNDkuNDE1NiA0Ni44NTM5TDQ5LjQwODEgNDYuODQ2NEw0Ni4yNjIgNDkuOTkyNUw0Ni4yNjk1IDUwTDM1LjI1OCA2MS4wMTE1QzMwLjkxNDEgNjUuMzU1NCAzMC45MTQxIDcyLjM5ODIgMzUuMjU4IDc2Ljc0MjFDMzkuMzE2NiA4MC44MDA3IDQ1LjczMTIgODEuMDY3MyA1MC4wOTkzIDc3LjU0MTlDNTAuMzI0NiA3NC43NjM0IDUwLjkyODUgNzIuMDkxOSA1MS44NTk0IDY5LjU3OTJaIiBmaWxsPSJ3aGl0ZSIvPgo8cGF0aCBkPSJNMTA5LjE1IDkwLjQ1QzEwOS4xNSA4OS4xMzMzIDEwOS4wMzMgODcuODgzMyAxMDguODMzIDg2LjY2NjdIOTBWOTQuMTgzM0gxMDAuNzgzQzEwMC4zIDk2LjY1IDk4Ljg4MzMgOTguNzMzMyA5Ni43ODMzIDEwMC4xNVYxMDUuMTVIMTAzLjIxN0MxMDYuOTgzIDEwMS42NjcgMTA5LjE1IDk2LjUzMzMgMTA5LjE1IDkwLjQ1WiIgZmlsbD0iIzQyODVGNCIvPgo8cGF0aCBkPSJNOTAuMDAwMSAxMTBDOTUuNDAwMSAxMTAgOTkuOTE2NyAxMDguMiAxMDMuMjE3IDEwNS4xNUw5Ni43ODMzIDEwMC4xNUM5NC45ODMzIDEwMS4zNSA5Mi43MDAxIDEwMi4wODMgOTAuMDAwMSAxMDIuMDgzQzg0Ljc4MzQgMTAyLjA4MyA4MC4zNjY3IDk4LjU2NjcgNzguNzgzNCA5My44MTY3SDcyLjE1MDFWOTguOTY2N0M3NS40MzM0IDEwNS41IDgyLjE4MzQgMTEwIDkwLjAwMDEgMTEwWiIgZmlsbD0iIzM0QTg1MyIvPgo8cGF0aCBkPSJNNzguNzgzNCA5My44MTY3Qzc4LjM2NjcgOTIuNjE2NyA3OC4xNSA5MS4zMzMzIDc4LjE1IDkwQzc4LjE1IDg4LjY2NjcgNzguMzgzMyA4Ny4zODMzIDc4Ljc4MzMgODYuMTgzM1Y4MS4wMzMzSDcyLjE1QzcwLjc4MzMgODMuNzMzMyA3MCA4Ni43NjY3IDcwIDkwQzcwIDkzLjIzMzMgNzAuNzgzNCA5Ni4yNjY3IDcyLjE1MDEgOTguOTY2N0w3OC43ODM0IDkzLjgxNjdaIiBmaWxsPSIjRkJCQzA1Ii8+CjxwYXRoIGQ9Ik05MC4wMDAxIDc3LjkxNjdDOTIuOTUwMSA3Ny45MTY3IDk1LjU4MzQgNzguOTMzMyA5Ny42NjY3IDgwLjkxNjdMMTAzLjM2NyA3NS4yMTY3Qzk5LjkxNjcgNzEuOTgzMyA5NS40MDAxIDcwIDkwLjAwMDEgNzBDODIuMTgzNCA3MCA3NS40MzMzIDc0LjUgNzIuMTUgODEuMDMzM0w3OC43ODMzIDg2LjE4MzNDODAuMzY2NyA4MS40MzMzIDg0Ljc4MzQgNzcuOTE2NyA5MC4wMDAxIDc3LjkxNjdaIiBmaWxsPSIjRUE0MzM1Ii8+Cjwvc3ZnPgo="; readonly supportedTransactionVersions = new Set([ "legacy" as TransactionVersion, 0 as TransactionVersion, ]); private _connecting: boolean; private _disconnected: boolean; private _wallet: TipLinkEmbed | null; private _publicKey: PublicKey | null; private _iden: number; private _buildEnv: TIPLINK_BUILD_ENV_TYPE; private _directConnect = true; private _title: string; private _theme: TipLinkWalletAdapterTheme; private _clientId: string; private _isDisallowed = false; private _installedOnDesktop: boolean; private _installedOnAndroid: boolean; private _installedOnIos: boolean; private readonly _forceIframe: boolean; private readonly dAppSessionId: string; private _hideDraggableWidget: boolean; private _hideWalletOnboard: boolean; private _walletAdapterNetwork: | WalletAdapterNetwork.Mainnet | WalletAdapterNetwork.Devnet; private _showWallet: ((page?: EmbeddedWalletPage) => void) | undefined; private _hideWallet: (() => void) | undefined; constructor({ theme, title, clientId, installedOnDesktop = true, installedOnIos = true, installedOnAndroid = false, hideDraggableWidget = false, hideWalletOnboard = false, walletAdapterNetwork = WalletAdapterNetwork.Mainnet, }: WalletAdapterConfig) { super(); this._buildEnv = TIPLINK_BUILD_ENV.PRODUCTION; // TODO: only allow TIPLINK_BUILD_ENV.PRODUCTION; if (typeof window !== "undefined") { void checkIfAllowListed( this._buildEnv, clientId, window.location.origin, () => { this._isDisallowed = true; this._wallet?.notifyDisallowed(); this.disconnect(); } ); } this._forceIframe = (typeof document !== "undefined" && document.referrer === "https://t.co/" && typeof navigator !== "undefined" && isMobileiOS()) || isPWA(); this.dAppSessionId = uuid(); preLoadIframe(this._buildEnv, clientId, walletAdapterNetwork, theme); this._title = title; this._connecting = false; this._disconnected = false; this._wallet = null; this._publicKey = null; // console.log("TorusParams: ", params); // this._params = params; this._iden = Date.now(); this._theme = theme; this._clientId = clientId; this._installedOnDesktop = installedOnDesktop; this._installedOnAndroid = installedOnAndroid; this._installedOnIos = installedOnIos; this._hideDraggableWidget = hideDraggableWidget; this._hideWalletOnboard = hideWalletOnboard; this._walletAdapterNetwork = walletAdapterNetwork; checkAndAttachTipLinkInstance(this); } get publicKey() { return this._publicKey; } get connecting() { return this._connecting; } get connected() { return !!this._wallet?.isLoggedIn; } get readyState() { const isAndroid = isMobileAndroid(); const isiOS = isMobileiOS(); const isPhantom = isPhantomBrowser(); const isInstalled = isPhantom ? false : isAndroid ? this._installedOnAndroid : isiOS ? this._installedOnIos : this._installedOnDesktop; return typeof window === "undefined" || typeof document === "undefined" ? WalletReadyState.Unsupported : installedToWalletReadyState(isInstalled); } private _accountChanged = (newPublicKeyString: string) => { // console.log( // "wallet adapter says account changed!!", // newPublicKeyString, // this._publicKey // ); const publicKey = this._publicKey; if (!publicKey) return; if (publicKey.toBase58() === newPublicKeyString) return; let newPublicKey; try { newPublicKey = new PublicKey(newPublicKeyString); } catch (error: any) { this.emit("error", new WalletPublicKeyError(error?.message, error)); return; } this._publicKey = newPublicKey; this.emit("connect", newPublicKey); }; private removeQueryParam(key: string): void { const url = new URL(window.location.href); if (url.searchParams.has(key)) { url.searchParams.delete(key); // console.log("url.toString()", url.toString()); window.history.replaceState({}, "", url.toString()); } } private async _connect({ forceNoDirectConnect, siwsInput, autoConnect = false, }: { forceNoDirectConnect?: boolean; siwsInput?: CustomSolanaSignInInput; autoConnect?: boolean; }): Promise { // console.log( // "BEGIN: connecting status for iden", // this.connecting, // this._iden // ); if (isInApp()) { // console.log("is in app, returning"); this.disconnect(); showDialog( this._buildEnv, "

The TipLink Wallet is not supported in this browser. Please open this page in your default browser instead.

" ); return Promise.reject( "The TipLink Wallet is not supported in this browser." ); } if (this.connected || this.connecting) return; const previouslyConnectedPublicKey = localStorage.getItem( "tipLink_pk_connected" ); if (previouslyConnectedPublicKey || this._forceIframe) { forceNoDirectConnect = true; autoConnect = true; } let solanaSignInOutput: SolanaSignInOutput | undefined; this._disconnected = false; try { if ( this.readyState !== WalletReadyState.Installed && this.readyState !== WalletReadyState.Loadable ) { throw new WalletNotReadyError(); } // console.log("adapter.connect, importing Torus"); // console.log("setting connecting to be true"); this._connecting = true; // let TorusClass: typeof Torus; // try { // // console.log("importing torus"); // TorusClass = (await import("./embed.js")).Torus; // } catch (error: any) { // throw new WalletLoadError(error?.message, error); // } let wallet: TipLinkEmbed; // console.log("checking object on window during connect", window.tipLink); try { wallet = new TipLinkEmbed( this._title, this._buildEnv, this._clientId, this._forceIframe, this.dAppSessionId, this._walletAdapterNetwork, () => { return this._isDisallowed; } ); } catch (error: any) { throw new WalletConfigError(error?.message, error); } // console.log("adapter.connect, initializing wallet"); // add wallet ref here to unmount even if user cancels flow this._wallet = wallet; let publicKey: PublicKey; try { const { pk, siwsOutput } = await wallet.init({ directConnect: forceNoDirectConnect ? false : this._directConnect, autoConnect, siwsInput, forceClickToContinue: this._forceIframe, theme: this._theme, hideDraggableWidget: this._hideDraggableWidget, hideWalletOnboard: this._hideWalletOnboard, onWalletHandshake: (methods: { showWallet: (page?: EmbeddedWalletPage) => void; hideWallet: () => void; }) => { this._showWallet = methods.showWallet; this._hideWallet = methods.hideWallet; }, }); publicKey = new PublicKey(pk); solanaSignInOutput = siwsOutput; // console.log( // "DONE WALLET INIT!! connecting status right now is", // this._connecting, // "disconnected", // this._disconnected, // "public key", // pk, // "siws output", // siwsOutput // ); } catch (error: any) { await this.disconnect(); throw new WalletConnectionError(error?.message, error); } if (this._disconnected) { return; } if ( previouslyConnectedPublicKey && previouslyConnectedPublicKey !== publicKey.toBase58() ) { this.disconnect(); return; } this.removeQueryParam("tipLinkAutoConnect"); this.removeQueryParam("tipLinkAutoConnectPublicKey"); this.removeQueryParam("promptTipLinkAutoConnect"); wallet.on("accountChanged", this._accountChanged); this._publicKey = publicKey; localStorage.setItem("tipLink_pk_connected", publicKey.toBase58()); this.emit("connect", publicKey); // console.log("FINISHED connecting"); // console.log("adapter.connect, connected"); } catch (error: any) { // console.error("ERROR: error was", error); this.emit("error", error); throw error; } finally { // console.log("END: setting connecting to be false", this._iden); this._connecting = false; } return { siwsOutput: solanaSignInOutput, }; } async autoConnect(): Promise { await this._connect({ forceNoDirectConnect: true, autoConnect: true, }); return; } async connect(): Promise { await this._connect({}); return; } async disconnect(): Promise { const wallet = this._wallet; this._connecting = false; // console.log("triggered disconnected!"); this._disconnected = true; // console.log("wallet", wallet); if (wallet) { this._wallet = null; this._publicKey = null; wallet.off("accountChanged", this._accountChanged); // console.log("is wallet logged in", wallet.isLoggedIn); try { if (wallet.isLoggedIn) { await wallet.cleanUp(); } else { wallet.clearElements(); } removePreviousWindowRef(TipLinkInstanceKey.ADAPTER); } catch (error: any) { this.emit("error", new WalletDisconnectionError(error?.message, error)); } } this.emit("disconnect"); } async signTransaction( transaction: T ): Promise { try { const wallet = this._wallet; if (!wallet || !this.connected) throw new WalletNotConnectedError(); // console.log("singing transaction in wallet adapter!!"); try { return ( ((await wallet.signTransaction(transaction)) as T) || transaction ); } catch (error: any) { 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 || !this.connected) throw new WalletNotConnectedError(); try { return ( ((await wallet.signAllTransactions(transactions)) as T[]) || transactions ); } catch (error: any) { 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 || !this.connected) throw new WalletNotConnectedError(); try { const { signature } = await wallet.signMessage(message); return signature; } catch (error: any) { throw new WalletSignMessageError(error?.message, error); } } catch (error: any) { this.emit("error", error); throw error; } } async sendTransaction( transaction: TransactionOrVersionedTransaction< this["supportedTransactionVersions"] >, connection: Connection, options: SendTransactionOptions = {} ): Promise { try { const wallet = this._wallet; if (!wallet || !this.connected) throw new WalletNotConnectedError(); return wallet.sendTransaction( transaction, this.prepareTransaction.bind(this), connection, options ); } catch (error: any) { // This logic differs from the default `sendTransaction` method in BaseSignerWalletAdapter. // In the default implementation, they don't emit an error if the error is of `WalletSignTransactionError` type // because that error is thrown by `signTransaction` which already emits an error. // However, we emit the error here always because the `wallet.sendTransaction` above will not emit an error // in the sign phase, since it goes through the `wallet` object, and not our default `signTransaction` method above. this.emit("error", error); throw error; } } async signIn(input?: CustomSolanaSignInInput): Promise { // console.log("triggering sign in!"); try { if (!this.connected) { const output = await this._connect({ siwsInput: input, }); const siwsOutput = output?.siwsOutput; if (input) { if (!siwsOutput) { throw new Error("No Solana Sign In Output"); } return siwsOutput; } } const wallet = this._wallet; if (!wallet || !this.connected) throw new WalletNotConnectedError(); const publicKey = this._publicKey; if (!publicKey) throw new WalletNotConnectedError("no public key found"); try { const siwsInput = typeof input === "function" ? input() : input ? Promise.resolve(input) : undefined; const siwsOutput = await wallet.signIn(siwsInput); return siwsOutput; } catch (error: any) { throw new WalletSignInError(error?.message, error); } } catch (error: any) { this.emit("error", error); throw error; } } public showWallet(page?: EmbeddedWalletPage) { if (this._showWallet) { this._showWallet(page); } else { console.error( `TipLinkWalletAdapter Error: "showWallet" method not found, please refresh or try again.` ); } } public hideWallet() { if (this._hideWallet) { this._hideWallet(); } else { console.error( `TipLinkWalletAdapter Error: "hideWallet" method not found, please refresh or try again.` ); } } }