fun wallet optimizations after sending
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -246,6 +246,12 @@ export default function getFormattedHomepageTxsForSpark(props) {
|
||||
paymentDetails.LRC20Token !== USDB_TOKEN_ID
|
||||
)
|
||||
continue;
|
||||
|
||||
if (
|
||||
paymentDetails.senderIdentityPublicKey ===
|
||||
process.env.SPARK_IDENTITY_PUBKEY
|
||||
)
|
||||
continue;
|
||||
if (shownTxs.has(currentTransaction.sparkID)) continue;
|
||||
if (isLRC20Payment && !hasSavedTokenData) continue;
|
||||
if (paymentStatus === TRANSACTION_CONSTANTS.FAILED) continue;
|
||||
@@ -588,7 +594,7 @@ export const UserTransaction = memo(function UserTransaction({
|
||||
: transaction.details.amount
|
||||
}
|
||||
useCustomLabel={isLRC20Payment}
|
||||
customLabel={token?.tokenTicker?.slice(0, 3)}
|
||||
customLabel={token?.tokenTicker}
|
||||
useMillionDenomination={true}
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -44,7 +44,7 @@ const getMnemonicHash = mnemonic => {
|
||||
return mnemonicHashCache.get(mnemonic);
|
||||
};
|
||||
|
||||
const getWallet = async mnemonic => {
|
||||
export const getWallet = async mnemonic => {
|
||||
const hash = getMnemonicHash(mnemonic);
|
||||
let wallet = sparkWallet[hash];
|
||||
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
import { AppState } from 'react-native';
|
||||
import { getWallet, selectSparkRuntime } from './index';
|
||||
import sha256Hash from '../hash';
|
||||
import {
|
||||
sendWebViewRequestGlobal,
|
||||
OPERATION_TYPES,
|
||||
} from '../../../context-store/webViewContext';
|
||||
|
||||
// Global state for optimization
|
||||
const optimizationState = {
|
||||
isLeafOptimizationRunning: false,
|
||||
isTokenOptimizationRunning: false,
|
||||
controller: null,
|
||||
timeout: null,
|
||||
};
|
||||
|
||||
/**
|
||||
* Abort any running optimization immediately
|
||||
*/
|
||||
export const abortOptimization = async mnemonic => {
|
||||
console.log('Aborting optimization immediately...');
|
||||
|
||||
const runtime = await selectSparkRuntime(mnemonic);
|
||||
|
||||
if (runtime === 'native') {
|
||||
if (optimizationState.controller) {
|
||||
optimizationState.controller.abort();
|
||||
optimizationState.controller = null;
|
||||
}
|
||||
|
||||
if (optimizationState.timeout) {
|
||||
clearTimeout(optimizationState.timeout);
|
||||
optimizationState.timeout = null;
|
||||
}
|
||||
|
||||
optimizationState.isLeafOptimizationRunning = false;
|
||||
optimizationState.isTokenOptimizationRunning = false;
|
||||
} else {
|
||||
await sendWebViewRequestGlobal(OPERATION_TYPES.abortOptimization, {
|
||||
mnemonic,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if any optimization is currently running
|
||||
*/
|
||||
export const isOptimizationRunning = async mnemonic => {
|
||||
const runtime = await selectSparkRuntime(mnemonic);
|
||||
if (runtime === 'native') {
|
||||
return (
|
||||
optimizationState.isLeafOptimizationRunning ||
|
||||
optimizationState.isTokenOptimizationRunning
|
||||
);
|
||||
} else {
|
||||
const result = await sendWebViewRequestGlobal(
|
||||
OPERATION_TYPES.isOptimizationRunning,
|
||||
{ mnemonic },
|
||||
);
|
||||
console.log('is opimization running result');
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if optimization is needed for a wallet
|
||||
*/
|
||||
export const checkIfOptimizationNeeded = async mnemonic => {
|
||||
try {
|
||||
const runtime = await selectSparkRuntime(mnemonic);
|
||||
|
||||
if (runtime === 'native') {
|
||||
const wallet = await getWallet(mnemonic);
|
||||
if (!wallet) return false;
|
||||
|
||||
const [isLeafOptInProgress, isTokenOptInProgress] = await Promise.all([
|
||||
wallet.isOptimizationInProgress(),
|
||||
wallet.isTokenOptimizationInProgress(),
|
||||
]);
|
||||
|
||||
// If already in progress, don't start another
|
||||
if (isLeafOptInProgress || isTokenOptInProgress) {
|
||||
return false;
|
||||
}
|
||||
const leaves = await wallet.getLeaves();
|
||||
console.log(leaves);
|
||||
|
||||
return false;
|
||||
} else {
|
||||
const result = await sendWebViewRequestGlobal(
|
||||
OPERATION_TYPES.checkIfOptimizationNeeded,
|
||||
{ mnemonic },
|
||||
);
|
||||
return result?.needed || false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error checking if optimization needed:', error);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run leaf optimization
|
||||
*/
|
||||
export const runLeafOptimization = async (mnemonic, identityPubKey) => {
|
||||
if (await isOptimizationRunning(mnemonic)) {
|
||||
console.log('Optimization already running, skipping');
|
||||
return { didWork: false, reason: 'already_running' };
|
||||
}
|
||||
|
||||
if (AppState.currentState !== 'active') {
|
||||
console.log('App not active, skipping optimization');
|
||||
return { didWork: false, reason: 'app_not_active' };
|
||||
}
|
||||
|
||||
if (!identityPubKey) {
|
||||
console.log('No identity pub key, skipping optimization');
|
||||
return { didWork: false, reason: 'no_identity' };
|
||||
}
|
||||
|
||||
try {
|
||||
optimizationState.isLeafOptimizationRunning = true;
|
||||
const runtime = await selectSparkRuntime(mnemonic);
|
||||
|
||||
console.log('Starting leaf optimization...');
|
||||
|
||||
if (runtime === 'native') {
|
||||
const wallet = await getWallet(mnemonic);
|
||||
if (!wallet) {
|
||||
throw new Error('Wallet not initialized');
|
||||
}
|
||||
for await (const progress of wallet.optimizeLeaves()) {
|
||||
// Store controller for abortion
|
||||
optimizationState.controller = progress.controller;
|
||||
|
||||
console.log(
|
||||
`Optimization progress: ${progress.step}/${progress.total}`,
|
||||
);
|
||||
// Check if we should abort
|
||||
if (!optimizationState.isLeafOptimizationRunning) {
|
||||
console.log('Optimization aborted by external signal');
|
||||
progress.controller.abort();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// WebView optimization
|
||||
await sendWebViewRequestGlobal(OPERATION_TYPES.runLeafOptimization, {
|
||||
mnemonic,
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Leaf optimization complete');
|
||||
return { didWork: true };
|
||||
} catch (error) {
|
||||
console.error('Error during leaf optimization:', error);
|
||||
return { didWork: false, error: error.message };
|
||||
} finally {
|
||||
optimizationState.isLeafOptimizationRunning = false;
|
||||
optimizationState.controller = null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Run token optimization
|
||||
*/
|
||||
export const runTokenOptimization = async (mnemonic, identityPubKey) => {
|
||||
if (await isOptimizationRunning(mnemonic)) {
|
||||
console.log('Optimization already running, skipping');
|
||||
return { didWork: false, reason: 'already_running' };
|
||||
}
|
||||
|
||||
if (AppState.currentState !== 'active') {
|
||||
console.log('App not active, skipping token optimization');
|
||||
return { didWork: false, reason: 'app_not_active' };
|
||||
}
|
||||
|
||||
if (!identityPubKey) {
|
||||
console.log('No identity pub key, skipping token optimization');
|
||||
return { didWork: false, reason: 'no_identity' };
|
||||
}
|
||||
|
||||
try {
|
||||
optimizationState.isTokenOptimizationRunning = true;
|
||||
const runtime = await selectSparkRuntime(mnemonic);
|
||||
|
||||
console.log('Starting token optimization...');
|
||||
|
||||
if (runtime === 'native') {
|
||||
const wallet = await getWallet(mnemonic);
|
||||
if (!wallet) {
|
||||
throw new Error('Wallet not initialized');
|
||||
}
|
||||
await wallet.optimizeTokenOutputs();
|
||||
} else {
|
||||
await sendWebViewRequestGlobal(OPERATION_TYPES.runTokenOptimization, {
|
||||
mnemonic,
|
||||
});
|
||||
}
|
||||
|
||||
console.log('Token optimization complete');
|
||||
return { didWork: true };
|
||||
} catch (error) {
|
||||
console.error('Error during token optimization:', error);
|
||||
return { didWork: false, error: error.message };
|
||||
} finally {
|
||||
optimizationState.isTokenOptimizationRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Schedule optimization to run after a delay
|
||||
*/
|
||||
export const scheduleOptimization = async (
|
||||
mnemonic,
|
||||
identityPubKey,
|
||||
delayMs = 5000,
|
||||
) => {
|
||||
// Clear any existing scheduled optimization
|
||||
if (optimizationState.timeout) {
|
||||
clearTimeout(optimizationState.timeout);
|
||||
optimizationState.timeout = null;
|
||||
}
|
||||
|
||||
console.log(`Scheduling optimization in ${delayMs}ms...`);
|
||||
|
||||
optimizationState.timeout = setTimeout(async () => {
|
||||
if (AppState.currentState !== 'active') {
|
||||
console.log('App not active, skipping scheduled optimization');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!identityPubKey) {
|
||||
console.log('No identity pub key, skipping scheduled optimization');
|
||||
return;
|
||||
}
|
||||
|
||||
const needed = await checkIfOptimizationNeeded(mnemonic);
|
||||
if (needed) {
|
||||
console.log('Running scheduled optimization...');
|
||||
// Run leaf optimization first, then token optimization
|
||||
const leafResult = await runLeafOptimization(mnemonic, identityPubKey);
|
||||
if (leafResult.didWork) {
|
||||
await runTokenOptimization(mnemonic, identityPubKey);
|
||||
}
|
||||
} else {
|
||||
console.log('Optimization not needed at this time');
|
||||
}
|
||||
}, delayMs);
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleanup optimization state (call on logout/unmount)
|
||||
*/
|
||||
export const cleanupOptimization = mnemoinc => {
|
||||
abortOptimization(mnemoinc);
|
||||
};
|
||||
@@ -33,6 +33,11 @@ import {
|
||||
payLightningWithToken,
|
||||
USD_ASSET_ADDRESS,
|
||||
} from './flashnet';
|
||||
import {
|
||||
abortOptimization,
|
||||
isOptimizationRunning,
|
||||
scheduleOptimization,
|
||||
} from '../spark/optimization';
|
||||
import { setFlashnetTransfer } from './handleFlashnetTransferIds';
|
||||
import {
|
||||
addSingleUnpaidSparkLightningTransaction,
|
||||
@@ -65,6 +70,15 @@ export const sparkPaymenWrapper = async ({
|
||||
}) => {
|
||||
try {
|
||||
console.log('Begining spark payment');
|
||||
|
||||
if (!getFee && (await isOptimizationRunning(mnemonic))) {
|
||||
console.log(
|
||||
'Optimization in progress, aborting immediately for payment...',
|
||||
);
|
||||
await abortOptimization(mnemonic);
|
||||
// Small delay to ensure abort completes
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
}
|
||||
// if (!sparkWallet[sha256Hash(mnemonic)])
|
||||
// throw new Error('sparkWallet not initialized');
|
||||
const supportFee = 0;
|
||||
@@ -516,6 +530,11 @@ export const sparkPaymenWrapper = async ({
|
||||
if (sparkInformation.identityPubKey) {
|
||||
await bulkUpdateSparkTransactions([response], 'paymentWrapperTx', 0);
|
||||
}
|
||||
|
||||
if (!getFee) {
|
||||
console.log('Scheduling post-payment optimization...');
|
||||
scheduleOptimization(mnemonic, sparkInformation.identityPubKey, 2000);
|
||||
}
|
||||
return {
|
||||
didWork: true,
|
||||
response,
|
||||
|
||||
@@ -67,6 +67,12 @@ import {
|
||||
createRestorePoller,
|
||||
} from '../app/functions/pollingManager';
|
||||
import { USDB_TOKEN_ID } from '../app/constants';
|
||||
import {
|
||||
cleanupOptimization,
|
||||
checkIfOptimizationNeeded,
|
||||
runLeafOptimization,
|
||||
runTokenOptimization,
|
||||
} from '../app/functions/spark/optimization';
|
||||
|
||||
export const isSendingPayingEventEmiiter = new EventEmitter();
|
||||
export const SENDING_PAYMENT_EVENT_NAME = 'SENDING_PAYMENT_EVENT';
|
||||
@@ -642,6 +648,13 @@ const SparkWalletProvider = ({ children }) => {
|
||||
|
||||
const details = parsedTx?.details;
|
||||
|
||||
if (
|
||||
details.senderIdentityPublicKey === process.env.SPARK_IDENTITY_PUBKEY
|
||||
) {
|
||||
console.log('Refund from Spark, do not show tosat here');
|
||||
return;
|
||||
}
|
||||
|
||||
if (new Date(details.time).getTime() < sessionTimeRef.current) {
|
||||
console.log(
|
||||
'created before session time was set, skipping confirm tx page navigation',
|
||||
@@ -978,10 +991,46 @@ const SparkWalletProvider = ({ children }) => {
|
||||
currentPollingMnemonicRef.current = null;
|
||||
};
|
||||
|
||||
// optimizations for leaves and tokens
|
||||
useEffect(() => {
|
||||
if (!sparkInformation.didConnect) return;
|
||||
if (!sparkInformation.identityPubKey) return;
|
||||
if (!didGetToHomepage) return;
|
||||
if (AppState.currentState !== 'active') return;
|
||||
|
||||
const runInitialOptimizationCheck = async () => {
|
||||
if (!sparkInfoRef.current.identityPubKey) return;
|
||||
if (AppState.currentState !== 'active') return;
|
||||
|
||||
const needed = await checkIfOptimizationNeeded(
|
||||
currentMnemonicRef.current,
|
||||
);
|
||||
|
||||
if (needed) {
|
||||
console.log('Running initial optimization check...');
|
||||
await runLeafOptimization(
|
||||
currentMnemonicRef.current,
|
||||
sparkInfoRef.current.identityPubKey,
|
||||
);
|
||||
await runTokenOptimization(
|
||||
currentMnemonicRef.current,
|
||||
sparkInfoRef.current.identityPubKey,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
runInitialOptimizationCheck();
|
||||
}, [
|
||||
sparkInformation.didConnect,
|
||||
sparkInformation.identityPubKey,
|
||||
didGetToHomepage,
|
||||
]);
|
||||
|
||||
const resetSparkState = useCallback(async (internalRefresh = false) => {
|
||||
// Reset refs to initial values
|
||||
await removeListeners(true);
|
||||
clearMnemonicCache();
|
||||
cleanupOptimization(prevAccountMnemoincRef.current);
|
||||
prevAccountMnemoincRef.current = null;
|
||||
isRunningAddListeners.current = false;
|
||||
if (depositAddressIntervalRef.current) {
|
||||
|
||||
@@ -77,6 +77,13 @@ export const OPERATION_TYPES = {
|
||||
checkClawbackStatus: 'checkClawbackStatus',
|
||||
requestBatchClawback: 'requestBatchClawback',
|
||||
listClawbackableTransfers: 'listClawbackableTransfers',
|
||||
|
||||
// Wallet optimizations
|
||||
abortOptimization: 'abortOptimization',
|
||||
isOptimizationRunning: 'isOptimizationRunning',
|
||||
checkIfOptimizationNeeded: 'checkIfOptimizationNeeded',
|
||||
runLeafOptimization: 'runLeafOptimization',
|
||||
runTokenOptimization: 'runTokenOptimization',
|
||||
};
|
||||
|
||||
const longOperations = [
|
||||
@@ -94,6 +101,8 @@ const longOperations = [
|
||||
OPERATION_TYPES.swapTokenToBitcoin,
|
||||
OPERATION_TYPES.payLightningWithToken,
|
||||
OPERATION_TYPES.requestClawback,
|
||||
OPERATION_TYPES.runLeafOptimization,
|
||||
OPERATION_TYPES.runTokenOptimization,
|
||||
];
|
||||
|
||||
const mediumOperations = [
|
||||
@@ -111,6 +120,7 @@ const mediumOperations = [
|
||||
OPERATION_TYPES.setPrivacyEnabled,
|
||||
OPERATION_TYPES.simulateSwap,
|
||||
OPERATION_TYPES.requestBatchClawback,
|
||||
OPERATION_TYPES.checkIfOptimizationNeeded,
|
||||
];
|
||||
|
||||
const rejectIfNotConnectedToInternet = [
|
||||
|
||||
Reference in New Issue
Block a user