diff --git a/src/components/Claim/Link/Onchain/Success.view.tsx b/src/components/Claim/Link/Onchain/Success.view.tsx
index 7c8680aeb..83c680b08 100644
--- a/src/components/Claim/Link/Onchain/Success.view.tsx
+++ b/src/components/Claim/Link/Onchain/Success.view.tsx
@@ -11,7 +11,7 @@ import * as _consts from '../../Claim.consts'
export const SuccessClaimLinkView = ({ transactionHash, claimLinkData, type }: _consts.IClaimScreenProps) => {
const connections = useConnections()
- const { isConnected, address, chain: currentChain } = useWallet()
+ const { isConnected, address, chain: currentChain, isPeanutWallet } = useWallet()
const { switchChainAsync } = useSwitchChain()
const { resetTokenContextProvider, selectedChainID } = useContext(context.tokenSelectorContext)
@@ -45,11 +45,11 @@ export const SuccessClaimLinkView = ({ transactionHash, claimLinkData, type }: _
}
useEffect(() => {
- resetTokenContextProvider()
+ if (!isPeanutWallet) resetTokenContextProvider()
if (transactionHash && type === 'claimxchain') {
fetchDestinationChain(transactionHash, setExplorerUrlDestChainWithTxHash)
}
- }, [])
+ }, [isPeanutWallet, transactionHash, type])
useEffect(() => {
if (isw3mEmailWallet && isConnected) {
diff --git a/src/components/Create/Create.tsx b/src/components/Create/Create.tsx
index cc80508c2..8b03c7726 100644
--- a/src/components/Create/Create.tsx
+++ b/src/components/Create/Create.tsx
@@ -53,7 +53,7 @@ export const Create = () => {
}[]
>([])
- const { address } = useWallet()
+ const { address, isPeanutWallet } = useWallet()
const { resetTokenContextProvider } = useContext(context.tokenSelectorContext)
@@ -105,9 +105,11 @@ export const Create = () => {
}
useEffect(() => {
- resetTokenContextProvider()
- fetchAndSetCrossChainDetails()
- }, [])
+ if (!isPeanutWallet) {
+ resetTokenContextProvider()
+ fetchAndSetCrossChainDetails()
+ }
+ }, [isPeanutWallet])
useEffect(() => {
if (address) {
diff --git a/src/components/Create/Link/Input.view.tsx b/src/components/Create/Link/Input.view.tsx
index 1e8a2d760..d4f765ceb 100644
--- a/src/components/Create/Link/Input.view.tsx
+++ b/src/components/Create/Link/Input.view.tsx
@@ -80,9 +80,6 @@ export const CreateLinkInputView = ({
const { open } = useAppKit()
- const handleConnectWallet = async () => {
- open()
- }
const { selectedWallet, signInModal, isConnected, address, isExternalWallet, isPeanutWallet } = useWallet()
const handleOnNext = async () => {
diff --git a/src/context/contextProvider.tsx b/src/context/contextProvider.tsx
index f3de0dc95..d695cf200 100644
--- a/src/context/contextProvider.tsx
+++ b/src/context/contextProvider.tsx
@@ -3,15 +3,18 @@ import { AuthProvider } from './authContext'
import { LoadingStateContextProvider } from './loadingStates.context'
import { PushProvider } from './pushProvider'
import { TokenContextProvider } from './tokenSelector.context'
+import { KernelClientProvider } from './kernelClient.context'
export const ContextProvider = ({ children }: { children: React.ReactNode }) => {
return (
-
- {children}
-
+
+
+ {children}
+
+
diff --git a/src/context/kernelClient.context.tsx b/src/context/kernelClient.context.tsx
new file mode 100644
index 000000000..b38d239c8
--- /dev/null
+++ b/src/context/kernelClient.context.tsx
@@ -0,0 +1,144 @@
+'use client'
+
+import { peanutPublicClient } from '@/constants/viem.consts'
+import * as consts from '@/constants/zerodev.consts'
+import { useAuth } from '@/context/authContext'
+import { useAppDispatch } from '@/redux/hooks'
+import { zerodevActions } from '@/redux/slices/zerodev-slice'
+import { getFromLocalStorage } from '@/utils'
+import { PasskeyValidatorContractVersion, toPasskeyValidator, toWebAuthnKey } from '@zerodev/passkey-validator'
+import {
+ createKernelAccount,
+ createKernelAccountClient,
+ createZeroDevPaymasterClient,
+ KernelAccountClient,
+} from '@zerodev/sdk'
+import { KERNEL_V3_1 } from '@zerodev/sdk/constants'
+import { createContext, useEffect, useState, useContext, ReactNode } from 'react'
+import { http, Transport } from 'viem'
+
+interface KernelClientContextType {
+ kernelClient: AppSmartAccountClient | undefined
+ setWebAuthnKey: (webAuthnKey: WebAuthnKey) => void
+}
+
+// types
+type AppSmartAccountClient = KernelAccountClient
+
+type WebAuthnKey = Awaited>
+
+const LOCAL_STORAGE_WEB_AUTHN_KEY = 'web-authn-key'
+
+const KernelClientContext = createContext(undefined)
+
+const createKernelClient = async (passkeyValidator: any) => {
+ console.log('Creating new kernel client...')
+ const kernelAccount = await createKernelAccount(peanutPublicClient, {
+ plugins: {
+ sudo: passkeyValidator,
+ },
+ entryPoint: consts.USER_OP_ENTRY_POINT,
+ kernelVersion: KERNEL_V3_1,
+ })
+
+ const kernelClient = createKernelAccountClient({
+ account: kernelAccount,
+ chain: consts.PEANUT_WALLET_CHAIN,
+ bundlerTransport: http(consts.BUNDLER_URL),
+ paymaster: {
+ getPaymasterData: async (userOperation) => {
+ const zerodevPaymaster = createZeroDevPaymasterClient({
+ chain: consts.PEANUT_WALLET_CHAIN,
+ transport: http(consts.PAYMASTER_URL),
+ })
+
+ try {
+ return await zerodevPaymaster.sponsorUserOperation({
+ userOperation,
+ shouldOverrideFee: true,
+ })
+ } catch (error) {
+ console.error('Paymaster error:', error)
+ throw error
+ }
+ },
+ },
+ })
+
+ return kernelClient
+}
+
+export const KernelClientProvider = ({ children }: { children: ReactNode }) => {
+ const [kernelClient, setKernelClient] = useState()
+ const [webAuthnKey, setWebAuthnKey] = useState(undefined)
+ const dispatch = useAppDispatch()
+ const { fetchUser } = useAuth()
+
+ // lifecycle hooks
+ useEffect(() => {
+ const storedWebAuthnKey = getFromLocalStorage(LOCAL_STORAGE_WEB_AUTHN_KEY)
+ if (storedWebAuthnKey) {
+ setWebAuthnKey(storedWebAuthnKey)
+ }
+ }, [])
+
+ useEffect(() => {
+ let isMounted = true
+
+ if (!webAuthnKey) {
+ return () => {
+ isMounted = false
+ }
+ }
+
+ const initializeClient = async () => {
+ try {
+ const validator = await toPasskeyValidator(peanutPublicClient, {
+ webAuthnKey,
+ entryPoint: consts.USER_OP_ENTRY_POINT,
+ kernelVersion: KERNEL_V3_1,
+ validatorContractVersion: PasskeyValidatorContractVersion.V0_0_2,
+ })
+
+ const client = await createKernelClient(validator)
+
+ if (isMounted) {
+ fetchUser()
+ setKernelClient(client)
+ dispatch(zerodevActions.setAddress(client.account!.address))
+ dispatch(zerodevActions.setIsKernelClientReady(true))
+ dispatch(zerodevActions.setIsRegistering(false))
+ dispatch(zerodevActions.setIsLoggingIn(false))
+ }
+ } catch (error) {
+ console.error('Error initializing kernel client:', error)
+ dispatch(zerodevActions.setIsKernelClientReady(false))
+ }
+ }
+
+ initializeClient()
+
+ return () => {
+ isMounted = false
+ }
+ }, [webAuthnKey])
+
+ return (
+
+ {children}
+
+ )
+}
+
+export const useKernelClient = (): KernelClientContextType => {
+ const context = useContext(KernelClientContext)
+ if (context === undefined) {
+ throw new Error('useKernelClient must be used within a KernelClientProvider')
+ }
+ return context
+}
diff --git a/src/hooks/useZeroDev.ts b/src/hooks/useZeroDev.ts
index 33ee8a8e1..49e56c90d 100644
--- a/src/hooks/useZeroDev.ts
+++ b/src/hooks/useZeroDev.ts
@@ -1,29 +1,16 @@
'use client'
-import { peanutPublicClient } from '@/constants/viem.consts'
import * as consts from '@/constants/zerodev.consts'
import { useAuth } from '@/context/authContext'
+import { useKernelClient } from '@/context/kernelClient.context'
import { useAppDispatch, useZerodevStore } from '@/redux/hooks'
import { zerodevActions } from '@/redux/slices/zerodev-slice'
-import { getFromLocalStorage, saveToLocalStorage } from '@/utils'
-import {
- PasskeyValidatorContractVersion,
- toPasskeyValidator,
- toWebAuthnKey,
- WebAuthnMode,
-} from '@zerodev/passkey-validator'
-import {
- createKernelAccount,
- createKernelAccountClient,
- createZeroDevPaymasterClient,
- KernelAccountClient,
-} from '@zerodev/sdk'
-import { KERNEL_V3_1 } from '@zerodev/sdk/constants'
-import { useCallback, useEffect, useState } from 'react'
-import { Abi, Address, encodeFunctionData, Hex, http, Transport } from 'viem'
+import { saveToLocalStorage } from '@/utils'
+import { toWebAuthnKey, WebAuthnMode } from '@zerodev/passkey-validator'
+import { useCallback } from 'react'
+import { Abi, Address, encodeFunctionData, Hex } from 'viem'
// types
-type AppSmartAccountClient = KernelAccountClient
type UserOpNotEncodedParams = {
to: Address
value: number
@@ -37,128 +24,16 @@ type UserOpEncodedParams = {
data?: Hex | undefined
}
-type WebAuthnKey = Awaited>
-
-const LOCAL_STORAGE_KERNEL_ADDRESS = 'kernel-address'
const LOCAL_STORAGE_WEB_AUTHN_KEY = 'web-authn-key'
export const useZeroDev = () => {
const dispatch = useAppDispatch()
- const { fetchUser, user } = useAuth()
+ const { user } = useAuth()
const { isKernelClientReady, isRegistering, isLoggingIn, isSendingUserOp, address } = useZerodevStore()
-
- // local states for non-UI state
- const [kernelClient, setKernelClient] = useState(undefined)
- const [webAuthnKey, setWebAuthnKey] = useState(undefined)
+ const { kernelClient, setWebAuthnKey } = useKernelClient()
const _getPasskeyName = (handle: string) => `${handle}.peanut.wallet`
- // setup function
- const createKernelClient = useCallback(
- async (passkeyValidator: any) => {
- // check if kernel client with the same address already exists
- const storedAddress = getFromLocalStorage(LOCAL_STORAGE_KERNEL_ADDRESS)
- if (kernelClient && storedAddress === kernelClient.account?.address) {
- return kernelClient
- }
-
- console.log('Creating new kernel client...')
- const kernelAccount = await createKernelAccount(peanutPublicClient, {
- plugins: {
- sudo: passkeyValidator,
- },
- entryPoint: consts.USER_OP_ENTRY_POINT,
- kernelVersion: KERNEL_V3_1,
- })
-
- const newKernelClient = createKernelAccountClient({
- account: kernelAccount,
- chain: consts.PEANUT_WALLET_CHAIN,
- bundlerTransport: http(consts.BUNDLER_URL),
- paymaster: {
- getPaymasterData: async (userOperation) => {
- const zerodevPaymaster = createZeroDevPaymasterClient({
- chain: consts.PEANUT_WALLET_CHAIN,
- transport: http(consts.PAYMASTER_URL),
- })
-
- try {
- return await zerodevPaymaster.sponsorUserOperation({
- userOperation,
- shouldOverrideFee: true,
- })
- } catch (error) {
- console.error('Paymaster error:', error)
- throw error
- }
- },
- },
- })
-
- // store address for future reference
- saveToLocalStorage(LOCAL_STORAGE_KERNEL_ADDRESS, newKernelClient.account?.address)
- return newKernelClient
- },
- [kernelClient]
- )
-
- // lifecycle hooks
- useEffect(() => {
- const storedWebAuthnKey = getFromLocalStorage(LOCAL_STORAGE_WEB_AUTHN_KEY)
- if (storedWebAuthnKey) {
- setWebAuthnKey(storedWebAuthnKey)
- }
- }, [])
-
- useEffect(() => {
- let isMounted = true
-
- if (!webAuthnKey) {
- return () => {
- isMounted = false
- }
- }
-
- // check if valid kernel client already exists
- if (kernelClient && kernelClient.account?.address) {
- dispatch(zerodevActions.setIsKernelClientReady(true))
- dispatch(zerodevActions.setIsRegistering(false))
- dispatch(zerodevActions.setIsLoggingIn(false))
- return
- }
-
- const initializeClient = async () => {
- try {
- const validator = await toPasskeyValidator(peanutPublicClient, {
- webAuthnKey,
- entryPoint: consts.USER_OP_ENTRY_POINT,
- kernelVersion: KERNEL_V3_1,
- validatorContractVersion: PasskeyValidatorContractVersion.V0_0_2,
- })
-
- const client = await createKernelClient(validator)
-
- if (isMounted) {
- fetchUser()
- setKernelClient(client)
- dispatch(zerodevActions.setAddress(client.account!.address))
- dispatch(zerodevActions.setIsKernelClientReady(true))
- dispatch(zerodevActions.setIsRegistering(false))
- dispatch(zerodevActions.setIsLoggingIn(false))
- }
- } catch (error) {
- console.error('Error initializing kernel client:', error)
- dispatch(zerodevActions.setIsKernelClientReady(false))
- }
- }
-
- initializeClient()
-
- return () => {
- isMounted = false
- }
- }, [webAuthnKey, dispatch, fetchUser, createKernelClient, kernelClient])
-
// register function
const handleRegister = async (handle: string): Promise => {
dispatch(zerodevActions.setIsRegistering(true))