Files
Blake KaufmanandGitHub d93b29d6c8 Auto buy (#882)
* building send and replace settings context

* adding translations

* fixing double parse

* adding spend and replace flags to send txs

* adding listener to handle sar payments

* add tx descripion handler for sar payments in restore

* making sure sar key is fetched during app load

* split toast manager and toast continer

* adding tests

* adding custom sar payment title to toast message

* removing spark sdk navigation

* adding sarPayment mark to transaction table

* pass poolInfoRef from context to remove fetch if value is already set

* remove spark naviagtion listener

* fix btc -> usd swaps not calling sar

* finalize spend and replace

* fix timeout mismatch

* adding usablePaymentMethod to narrow eligiblility

* block requsets if account switches

* remove duplicate storage and keep all referances to keyscontext

* steralize writes to fix overlap issue
2026-05-30 14:38:06 -04:00

124 lines
3.8 KiB
JavaScript

import { useEffect, createContext, useRef } from 'react';
import { useTranslation } from 'react-i18next';
import {
sparkTransactionsEventEmitter,
SPARK_TX_UPDATE_ENVENT_NAME,
ensureSparkDatabaseReady,
} from '../app/functions/spark/transactions';
import { SPEND_AND_REPLACE_STORAGE_KEY } from '../app/constants';
import { useGlobalContextProvider } from './context';
import { useSparkWallet } from './sparkContext';
import { useKeysContext } from './keys';
import { processSpendAndReplaceIntents } from '../app/functions/spark/spendAndReplace';
import { useFlashnet } from './flashnetContext';
const SpendAndReplaceContext = createContext(null);
export function SpendAndReplaceProvider({ children }) {
const { masterInfoObject } = useGlobalContextProvider();
const { sparkInformation } = useSparkWallet();
const { accountMnemoinc } = useKeysContext();
const { t } = useTranslation();
const { poolInfoRef } = useFlashnet();
const isProcessingRef = useRef(false);
const needsRerunRef = useRef(false);
const latestAccountRef = useRef({
accountId: '',
sparkAddress: '',
mnemonic: '',
});
useEffect(() => {
latestAccountRef.current = {
accountId: sparkInformation?.identityPubKey || '',
sparkAddress: sparkInformation?.sparkAddress || '',
mnemonic: accountMnemoinc || '',
};
}, [
sparkInformation?.identityPubKey,
sparkInformation?.sparkAddress,
accountMnemoinc,
]);
useEffect(() => {
const handleTransactionUpdate = async () => {
if (!masterInfoObject[SPEND_AND_REPLACE_STORAGE_KEY]?.isEnabled) return;
const accountSnapshot = {
accountId: sparkInformation?.identityPubKey || '',
sparkAddress: sparkInformation?.sparkAddress || '',
mnemonic: accountMnemoinc || '',
};
if (
!accountSnapshot.accountId ||
!accountSnapshot.sparkAddress ||
!accountSnapshot.mnemonic
) {
return;
}
const isSameActiveAccount = () => {
const latestAccount = latestAccountRef.current;
return (
latestAccount.accountId === accountSnapshot.accountId &&
latestAccount.sparkAddress === accountSnapshot.sparkAddress &&
latestAccount.mnemonic === accountSnapshot.mnemonic
);
};
if (!isSameActiveAccount()) return;
if (isProcessingRef.current) {
needsRerunRef.current = true;
return;
}
isProcessingRef.current = true;
try {
// Drain loop: rerun while events landed mid-pass, until a pass finds no
// freshly-claimable rows.
do {
needsRerunRef.current = false;
if (!isSameActiveAccount()) return;
const db = await ensureSparkDatabaseReady();
await processSpendAndReplaceIntents({
db,
accountId: accountSnapshot.accountId,
mnemonic: accountSnapshot.mnemonic,
sparkAddress: accountSnapshot.sparkAddress,
t,
poolInfoRef,
isSameActiveAccount,
});
} while (needsRerunRef.current && isSameActiveAccount());
} catch (err) {
console.error('SpendAndReplace handler error:', err);
} finally {
isProcessingRef.current = false;
}
};
sparkTransactionsEventEmitter.on(
SPARK_TX_UPDATE_ENVENT_NAME,
handleTransactionUpdate,
);
// Run once on mount to resume payments that confirmed while the app was
// closed (discovered as unclaimed on next launch).
handleTransactionUpdate();
return () => {
sparkTransactionsEventEmitter.off(
SPARK_TX_UPDATE_ENVENT_NAME,
handleTransactionUpdate,
);
};
}, [masterInfoObject, sparkInformation, accountMnemoinc, t]);
return (
<SpendAndReplaceContext.Provider value={null}>
{children}
</SpendAndReplaceContext.Provider>
);
}