UNPKG

@web3auth/no-modal

Version:
176 lines (170 loc) 7.58 kB
import _objectSpread from '@babel/runtime/helpers/objectSpread2'; import _defineProperty from '@babel/runtime/helpers/defineProperty'; import { getBase64EncodedWireTransaction, getBase64Decoder, getBase58Encoder, getTransactionDecoder, getBase64Encoder } from '@solana/kit'; import { SolanaSignTransaction, SolanaSignMessage, SolanaSignAndSendTransaction } from '@solana/wallet-standard-features'; import { StandardEvents, StandardDisconnect, StandardConnect } from '@wallet-standard/features'; import { SOLANA_METHOD_TYPES } from '@web3auth/ws-embed'; import { WEB3AUTH_ICON } from '../../base/wallet/index.js'; import { WalletLoginError } from '../../base/errors/index.js'; import { getSolanaChainByChainConfig } from '../../base/wallet/solana.js'; import { SOLANA_CAIP_CHAIN_MAP } from '../../base/constants.js'; const base58Encoder = getBase58Encoder(); const base64Decoder = getBase64Decoder(); const base64Encoder = getBase64Encoder(); const transactionDecoder = getTransactionDecoder(); const ACCOUNT_FEATURES = [SolanaSignAndSendTransaction, SolanaSignMessage, SolanaSignTransaction]; function solanaWalletChainsFromConfigs(solanaChainConfigs) { const ids = solanaChainConfigs.map(getSolanaChainByChainConfig).filter(id => id != null); const unique = [...new Set(ids)]; return unique; } const SOLANA_PROVIDER_HEX_CHAIN_IDS = new Set(Object.keys(SOLANA_CAIP_CHAIN_MAP).map(id => id.toLowerCase())); /** * AuthSolanaWallet implements the Wallet Standard interface, wrapping a JRPC provider. * Used by AuthConnector so consumers get a standards-compliant Wallet from `connection.solanaWallet`. * * {@link Wallet.accounts} is synchronous in the Wallet Standard; it returns `[]` until accounts have been loaded * (`null` internally). The first async operation ({@link StandardConnect}, sign, etc.) calls `ensureAccountsLoaded`, which * requires {@link IProvider.chainId} to be a configured Solana network (see {@link SOLANA_CAIP_CHAIN_MAP}) before {@link SOLANA_METHOD_TYPES.GET_ACCOUNTS}. */ class AuthSolanaWallet { /** * @param solanaChainConfigs - All configured Solana {@link CustomChainConfig} entries (same namespace). */ constructor(_provider, solanaChainConfigs) { this._provider = _provider; _defineProperty(this, "version", "1.0.0"); _defineProperty(this, "name", "Web3Auth"); _defineProperty(this, "icon", WEB3AUTH_ICON); _defineProperty(this, "chains", void 0); _defineProperty(this, "features", void 0); _defineProperty(this, "_accounts", null); _defineProperty(this, "_listeners", {}); this.chains = solanaWalletChainsFromConfigs(solanaChainConfigs); this.features = { [StandardConnect]: { version: "1.0.0", connect: async () => { await this.ensureAccountsLoaded(); return { accounts: this.accounts }; } }, [StandardDisconnect]: { version: "1.0.0", disconnect: async () => {} }, [StandardEvents]: { version: "1.0.0", on: (event, listener) => { var _this$_listeners, _this$_listeners$even; ((_this$_listeners$even = (_this$_listeners = this._listeners)[event]) !== null && _this$_listeners$even !== void 0 ? _this$_listeners$even : _this$_listeners[event] = new Set()).add(listener); return () => this._listeners[event].delete(listener); } }, [SolanaSignAndSendTransaction]: { version: "1.0.0", supportedTransactionVersions: ["legacy", 0], signAndSendTransaction: async (...inputs) => { await this.ensureAccountsLoaded(); return Promise.all(inputs.map(async input => { const base64Tx = base64Decoder.decode(input.transaction); const signature = await this._provider.request({ method: SOLANA_METHOD_TYPES.SEND_TRANSACTION, params: { message: base64Tx } }); return { signature: new Uint8Array(base58Encoder.encode(signature)) }; })); } }, [SolanaSignMessage]: { version: "1.0.0", signMessage: async (...inputs) => { await this.ensureAccountsLoaded(); return Promise.all(inputs.map(async input => { const message = new TextDecoder().decode(input.message); const signature = await this._provider.request({ method: SOLANA_METHOD_TYPES.SIGN_MESSAGE, params: { data: message, from: input.account.address } }); return { signedMessage: new Uint8Array(input.message), signature: new Uint8Array(base58Encoder.encode(signature)) }; })); } }, [SolanaSignTransaction]: { version: "1.0.0", supportedTransactionVersions: ["legacy", 0], signTransaction: async (...inputs) => { await this.ensureAccountsLoaded(); return Promise.all(inputs.map(async input => { const base64Tx = base64Decoder.decode(input.transaction); const signatureBase58 = await this._provider.request({ method: SOLANA_METHOD_TYPES.SIGN_TRANSACTION, params: { message: base64Tx } }); const sigBytes = new Uint8Array(base58Encoder.encode(signatureBase58)); const decodedTx = transactionDecoder.decode(input.transaction); const signedTx = _objectSpread(_objectSpread({}, decodedTx), {}, { signatures: _objectSpread(_objectSpread({}, decodedTx.signatures), {}, { [input.account.address]: sigBytes }) }); const signedBase64 = getBase64EncodedWireTransaction(signedTx); return { signedTransaction: new Uint8Array(base64Encoder.encode(signedBase64)) }; })); } } }; } /** * Wallet Standard requires a synchronous getter; RPC runs on first async wallet operation instead. */ get accounts() { var _this$_accounts; return (_this$_accounts = this._accounts) !== null && _this$_accounts !== void 0 ? _this$_accounts : []; } /** Throws if the embed is not on Solana; otherwise loads accounts once via {@link SOLANA_METHOD_TYPES.GET_ACCOUNTS}. */ async ensureAccountsLoaded() { var _await$this$_provider; // assert solana chain const chainId = this._provider.chainId; if (!SOLANA_PROVIDER_HEX_CHAIN_IDS.has(chainId.toLowerCase())) throw WalletLoginError.unsupportedOperation(`Solana wallet operations require the embedded provider to be on a Solana network (current chainId: ${chainId}). Switch chain first.`); if (this._accounts !== null) return; const addresses = (_await$this$_provider = await this._provider.request({ method: SOLANA_METHOD_TYPES.GET_ACCOUNTS })) !== null && _await$this$_provider !== void 0 ? _await$this$_provider : []; const accountChains = this.chains; this._accounts = addresses.map(address => ({ address, publicKey: new Uint8Array(base58Encoder.encode(address)), chains: accountChains.length ? accountChains : [], features: ACCOUNT_FEATURES })); this.emitChange({ accounts: this.accounts }); } emitChange(properties) { const listeners = this._listeners.change; if (!listeners) return; listeners.forEach(listener => { listener(properties); }); } } export { AuthSolanaWallet };