UNPKG

@web3modal/base

Version:

#### 🔗 [Website](https://web3modal.com)

236 lines • 9.9 kB
import UniversalProvider from '@walletconnect/universal-provider'; import { SolConstantsUtil } from '@web3modal/scaffold-utils/solana'; import { ProviderEventEmitter } from './shared/ProviderEventEmitter.js'; import base58 from 'bs58'; import { Connection, PublicKey, Transaction, VersionedTransaction } from '@solana/web3.js'; import { isVersionedTransaction } from '@solana/wallet-adapter-base'; import { withSolanaNamespace } from '../utils/withSolanaNamespace.js'; import { WalletConnectMethodNotSupportedError } from './shared/Errors.js'; export class WalletConnectProvider extends ProviderEventEmitter { constructor({ provider, chains, getActiveChain }) { super(); this.name = 'WalletConnect'; this.type = 'WALLET_CONNECT'; this.icon = 'https://imagedelivery.net/_aTEfDRm7z3tKgu9JhfeKA/05338e12-4f75-4982-4e8a-83c67b826b00/md'; this.requestedChains = chains; this.provider = provider; this.getActiveChain = getActiveChain; } get chains() { return this.sessionChains .map(sessionChainId => { let chainId = sessionChainId; if (chainId === SolConstantsUtil.CHAIN_IDS.Deprecated_Mainnet) { chainId = SolConstantsUtil.CHAIN_IDS.Mainnet; } else if (chainId === SolConstantsUtil.CHAIN_IDS.Deprecated_Devnet) { chainId = SolConstantsUtil.CHAIN_IDS.Devnet; } return this.requestedChains.find(chain => withSolanaNamespace(chain.chainId) === chainId); }) .filter(Boolean); } get publicKey() { const account = this.getAccount(false); if (account) { return new PublicKey(account.publicKey); } return undefined; } async connect() { const rpcMap = this.requestedChains.reduce((acc, chain) => { acc[withSolanaNamespace(chain.chainId)] = chain.rpcUrl; return acc; }, {}); if (this.provider.session?.namespaces['solana']) { this.session = this.provider.session; } else { this.provider.on('display_uri', this.onUri); this.session = await this.provider.connect({ optionalNamespaces: { solana: { chains: this.getRequestedChainsWithDeprecated(), methods: [ 'solana_signMessage', 'solana_signTransaction', 'solana_signAndSendTransaction', 'solana_signAllTransactions' ], events: [], rpcMap } } }); this.provider.removeListener('display_uri', this.onUri); } const account = this.getAccount(true); this.emit('connect', new PublicKey(account.publicKey)); return account.address; } async disconnect() { await this.provider?.disconnect(); this.emit('disconnect', undefined); } async signMessage(message) { this.checkIfMethodIsSupported('solana_signMessage'); const signedMessage = await this.request('solana_signMessage', { message: base58.encode(message), pubkey: this.getAccount(true).address }); return base58.decode(signedMessage.signature); } async signTransaction(transaction) { this.checkIfMethodIsSupported('solana_signTransaction'); const serializedTransaction = this.serializeTransaction(transaction); const result = await this.request('solana_signTransaction', { transaction: serializedTransaction, pubkey: this.getAccount(true).address, ...this.getRawRPCParams(transaction) }); if ('signature' in result) { transaction.addSignature(new PublicKey(this.getAccount(true).publicKey), Buffer.from(base58.decode(result.signature))); return transaction; } const decodedTransaction = Buffer.from(result.transaction, 'base64'); if (isVersionedTransaction(transaction)) { return VersionedTransaction.deserialize(decodedTransaction); } return Transaction.from(decodedTransaction); } async signAndSendTransaction(transaction, sendOptions) { this.checkIfMethodIsSupported('solana_signAndSendTransaction'); const serializedTransaction = this.serializeTransaction(transaction); const result = await this.request('solana_signAndSendTransaction', { transaction: serializedTransaction, pubkey: this.getAccount(true).address, sendOptions }); return result.signature; } async sendTransaction(transaction, connection, options) { const signedTransaction = await this.signTransaction(transaction); const signature = await connection.sendRawTransaction(signedTransaction.serialize(), options); return signature; } async signAllTransactions(transactions) { try { this.checkIfMethodIsSupported('solana_signAllTransactions'); const result = await this.request('solana_signAllTransactions', { transactions: transactions.map(transaction => this.serializeTransaction(transaction)) }); return result.transactions.map((serializedTransaction, index) => { const transaction = transactions[index]; if (!transaction) { throw new Error('Invalid transactions response'); } const decodedTransaction = Buffer.from(serializedTransaction, 'base64'); if (isVersionedTransaction(transaction)) { return VersionedTransaction.deserialize(decodedTransaction); } return Transaction.from(decodedTransaction); }); } catch (error) { if (error instanceof WalletConnectMethodNotSupportedError) { const signedTransactions = []; for (const transaction of transactions) { signedTransactions.push(await this.signTransaction(transaction)); } return signedTransactions; } throw error; } } request(method, params) { const chain = this.chains.find(c => this.getActiveChain()?.chainId === c.chainId); let chainId = withSolanaNamespace(chain?.chainId); switch (chainId) { case SolConstantsUtil.CHAIN_IDS.Mainnet: if (!this.sessionChains.includes(SolConstantsUtil.CHAIN_IDS.Mainnet)) { chainId = SolConstantsUtil.CHAIN_IDS.Deprecated_Mainnet; } break; case SolConstantsUtil.CHAIN_IDS.Devnet: if (!this.sessionChains.includes(SolConstantsUtil.CHAIN_IDS.Devnet)) { chainId = SolConstantsUtil.CHAIN_IDS.Deprecated_Devnet; } break; default: break; } return this.provider?.request({ method, params }, chainId); } get sessionChains() { const solanaNamespace = this.session?.namespaces['solana']; if (!solanaNamespace) { return []; } const chains = solanaNamespace.chains || []; const accountsChains = solanaNamespace.accounts.map(account => { const [chainNamespace, chainId] = account.split(':'); return `${chainNamespace}:${chainId}`; }); return Array.from(new Set([...chains, ...accountsChains])); } serializeTransaction(transaction) { return Buffer.from(transaction.serialize({ verifySignatures: false })).toString('base64'); } getAccount(required) { const account = this.session?.namespaces['solana']?.accounts[0]; if (!account) { if (required) { throw new Error('Account not found'); } return undefined; } const address = account.split(':')[2]; if (!address) { if (required) { throw new Error('Address not found'); } return undefined; } return { address, publicKey: base58.decode(address) }; } getRequestedChainsWithDeprecated() { const chains = this.requestedChains.map(chain => withSolanaNamespace(chain.chainId)); if (chains.includes(SolConstantsUtil.CHAIN_IDS.Mainnet)) { chains.push(SolConstantsUtil.CHAIN_IDS.Deprecated_Mainnet); } if (chains.includes(SolConstantsUtil.CHAIN_IDS.Devnet)) { chains.push(SolConstantsUtil.CHAIN_IDS.Deprecated_Devnet); } return chains; } getRawRPCParams(transaction) { if (isVersionedTransaction(transaction)) { return {}; } return { feePayer: transaction.feePayer?.toBase58() ?? '', instructions: transaction.instructions.map(instruction => ({ data: base58.encode(instruction.data), keys: instruction.keys.map(key => ({ isWritable: key.isWritable, isSigner: key.isSigner, pubkey: key.pubkey.toBase58() })), programId: instruction.programId.toBase58() })), recentBlockhash: transaction.recentBlockhash ?? '' }; } checkIfMethodIsSupported(method) { if (!this.session?.namespaces['solana']?.methods.includes(method)) { throw new WalletConnectMethodNotSupportedError(method); } } } //# sourceMappingURL=WalletConnectProvider.js.map