UNPKG

@web3auth/no-modal

Version:
159 lines (151 loc) 7.05 kB
import { createWalletStandardConnector, createClient } from '@solana/client'; import { defineComponent, ref, provide, watch, onBeforeUnmount, h, Fragment } from 'vue'; import { CHAIN_NAMESPACES } from '@toruslabs/base-controllers'; import { SOLANA_CLIENT_KEY } from './constants.js'; import { useWeb3Auth } from '../composables/useWeb3Auth.js'; import { useChain } from '../composables/useChain.js'; import { log } from '../../base/loglevel.js'; import { getCaipChainId } from '../../base/utils.js'; const disposeClient = async client => { client.destroy(); }; const resolveSolanaChain = (web3Auth, connection) => { var _web3Auth$coreOptions2; const currentChain = web3Auth === null || web3Auth === void 0 ? void 0 : web3Auth.currentChain; if ((currentChain === null || currentChain === void 0 ? void 0 : currentChain.chainNamespace) === CHAIN_NAMESPACES.SOLANA) { return currentChain; } const connectedScope = connection !== null && connection !== void 0 && connection.solanaWallet && "scope" in connection.solanaWallet && typeof connection.solanaWallet.scope === "string" ? connection.solanaWallet.scope : null; if (connectedScope) { var _web3Auth$coreOptions; const connectedChain = web3Auth === null || web3Auth === void 0 || (_web3Auth$coreOptions = web3Auth.coreOptions.chains) === null || _web3Auth$coreOptions === void 0 ? void 0 : _web3Auth$coreOptions.find(chain => { return chain.chainNamespace === CHAIN_NAMESPACES.SOLANA && getCaipChainId(chain) === connectedScope; }); if (connectedChain) return connectedChain; } return (web3Auth === null || web3Auth === void 0 || (_web3Auth$coreOptions2 = web3Auth.coreOptions.chains) === null || _web3Auth$coreOptions2 === void 0 ? void 0 : _web3Auth$coreOptions2.find(chain => chain.chainNamespace === CHAIN_NAMESPACES.SOLANA)) || null; }; /** * Syncs Web3Auth Solana connection with Framework Kit client. * For multichain wallets, keep the Solana client warm across namespace switches so * switching back to Solana can reuse the existing wallet session. */ const SolanaProvider = defineComponent({ name: "SolanaProvider", setup(_, { slots }) { const { isConnected, connection, web3Auth } = useWeb3Auth(); const { chainId } = useChain(); const clientRef = ref(null); let connectedClient = null; // let connectedClientKey: string | null = null; // Holds the token for the newest requested sync run. Older async runs compare against it // before publishing results so a slower reconnect cannot overwrite a newer chain/account update. let activeSyncToken = null; // provide the client to the app provide(SOLANA_CLIENT_KEY, clientRef); const syncClient = async () => { var _web3Auth$value, _web3Auth$value2; // Only the latest async, `syncing` run should be allowed to attach its client. // A fresh Symbol gives each run a unique identity without relying on counters. const syncToken = Symbol("solana-client-sync"); activeSyncToken = syncToken; const newIsConnected = isConnected.value; const newConnection = connection.value; const currentChain = (_web3Auth$value = web3Auth.value) === null || _web3Auth$value === void 0 ? void 0 : _web3Auth$value.currentChain; if ((currentChain === null || currentChain === void 0 ? void 0 : currentChain.chainNamespace) !== CHAIN_NAMESPACES.SOLANA) { // Mirror the React provider behavior: hide the live Solana client from injected // consumers whenever Web3Auth is currently scoped to a non-Solana namespace. clientRef.value = null; return; } const preferredSolanaChain = resolveSolanaChain(web3Auth.value, newConnection); const shouldKeepSolanaClient = newIsConnected && Boolean(newConnection === null || newConnection === void 0 ? void 0 : newConnection.solanaWallet) && Boolean(preferredSolanaChain) && // only manage the client for the primary connector (newConnection === null || newConnection === void 0 ? void 0 : newConnection.connectorName) === ((_web3Auth$value2 = web3Auth.value) === null || _web3Auth$value2 === void 0 ? void 0 : _web3Auth$value2.primaryConnectorName); if (!shouldKeepSolanaClient) { clientRef.value = null; const prevClient = connectedClient; connectedClient = null; // connectedClientKey = null; if (prevClient) { await disposeClient(prevClient); } return; } let client = null; try { // create a wallet standard connector from connected wallet const solanaWalletId = "wallet-standard:" + newConnection.connectorName; const connector = createWalletStandardConnector(newConnection.solanaWallet, { id: solanaWalletId, name: newConnection.connectorName }); // create a solana client const { rpcTarget, wsTarget } = preferredSolanaChain; client = createClient({ endpoint: rpcTarget, websocketEndpoint: wsTarget, walletConnectors: [connector] }); // connect the client to the wallet await client.actions.connectWallet(solanaWalletId, { autoConnect: true }); // If another sync started while connectWallet was in flight, this client is stale. if (activeSyncToken !== syncToken) { await disposeClient(client); return; } const prevClient = connectedClient; connectedClient = client; // connectedClientKey = nextClientKey; clientRef.value = (currentChain === null || currentChain === void 0 ? void 0 : currentChain.chainNamespace) === CHAIN_NAMESPACES.SOLANA ? client : null; if (prevClient) { await disposeClient(prevClient); } } catch (err) { if (client) { await disposeClient(client); } log.error("Failed to create or connect Solana client", err); // Only clear the shared ref when this failing run is still the newest one. if (activeSyncToken === syncToken) { clientRef.value = null; } } }; // watch for changes in the connection and active chain watch([isConnected, connection, chainId], () => { void syncClient(); }, { immediate: true }); onBeforeUnmount(() => { const publishedClient = clientRef.value; clientRef.value = null; const prevClient = connectedClient; connectedClient = null; if (prevClient) { void disposeClient(prevClient); } else if (publishedClient) { void disposeClient(publishedClient); } }); return () => { var _slots$default, _slots$default2; return h(Fragment, null, (_slots$default = (_slots$default2 = slots.default) === null || _slots$default2 === void 0 ? void 0 : _slots$default2.call(slots)) !== null && _slots$default !== void 0 ? _slots$default : []); }; } }); export { SolanaProvider };