@@ -11,6 +11,20 @@ import {
1111 http
1212} from 'viem'
1313
14+ // Cache for expensive RPC calls (code, balance, tx count)
15+ interface EOACacheEntry {
16+ code ?: { value : string | undefined ; timestamp : number }
17+ balance ?: { value : bigint ; timestamp : number }
18+ txCount ?: { value : number ; timestamp : number }
19+ }
20+
21+ const CACHE_DURATION_MS = 2 * 60 * 1000 // 2 minutes
22+ const eoaCache = new Map < string , EOACacheEntry > ( )
23+
24+ function getCacheKey ( address : string , chainId : number ) : string {
25+ return `${ address } -${ chainId } `
26+ }
27+
1428export function isViemWalletClient (
1529 wallet : WalletClient | AdaptedWallet
1630) : wallet is WalletClient {
@@ -245,60 +259,219 @@ export const adaptViemWallet = (wallet: WalletClient): AdaptedWallet => {
245259 } ,
246260 isEOA : async (
247261 chainId : number
248- ) : Promise < { isEOA : boolean ; isEIP7702Delegated : boolean } > => {
262+ ) : Promise < {
263+ isEOA : boolean
264+ isEIP7702Delegated : boolean
265+ } > => {
249266 if ( ! wallet . account ) {
250- return { isEOA : false , isEIP7702Delegated : false }
267+ return {
268+ isEOA : false ,
269+ isEIP7702Delegated : false
270+ }
251271 }
252272
253273 try {
254- let hasSmartWalletCapabilities = false
255- try {
256- const capabilities = await wallet . getCapabilities ( {
257- account : wallet . account ,
258- chainId
274+ const address = wallet . account . address
275+ const cacheKey = getCacheKey ( address , chainId )
276+ const now = Date . now ( )
277+
278+ // Get or create cache entry for this address+chain
279+ let cacheEntry = eoaCache . get ( cacheKey )
280+ if ( ! cacheEntry ) {
281+ cacheEntry = { }
282+ eoaCache . set ( cacheKey , cacheEntry )
283+ }
284+
285+ // Always fetch capabilities fresh (wallet-specific, not cacheable)
286+ const getSmartWalletCapabilities = async ( ) => {
287+ let _hasSmartWalletCapabilities = false
288+ try {
289+ const capabilities = await wallet . getCapabilities ( {
290+ account : wallet . account ,
291+ chainId
292+ } )
293+
294+ _hasSmartWalletCapabilities = Boolean (
295+ capabilities ?. atomicBatch ?. supported ||
296+ capabilities ?. paymasterService ?. supported ||
297+ capabilities ?. auxiliaryFunds ?. supported ||
298+ capabilities ?. sessionKeys ?. supported
299+ )
300+ } catch ( capabilitiesError ) { }
301+ return _hasSmartWalletCapabilities
302+ }
303+
304+ // Get code with caching
305+ const getCode = async ( ) => {
306+ // Check cache first
307+ console . log ( 'CHECKING CODE CACHE' )
308+ if (
309+ cacheEntry &&
310+ cacheEntry ?. code &&
311+ now - cacheEntry ?. code . timestamp < CACHE_DURATION_MS
312+ ) {
313+ console . log ( 'CODE CACHE HIT' )
314+ const code = cacheEntry ! . code . value
315+ const hasCode = Boolean ( code && code !== '0x' )
316+ const isEIP7702Delegated = Boolean (
317+ code && code . toLowerCase ( ) . startsWith ( '0xef01' )
318+ )
319+ return { hasCode, isEIP7702Delegated }
320+ }
321+
322+ // Fetch from RPC
323+ console . log ( 'FETCHING CODE FROM RPC' )
324+ const client = getClient ( )
325+ const chain = client . chains . find ( ( chain ) => chain . id === chainId )
326+ const rpcUrl = chain ?. httpRpcUrl
327+
328+ if ( ! chain ) {
329+ throw new Error ( `Chain ${ chainId } not found in relay client` )
330+ }
331+
332+ const viemClient = createPublicClient ( {
333+ chain : chain ?. viemChain ,
334+ transport : rpcUrl ? http ( rpcUrl ) : http ( )
259335 } )
260336
261- hasSmartWalletCapabilities = Boolean (
262- capabilities ?. atomicBatch ?. supported ||
263- capabilities ?. paymasterService ?. supported ||
264- capabilities ?. auxiliaryFunds ?. supported ||
265- capabilities ?. sessionKeys ?. supported
266- )
267- } catch ( capabilitiesError ) { }
337+ try {
338+ const _code = await viemClient . getCode ( { address } )
268339
269- const client = getClient ( )
270- const chain = client . chains . find ( ( chain ) => chain . id === chainId )
271- const rpcUrl = chain ?. httpRpcUrl
340+ // Cache the result
341+ cacheEntry ! . code = { value : _code , timestamp : now }
272342
273- if ( ! chain ) {
274- throw new Error ( `Chain ${ chainId } not found in relay client` )
343+ const hasCode = Boolean ( _code && _code !== '0x' )
344+ const isEIP7702Delegated = Boolean (
345+ _code && _code . toLowerCase ( ) . startsWith ( '0xef01' )
346+ )
347+ return { hasCode, isEIP7702Delegated }
348+ } catch ( getCodeError ) {
349+ throw getCodeError
350+ }
275351 }
276352
277- const viemClient = createPublicClient ( {
278- chain : chain ?. viemChain ,
279- transport : rpcUrl ? http ( rpcUrl ) : http ( )
280- } )
353+ // Get balance with caching
354+ const getNativeBalance = async ( ) => {
355+ // Check cache first
356+ console . log ( 'CHECKING BALANCE CACHE' )
357+ if (
358+ cacheEntry &&
359+ cacheEntry ?. balance &&
360+ now - cacheEntry ?. balance . timestamp < CACHE_DURATION_MS
361+ ) {
362+ console . log ( 'BALANCE CACHE HIT' )
363+ return cacheEntry ?. balance . value
364+ }
281365
282- let code
283- try {
284- code = await viemClient . getCode ( {
285- address : wallet . account . address
366+ // Fetch from RPC
367+ console . log ( 'FETCHING BALANCE FROM RPC' )
368+ const client = getClient ( )
369+ const chain = client . chains . find ( ( chain ) => chain . id === chainId )
370+ const rpcUrl = chain ?. httpRpcUrl
371+
372+ if ( ! chain ) {
373+ return BigInt ( 0 )
374+ }
375+
376+ const viemClient = createPublicClient ( {
377+ chain : chain ?. viemChain ,
378+ transport : rpcUrl ? http ( rpcUrl ) : http ( )
286379 } )
287- } catch ( getCodeError ) {
288- throw getCodeError
380+
381+ try {
382+ console . log ( 'FETCHING BALANCE FROM RPC' )
383+ const balance = await viemClient . getBalance ( { address } )
384+ console . log ( 'BALANCE FETCHED FROM RPC' )
385+ // Cache the result
386+ cacheEntry ! . balance = { value : balance , timestamp : now }
387+ return balance
388+ } catch ( error ) {
389+ return BigInt ( 0 )
390+ }
289391 }
290392
291- const hasCode = Boolean ( code && code !== '0x' )
292- const isEIP7702Delegated = Boolean (
293- code && code . toLowerCase ( ) . startsWith ( '0xef01' )
294- )
295- const isSmartWallet =
393+ // Get transaction count with caching
394+ const getTransactionCount = async ( ) => {
395+ // Check cache first
396+ console . log ( 'CHECKING TRANSACTION COUNT CACHE' )
397+ if (
398+ cacheEntry &&
399+ cacheEntry ?. txCount &&
400+ now - cacheEntry ?. txCount . timestamp < CACHE_DURATION_MS
401+ ) {
402+ console . log ( 'TRANSACTION COUNT CACHE HIT' )
403+ return cacheEntry ?. txCount . value
404+ }
405+
406+ // Fetch from RPC
407+ console . log ( 'FETCHING TRANSACTION COUNT FROM RPC' )
408+ const client = getClient ( )
409+ const chain = client . chains . find ( ( chain ) => chain . id === chainId )
410+ const rpcUrl = chain ?. httpRpcUrl
411+
412+ if ( ! chain ) {
413+ return 0
414+ }
415+
416+ const viemClient = createPublicClient ( {
417+ chain : chain ?. viemChain ,
418+ transport : rpcUrl ? http ( rpcUrl ) : http ( )
419+ } )
420+
421+ try {
422+ const txCount = await viemClient . getTransactionCount ( { address } )
423+ // Cache the result
424+ cacheEntry ! . txCount = { value : txCount , timestamp : now }
425+ return txCount
426+ } catch ( error ) {
427+ return 0
428+ }
429+ }
430+
431+ const [
432+ hasSmartWalletCapabilitiesResult ,
433+ getCodeResult ,
434+ nativeBalanceResult ,
435+ transactionCountResult
436+ ] = await Promise . allSettled ( [
437+ getSmartWalletCapabilities ( ) ,
438+ getCode ( ) ,
439+ getNativeBalance ( ) ,
440+ getTransactionCount ( )
441+ ] )
442+
443+ const hasSmartWalletCapabilities =
444+ hasSmartWalletCapabilitiesResult . status === 'fulfilled'
445+ ? hasSmartWalletCapabilitiesResult . value
446+ : false
447+ const { hasCode, isEIP7702Delegated } =
448+ getCodeResult . status === 'fulfilled'
449+ ? getCodeResult . value
450+ : { hasCode : false , isEIP7702Delegated : false }
451+ const nativeBalance =
452+ nativeBalanceResult . status === 'fulfilled'
453+ ? nativeBalanceResult . value
454+ : BigInt ( 0 )
455+ const transactionCount =
456+ transactionCountResult . status === 'fulfilled'
457+ ? transactionCountResult . value
458+ : 0
459+
460+ let isSmartWallet =
296461 hasSmartWalletCapabilities || hasCode || isEIP7702Delegated
297- const isEOA = ! isSmartWallet
298462
299- return { isEOA, isEIP7702Delegated }
463+ // If balance is zero or transaction count is <= 1, it's likely a smart wallet
464+ if ( nativeBalance === BigInt ( 0 ) || transactionCount <= 1 ) {
465+ isSmartWallet = true
466+ }
467+
468+ return { isEOA : ! isSmartWallet , isEIP7702Delegated }
300469 } catch ( error ) {
301- return { isEOA : false , isEIP7702Delegated : false }
470+ // On error, default to explicit deposit (isEOA: false) for safety
471+ return {
472+ isEOA : false ,
473+ isEIP7702Delegated : false
474+ }
302475 }
303476 }
304477 }
0 commit comments