diff --git a/hooks/use-auto-withdraw.ts b/hooks/use-auto-withdraw.ts index 4c322b0..8553c67 100644 --- a/hooks/use-auto-withdraw.ts +++ b/hooks/use-auto-withdraw.ts @@ -69,18 +69,31 @@ export function useAutoWithdraw(stream: StreamData | null) { const { network } = useNetwork(); const [settings, setSettings] = useState(DEFAULT_SETTINGS); + // Ref that always mirrors the latest settings so callbacks that need to + // persist to localStorage never read a stale closure value. + const settingsRef = useRef(DEFAULT_SETTINGS); const [lastAutoWithdraw, setLastAutoWithdraw] = useState(null); const [autoWithdrawPending, setAutoWithdrawPending] = useState(false); const autoWithdrawPendingRef = useRef(false); const intervalRef = useRef | null>(null); useEffect(() => { - if (stream) setSettings(loadSettings(stream.id)); + if (stream) { + const loaded = loadSettings(stream.id); + settingsRef.current = loaded; + setSettings(loaded); + } }, [stream?.id]); const updateSettings = useCallback( (update: Partial) => { if (!stream) return; + setSettings((prev) => { + const next = { ...prev, ...update }; + settingsRef.current = next; + saveSettings(stream.id, next); + return next; + }); const next = { ...settings, ...update }; if (update.intervalHours !== undefined) { next.intervalHours = clampIntervalHours(update.intervalHours); @@ -88,7 +101,7 @@ export function useAutoWithdraw(stream: StreamData | null) { setSettings(next); saveSettings(stream.id, next); }, - [stream, settings], + [stream], ); const addWithdrawalHistory = useCallback( @@ -99,6 +112,7 @@ export function useAutoWithdraw(stream: StreamData | null) { ...prev, withdrawalHistory: [entry, ...prev.withdrawalHistory.slice(0, 99)], } as AutoWithdrawSettings; + settingsRef.current = next; saveSettings(stream.id, next); return next; }); @@ -108,25 +122,27 @@ export function useAutoWithdraw(stream: StreamData | null) { const calculateWithdrawAmount = useCallback( (withdrawable: bigint, stream: StreamData): bigint => { - const minAmount = BigInt(settings.minAmountRaw || "0"); - const maxLimit = BigInt(settings.maxSafetyLimitRaw || "0"); + // Always read from the ref so this never uses a stale closure snapshot. + const s = settingsRef.current; + const minAmount = BigInt(s.minAmountRaw || "0"); + const maxLimit = BigInt(s.maxSafetyLimitRaw || "0"); if (withdrawable <= 0n) return 0n; if (minAmount > 0n && withdrawable < minAmount) return 0n; let amount = withdrawable; - switch (settings.strategy) { + switch (s.strategy) { case "threshold-based": { const threshold = - (stream.depositedAmount * BigInt(settings.thresholdPercentage)) / + (stream.depositedAmount * BigInt(s.thresholdPercentage)) / 100n; if (withdrawable < threshold) return 0n; amount = withdrawable; break; } case "gas-optimized": { - const lastWithdraw = settings.withdrawalHistory[0]; + const lastWithdraw = s.withdrawalHistory[0]; const daysSinceLastWithdraw = lastWithdraw ? (Date.now() - lastWithdraw.timestamp) / (1000 * 60 * 60 * 24) : Infinity; @@ -151,7 +167,8 @@ export function useAutoWithdraw(stream: StreamData | null) { return amount; }, - [settings], + // No settings dependency — reads live value through ref instead. + [], ); useEffect(() => {