diff --git a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts index 70cedc49f..c17e11ac1 100644 --- a/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts +++ b/advanced/wallets/react-wallet-v2/src/hooks/useSmartAccounts.ts @@ -1,11 +1,16 @@ import { EIP155Chain } from '@/data/EIP155Data' -import SettingsStore from '@/store/SettingsStore' +import SettingsStore, { + SAFE_SMART_ACCOUNTS_ENABLED_KEY, + ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY, + BICONOMY_SMART_ACCOUNTS_ENABLED_KEY +} from '@/store/SettingsStore' import { createOrRestoreBiconomySmartAccount, createOrRestoreKernelSmartAccount, createOrRestoreSafeSmartAccount, smartAccountWallets } from '@/utils/SmartAccountUtil' +import { styledToast } from '@/utils/HelperUtil' import { useSnapshot } from 'valtio' @@ -19,19 +24,58 @@ export default function useSmartAccounts() { const initializeSmartAccounts = async (privateKey: string) => { if (smartAccountEnabled) { - if (kernelSmartAccountEnabled) { - const { kernelSmartAccountAddress } = await createOrRestoreKernelSmartAccount(privateKey) - SettingsStore.setKernelSmartAccountAddress(kernelSmartAccountAddress) - } - if (safeSmartAccountEnabled) { - const { safeSmartAccountAddress } = await createOrRestoreSafeSmartAccount(privateKey) - SettingsStore.setSafeSmartAccountAddress(safeSmartAccountAddress) - } - if (biconomySmartAccountEnabled) { - const { biconomySmartAccountAddress } = await createOrRestoreBiconomySmartAccount( - privateKey - ) - SettingsStore.setBiconomySmartAccountAddress(biconomySmartAccountAddress) + try { + const promises = [] + + if (kernelSmartAccountEnabled) { + promises.push( + (async () => { + try { + const address = await createOrRestoreKernelSmartAccount(privateKey) + SettingsStore.setKernelSmartAccountAddress(address) + } catch (error) { + SettingsStore.state.kernelSmartAccountEnabled = false + localStorage.removeItem(ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY) + styledToast('Kernel smart account initialization failed', 'warning') + } + })() + ) + } + + if (safeSmartAccountEnabled) { + promises.push( + (async () => { + try { + const address = await createOrRestoreSafeSmartAccount(privateKey) + SettingsStore.setSafeSmartAccountAddress(address) + } catch (error) { + SettingsStore.state.safeSmartAccountEnabled = false + localStorage.removeItem(SAFE_SMART_ACCOUNTS_ENABLED_KEY) + styledToast('Safe smart account initialization failed', 'warning') + } + })() + ) + } + + if (biconomySmartAccountEnabled) { + promises.push( + (async () => { + try { + const address = await createOrRestoreBiconomySmartAccount(privateKey) + SettingsStore.setBiconomySmartAccountAddress(address) + } catch (error) { + SettingsStore.state.biconomySmartAccountEnabled = false + localStorage.removeItem(BICONOMY_SMART_ACCOUNTS_ENABLED_KEY) + styledToast('Biconomy smart account initialization failed', 'warning') + } + })() + ) + } + + await Promise.all(promises) + } catch (error) { + console.error('Error initializing smart accounts:', error) + styledToast('Error initializing smart accounts', 'error') } } } diff --git a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SmartAccountLib.ts b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SmartAccountLib.ts index 0962d162a..465376e8c 100644 --- a/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SmartAccountLib.ts +++ b/advanced/wallets/react-wallet-v2/src/lib/smart-accounts/SmartAccountLib.ts @@ -27,12 +27,7 @@ import { createSmartAccountClient } from 'permissionless' import { PimlicoBundlerActions, pimlicoBundlerActions } from 'permissionless/actions/pimlico' -import { - PIMLICO_NETWORK_NAMES, - publicClientUrl, - publicRPCUrl, - UrlConfig -} from '@/utils/SmartAccountUtil' +import { PIMLICO_NETWORK_NAMES, publicClientUrl, UrlConfig } from '@/utils/SmartAccountUtil' import { EntryPoint } from 'permissionless/types/entrypoint' import { Erc7579Actions, erc7579Actions } from 'permissionless/actions/erc7579' import { SmartAccount } from 'permissionless/accounts' @@ -85,7 +80,6 @@ export abstract class SmartAccountLib implements EIP155Wallet { entryPointVersion = 6 }: SmartAccountLibOptions) { const apiKey = process.env.NEXT_PUBLIC_PIMLICO_KEY - const publicClientRPCUrl = process.env.NEXT_PUBLIC_LOCAL_CLIENT_URL || publicRPCUrl({ chain }) const paymasterUrl = ({ chain }: UrlConfig) => { const localPaymasterUrl = process.env.NEXT_PUBLIC_LOCAL_PAYMASTER_URL if (localPaymasterUrl) { diff --git a/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts b/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts index 706cc9fe8..9fe0ffdc3 100644 --- a/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts +++ b/advanced/wallets/react-wallet-v2/src/store/SettingsStore.ts @@ -7,13 +7,14 @@ import { } from '@/utils/SmartAccountUtil' import { Verify, SessionTypes } from '@walletconnect/types' import { proxy } from 'valtio' +import { styledToast } from '@/utils/HelperUtil' const TEST_NETS_ENABLED_KEY = 'TEST_NETS' const CA_ENABLED_KEY = 'CHAIN_ABSTRACTION' -const SMART_ACCOUNTS_ENABLED_KEY = 'SMART_ACCOUNTS' -const ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY = 'ZERO_DEV_SMART_ACCOUNTS' -const SAFE_SMART_ACCOUNTS_ENABLED_KEY = 'SAFE_SMART_ACCOUNTS' -const BICONOMY_SMART_ACCOUNTS_ENABLED_KEY = 'BICONOMY_SMART_ACCOUNTS' +export const SMART_ACCOUNTS_ENABLED_KEY = 'SMART_ACCOUNTS' +export const ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY = 'ZERO_DEV_SMART_ACCOUNTS' +export const SAFE_SMART_ACCOUNTS_ENABLED_KEY = 'SAFE_SMART_ACCOUNTS' +export const BICONOMY_SMART_ACCOUNTS_ENABLED_KEY = 'BICONOMY_SMART_ACCOUNTS' const MODULE_MANAGEMENT_ENABLED_KEY = 'MODULE_MANAGEMENT' /** @@ -213,13 +214,19 @@ const SettingsStore = { async toggleKernelSmartAccountsEnabled() { state.kernelSmartAccountEnabled = !state.kernelSmartAccountEnabled + if (state.kernelSmartAccountEnabled) { - const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() - const { kernelSmartAccountAddress } = await createOrRestoreKernelSmartAccount( - eip155Wallets[eip155Addresses[0]].getPrivateKey() - ) - SettingsStore.setKernelSmartAccountAddress(kernelSmartAccountAddress) - localStorage.setItem(ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + try { + const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() + const address = await createOrRestoreKernelSmartAccount( + eip155Wallets[eip155Addresses[0]].getPrivateKey() + ) + SettingsStore.setKernelSmartAccountAddress(address) + localStorage.setItem(ZERO_DEV_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + } catch (error) { + state.kernelSmartAccountEnabled = false + styledToast('Failed to initialize Kernel smart account', 'error') + } } else { removeSmartAccount(SettingsStore.state.kernelSmartAccountAddress) SettingsStore.setKernelSmartAccountAddress('') @@ -231,13 +238,19 @@ const SettingsStore = { async toggleSafeSmartAccountsEnabled() { state.safeSmartAccountEnabled = !state.safeSmartAccountEnabled + if (state.safeSmartAccountEnabled) { - const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() - const { safeSmartAccountAddress } = await createOrRestoreSafeSmartAccount( - eip155Wallets[eip155Addresses[0]].getPrivateKey() - ) - SettingsStore.setSafeSmartAccountAddress(safeSmartAccountAddress) - localStorage.setItem(SAFE_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + try { + const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() + const address = await createOrRestoreSafeSmartAccount( + eip155Wallets[eip155Addresses[0]].getPrivateKey() + ) + SettingsStore.setSafeSmartAccountAddress(address) + localStorage.setItem(SAFE_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + } catch (error) { + state.safeSmartAccountEnabled = false + styledToast('Failed to initialize Safe smart account', 'error') + } } else { removeSmartAccount(SettingsStore.state.safeSmartAccountAddress) SettingsStore.setSafeSmartAccountAddress('') @@ -249,18 +262,22 @@ const SettingsStore = { async toggleBiconomySmartAccountsEnabled() { state.biconomySmartAccountEnabled = !state.biconomySmartAccountEnabled + if (state.biconomySmartAccountEnabled) { - const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() - const { biconomySmartAccountAddress } = await createOrRestoreBiconomySmartAccount( - eip155Wallets[eip155Addresses[0]].getPrivateKey() - ) - SettingsStore.setBiconomySmartAccountAddress(biconomySmartAccountAddress) - localStorage.setItem(BICONOMY_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + try { + const { eip155Addresses, eip155Wallets } = createOrRestoreEIP155Wallet() + const address = await createOrRestoreBiconomySmartAccount( + eip155Wallets[eip155Addresses[0]].getPrivateKey() + ) + SettingsStore.setBiconomySmartAccountAddress(address) + localStorage.setItem(BICONOMY_SMART_ACCOUNTS_ENABLED_KEY, 'YES') + } catch (error) { + state.biconomySmartAccountEnabled = false + styledToast('Failed to initialize Biconomy smart account', 'error') + } } else { removeSmartAccount(SettingsStore.state.biconomySmartAccountAddress) SettingsStore.setBiconomySmartAccountAddress('') - state.moduleManagementEnabled = false - localStorage.removeItem(MODULE_MANAGEMENT_ENABLED_KEY) localStorage.removeItem(BICONOMY_SMART_ACCOUNTS_ENABLED_KEY) } } diff --git a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts index 8a196a80c..8d8bfe046 100644 --- a/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts +++ b/advanced/wallets/react-wallet-v2/src/utils/SmartAccountUtil.ts @@ -6,6 +6,7 @@ import { KernelSmartAccountLib } from '@/lib/smart-accounts/KernelSmartAccountLi import { baseSepolia, sepolia } from 'viem/chains' import { SafeSmartAccountLib } from '@/lib/smart-accounts/SafeSmartAccountLib' import { SmartAccountLib } from '@/lib/smart-accounts/SmartAccountLib' +import { getChainData } from '@/data/chainsUtil' // Entrypoints [I think this is constant but JIC] export const ENTRYPOINT_ADDRESSES: Record = { @@ -32,14 +33,6 @@ export const USDC_ADDRESSES: Record = { 'Base Sepolia': '0x07865c6e87b9f70255377e024ace6630c1eaa37f' //Dummy } -// RPC URLs -export const RPC_URLS: Record = { - Sepolia: 'https://rpc.ankr.com/eth_sepolia', - 'Polygon Mumbai': 'https://mumbai.rpc.thirdweb.com', - Goerli: 'https://ethereum-goerli.publicnode.com', - 'Base Sepolia': 'https://sepolia.base.org' -} - // Pimlico RPC names export const PIMLICO_NETWORK_NAMES: Record = { Sepolia: 'sepolia', @@ -48,8 +41,15 @@ export const PIMLICO_NETWORK_NAMES: Record = { 'Base Sepolia': 'base-sepolia' } -export const publicRPCUrl = ({ chain }: UrlConfig) => { - return RPC_URLS[chain?.name] +// Get RPC URL from chain data +export const getRpcUrl = (chainId: number): string => { + const chainData = getChainData(`eip155:${chainId}`) + if (chainData && 'rpc' in chainData && typeof chainData.rpc === 'string') { + return chainData.rpc + } + + console.error(`No RPC URL found for chain ID ${chainId}`) + return '' } export function supportedAddressPriority( @@ -89,21 +89,24 @@ export function isAllowedKernelChain(chainId: number): boolean { return kernelAllowedChains.some(chain => chain.id == chainId) } -export async function createOrRestoreKernelSmartAccount(privateKey: string) { - const lib = new KernelSmartAccountLib({ - privateKey, - chain: sepolia, - sponsored: true, - entryPointVersion: 6 - }) - await lib.init() - const address = lib.getAddress() - const key = `${sepolia.id}:${address}` - if (!smartAccountWallets[key]) { - smartAccountWallets[key] = lib - } - return { - kernelSmartAccountAddress: address +export async function createOrRestoreKernelSmartAccount(privateKey: string): Promise { + try { + const lib = new KernelSmartAccountLib({ + privateKey, + chain: sepolia, + sponsored: true, + entryPointVersion: 6 + }) + await lib.init() + const address = lib.getAddress() + const key = `${sepolia.id}:${address}` + if (!smartAccountWallets[key]) { + smartAccountWallets[key] = lib + } + return address + } catch (error) { + console.error('Error creating/restoring Kernel Smart Account:', error) + throw error } } @@ -123,42 +126,46 @@ const initializeSafeSmartAccountLib = async (chain: Chain, privateKey: string) = return lib } catch (error) { console.error(`Error initializing SafeSmartAccountLib for chain ${chain}:`, error) - return null // or throw error if you want to stop the entire process + return null } } -export async function createOrRestoreSafeSmartAccount(privateKey: string) { - if (safeAllowedChains.length === 0) { - throw new Error('No allowed chains for SafeSmartAccount') - } - const libs = await Promise.all( - safeAllowedChains.map(chain => initializeSafeSmartAccountLib(chain, privateKey)) - ).then(results => results.filter((lib): lib is NonNullable => lib !== null)) +export async function createOrRestoreSafeSmartAccount(privateKey: string): Promise { + try { + if (safeAllowedChains.length === 0) { + throw new Error('No allowed chains for SafeSmartAccount') + } - if (libs.length === 0) { - throw new Error('No safe smart account initialized') - } + const libs = await Promise.all( + safeAllowedChains.map(chain => initializeSafeSmartAccountLib(chain, privateKey)) + ).then(results => results.filter((lib): lib is NonNullable => lib !== null)) - libs.forEach(lib => { - const address = lib.getAddress() - const key = `${lib.chain.id}:${address}` - if (!smartAccountWallets[key]) { - smartAccountWallets[key] = lib + if (libs.length === 0) { + throw new Error('No safe smart account could be initialized') } - }) - const safeSmartAccountAddress: string = libs[0].getAddress() + libs.forEach(lib => { + const address = lib.getAddress() + const key = `${lib.chain.id}:${address}` + if (!smartAccountWallets[key]) { + smartAccountWallets[key] = lib + } + }) - return { - safeSmartAccountAddress + return libs[0].getAddress() + } catch (error) { + console.error('Error creating/restoring Safe Smart Account:', error) + throw error } } + export function removeSmartAccount(address: string) { const key = `${sepolia.id}:${address}` if (smartAccountWallets[key]) { delete smartAccountWallets[key] } } + export function removeSmartAccounts(addresses: string[]) { addresses.forEach(address => { if (smartAccountWallets[address]) { @@ -167,16 +174,19 @@ export function removeSmartAccounts(addresses: string[]) { }) } -export async function createOrRestoreBiconomySmartAccount(privateKey: string) { - const lib = new BiconomySmartAccountLib({ privateKey, chain: sepolia, sponsored: true }) - await lib.init() - const address = lib.getAddress() - const key = `${sepolia.id}:${address}` - if (!smartAccountWallets[key]) { - smartAccountWallets[key] = lib - } - return { - biconomySmartAccountAddress: address +export async function createOrRestoreBiconomySmartAccount(privateKey: string): Promise { + try { + const lib = new BiconomySmartAccountLib({ privateKey, chain: sepolia, sponsored: true }) + await lib.init() + const address = lib.getAddress() + const key = `${sepolia.id}:${address}` + if (!smartAccountWallets[key]) { + smartAccountWallets[key] = lib + } + return address + } catch (error) { + console.error('Error creating/restoring Biconomy Smart Account:', error) + throw error } } @@ -185,7 +195,11 @@ export type UrlConfig = { } export const publicClientUrl = ({ chain }: UrlConfig) => { - return process.env.NEXT_PUBLIC_LOCAL_CLIENT_URL || publicRPCUrl({ chain }) + if (process.env.NEXT_PUBLIC_LOCAL_CLIENT_URL) { + return process.env.NEXT_PUBLIC_LOCAL_CLIENT_URL + } + + return getRpcUrl(chain.id) } export const paymasterUrl = ({ chain }: UrlConfig) => { diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionCheckoutModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionCheckoutModal.tsx index 22a6c46de..26633d8ab 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionCheckoutModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionCheckoutModal.tsx @@ -139,14 +139,11 @@ export default function SessionCheckoutModal() { await walletkit.respondSessionRequest({ topic, response }) styledToast('Payment approved successfully', 'success') - } + } } catch (error) { // Handle any unexpected errors console.error('Error processing payment:', error) - const response = WalletCheckoutUtil.formatCheckoutErrorResponse( - requestEvent.id, - error - ) + const response = WalletCheckoutUtil.formatCheckoutErrorResponse(requestEvent.id, error) await walletkit.respondSessionRequest({ topic, response }) styledToast((error as Error).message, 'error') diff --git a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx index bbfc9fa48..91a9e9596 100644 --- a/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx +++ b/advanced/wallets/react-wallet-v2/src/views/SessionProposalModal.tsx @@ -120,9 +120,7 @@ export default function SessionProposalModal() { return { eip155: { chains: eip155Chains, - methods: eip155Methods - .concat(eip5792Methods) - .concat(eip7715Methods), + methods: eip155Methods.concat(eip5792Methods).concat(eip7715Methods), events: ['accountsChanged', 'chainChanged'], accounts: eip155Chains .map(chain =>