UNPKG

@cygnus-wealth/wallet-integration-system

Version:

Multi-chain wallet integration system for CygnusWealth

156 lines (155 loc) 5.89 kB
import { ethers } from 'ethers'; import { CHAIN_CONFIGS, EVM_CHAINS } from '../../utils/constants'; import './types'; export class EVMWalletIntegration { chain; source; provider = null; connected = false; accounts = []; activeAccountIndex = 0; constructor(chain, source, _config // Unused for EVM as it uses injected provider ) { this.chain = chain; this.source = source; if (!EVM_CHAINS.includes(chain)) { throw new Error(`Chain ${chain} is not an EVM chain`); } // EVM wallets use the injected provider (MetaMask, etc.) // which manages its own RPC connections } async connect() { if (typeof window === 'undefined' || !window.ethereum) { throw new Error('No Ethereum provider found'); } try { this.provider = new ethers.BrowserProvider(window.ethereum); const chainConfig = CHAIN_CONFIGS[this.chain]; if (!chainConfig) { throw new Error(`Chain ${this.chain} not configured`); } const currentChainId = await window.ethereum.request({ method: 'eth_chainId' }); if (parseInt(currentChainId, 16) !== chainConfig.chainId) { await this.switchChain(chainConfig.chainId); } const addresses = await this.provider.send('eth_requestAccounts', []); if (addresses.length === 0) { throw new Error('No accounts found'); } // Create Account objects for all addresses // Note: We cannot determine the actual derivation paths or which wallet/mnemonic // each address belongs to. MetaMask returns all previously connected addresses // across all configured wallets (mnemonics, hardware wallets, imported keys) this.accounts = addresses.map((address, index) => ({ address: address.toLowerCase(), index, derivationPath: undefined, // Unknown - could be from any wallet/path label: index === 0 ? 'Active Account' : `Connected Account ${index}` })); this.activeAccountIndex = 0; this.connected = true; return { address: this.accounts[0].address, chain: this.chain, source: this.source, connected: true, connectedAt: new Date(), accounts: this.accounts, activeAccount: this.accounts[0] }; } catch (error) { this.connected = false; throw error; } } async disconnect() { this.provider = null; this.connected = false; this.accounts = []; this.activeAccountIndex = 0; } async getAddress() { if (!this.connected || this.accounts.length === 0) { throw new Error('Wallet not connected'); } return this.accounts[this.activeAccountIndex].address; } async getAllAccounts() { if (!this.connected) { throw new Error('Wallet not connected'); } // Try to get fresh account list from provider if (this.provider && window.ethereum) { try { const addresses = await window.ethereum.request({ method: 'eth_accounts' }); // Update accounts if changed if (addresses.length !== this.accounts.length) { this.accounts = addresses.map((address, index) => ({ address: address.toLowerCase(), index, derivationPath: undefined, // Unknown - could be from any wallet/path label: index === 0 ? 'Active Account' : `Connected Account ${index}` })); } } catch (error) { console.error('Error fetching accounts:', error); } } return [...this.accounts]; } async switchAccount(address) { if (!this.connected) { throw new Error('Wallet not connected'); } const accountIndex = this.accounts.findIndex(acc => acc.address.toLowerCase() === address.toLowerCase()); if (accountIndex === -1) { throw new Error(`Account ${address} not found`); } this.activeAccountIndex = accountIndex; } async getActiveAccount() { if (!this.connected || this.accounts.length === 0) { return null; } return this.accounts[this.activeAccountIndex]; } isConnected() { return this.connected; } async switchChain(chainId) { const chainIdHex = `0x${chainId.toString(16)}`; try { await window.ethereum.request({ method: 'wallet_switchEthereumChain', params: [{ chainId: chainIdHex }] }); } catch (error) { if (error.code === 4902) { const chainConfig = CHAIN_CONFIGS[this.chain]; if (!chainConfig) { throw new Error(`Chain ${this.chain} not configured`); } await window.ethereum.request({ method: 'wallet_addEthereumChain', params: [{ chainId: chainIdHex, chainName: this.chain, nativeCurrency: chainConfig.nativeCurrency, rpcUrls: [chainConfig.rpcUrl], blockExplorerUrls: [chainConfig.blockExplorerUrl] }] }); } else { throw error; } } } }