-
Notifications
You must be signed in to change notification settings - Fork 13
[TASK-13531] Fix: Automatic balance refresh not working #1161
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: peanut-wallet-dev
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -13,7 +13,7 @@ import { useAppDispatch, useWalletStore } from '@/redux/hooks' | |||||||||||||
import { walletActions } from '@/redux/slices/wallet-slice' | ||||||||||||||
import { formatAmount } from '@/utils' | ||||||||||||||
import { interfaces as peanutInterfaces } from '@squirrel-labs/peanut-sdk' | ||||||||||||||
import { useCallback, useEffect, useState } from 'react' | ||||||||||||||
import { useCallback, useEffect, useState, useRef } from 'react' | ||||||||||||||
import type { Hex, Address } from 'viem' | ||||||||||||||
import { erc20Abi, formatUnits, parseUnits, encodeFunctionData, getAddress } from 'viem' | ||||||||||||||
import { useZeroDev } from '../useZeroDev' | ||||||||||||||
|
@@ -24,6 +24,9 @@ export const useWallet = () => { | |||||||||||||
const [isFetchingBalance, setIsFetchingBalance] = useState(true) | ||||||||||||||
const [isFetchingRewardBalance, setIsFetchingRewardBalance] = useState(true) | ||||||||||||||
const { balance } = useWalletStore() | ||||||||||||||
const eventListenerRef = useRef<(() => void) | null>(null) | ||||||||||||||
const retryTimerRef = useRef<NodeJS.Timeout | null>(null) | ||||||||||||||
const TIMEOUT_INTERVAL = 2000 | ||||||||||||||
|
||||||||||||||
Comment on lines
+27
to
30
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fix timer ref type for browser env (Next.js client) - const retryTimerRef = useRef<NodeJS.Timeout | null>(null)
+ const retryTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null) 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents
|
||||||||||||||
const sendMoney = useCallback( | ||||||||||||||
async (toAddress: Address, amountInUsd: string) => { | ||||||||||||||
|
@@ -103,11 +106,88 @@ export const useWallet = () => { | |||||||||||||
}) | ||||||||||||||
}, [address, dispatch]) | ||||||||||||||
|
||||||||||||||
// Set up real-time balance monitoring via contract events | ||||||||||||||
const setupBalanceMonitoring = useCallback(() => { | ||||||||||||||
if (!address) return | ||||||||||||||
|
||||||||||||||
// Clean up previous event listener and retry timer | ||||||||||||||
eventListenerRef.current?.() | ||||||||||||||
if (retryTimerRef.current) { | ||||||||||||||
clearTimeout(retryTimerRef.current) | ||||||||||||||
retryTimerRef.current = null | ||||||||||||||
} | ||||||||||||||
|
||||||||||||||
try { | ||||||||||||||
// Create two separate watchers for incoming and outgoing transfers | ||||||||||||||
// Watch for incoming transfers (to: address) | ||||||||||||||
const watchIncoming = peanutPublicClient.watchContractEvent({ | ||||||||||||||
address: PEANUT_WALLET_TOKEN, | ||||||||||||||
abi: erc20Abi, | ||||||||||||||
eventName: 'Transfer', | ||||||||||||||
args: { | ||||||||||||||
to: address as `0x${string}`, | ||||||||||||||
}, | ||||||||||||||
onLogs: () => { | ||||||||||||||
fetchBalance() | ||||||||||||||
}, | ||||||||||||||
onError: (error) => { | ||||||||||||||
console.error('Contract event listener error (incoming):', error) | ||||||||||||||
if (retryTimerRef.current) { | ||||||||||||||
clearTimeout(retryTimerRef.current) | ||||||||||||||
} | ||||||||||||||
retryTimerRef.current = setTimeout(() => setupBalanceMonitoring(), TIMEOUT_INTERVAL) | ||||||||||||||
}, | ||||||||||||||
Zishan-7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||
}) | ||||||||||||||
|
||||||||||||||
// Watch for outgoing transfers (from: address) | ||||||||||||||
const watchOutgoing = peanutPublicClient.watchContractEvent({ | ||||||||||||||
address: PEANUT_WALLET_TOKEN, | ||||||||||||||
abi: erc20Abi, | ||||||||||||||
eventName: 'Transfer', | ||||||||||||||
args: { | ||||||||||||||
from: address as `0x${string}`, | ||||||||||||||
}, | ||||||||||||||
onLogs: () => { | ||||||||||||||
fetchBalance() | ||||||||||||||
}, | ||||||||||||||
onError: (error) => { | ||||||||||||||
console.error('Contract event listener error (outgoing):', error) | ||||||||||||||
if (retryTimerRef.current) { | ||||||||||||||
clearTimeout(retryTimerRef.current) | ||||||||||||||
} | ||||||||||||||
retryTimerRef.current = setTimeout(() => setupBalanceMonitoring(), TIMEOUT_INTERVAL) | ||||||||||||||
}, | ||||||||||||||
}) | ||||||||||||||
|
||||||||||||||
// Store cleanup function that cleans up both watchers | ||||||||||||||
eventListenerRef.current = () => { | ||||||||||||||
watchIncoming() | ||||||||||||||
watchOutgoing() | ||||||||||||||
} | ||||||||||||||
} catch (error) { | ||||||||||||||
console.error('Failed to setup balance monitoring:', error) | ||||||||||||||
} | ||||||||||||||
}, [address, fetchBalance]) | ||||||||||||||
Comment on lines
+123
to
+170
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue: this is not too efficient with credits Some back of napkin calculations: viem's watchContractEvent does the following: ![]() Which in infura (ignoring alchemy for now) it has these costs: ![]() Lets assume a session of 1 minute, that would be 30 calls to getFilterChanges (with the 2sec interval) 30 calls x 140 credits = 4200 credits I strongly recommend doing this with websocket triggered from the deposit indexer. |
||||||||||||||
|
||||||||||||||
useEffect(() => { | ||||||||||||||
if (!address) return | ||||||||||||||
|
||||||||||||||
// Initial balance fetch | ||||||||||||||
fetchBalance() | ||||||||||||||
getRewardWalletBalance() | ||||||||||||||
}, [address, fetchBalance, getRewardWalletBalance]) | ||||||||||||||
|
||||||||||||||
// Setup real-time monitoring | ||||||||||||||
setupBalanceMonitoring() | ||||||||||||||
|
||||||||||||||
// Cleanup on unmount or address change | ||||||||||||||
return () => { | ||||||||||||||
eventListenerRef.current?.() | ||||||||||||||
if (retryTimerRef.current) { | ||||||||||||||
clearTimeout(retryTimerRef.current) | ||||||||||||||
retryTimerRef.current = null | ||||||||||||||
} | ||||||||||||||
} | ||||||||||||||
}, [address, fetchBalance, getRewardWalletBalance, setupBalanceMonitoring]) | ||||||||||||||
|
||||||||||||||
return { | ||||||||||||||
address: address!, | ||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.