@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
159 lines (151 loc) • 7.05 kB
JavaScript
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 };