removing reliance on flashnet to send

This commit is contained in:
Blake Kaufman
2026-01-20 10:52:47 -05:00
parent f7d0f705bc
commit df1b781643
16 changed files with 133 additions and 63 deletions
@@ -238,6 +238,7 @@ export default function SendAndRequestPage(props) {
isUsingLRC20: false,
useFullTokensDisplay: false,
selectedPaymentMethod,
sparkInformation,
});
const handleSelectPaymentMethod = useCallback(() => {
@@ -299,6 +300,9 @@ export default function SendAndRequestPage(props) {
currency2: t('constants.bitcoin_upper'),
});
}
if (!sparkInformation?.didConnectToFlashnet) {
return t('wallet.sendPages.acceptButton.flashnetOffineError');
}
}
// Validate balance sufficiency
@@ -333,6 +337,7 @@ export default function SendAndRequestPage(props) {
masterInfoObject,
fiatStats,
t,
sparkInformation?.didConnectToFlashnet,
]);
console.log(getValidationError);
@@ -43,6 +43,9 @@ export default function usePaymentValidation({
masterInfoObject,
fiatStats,
inputDenomination,
//can perform swap
sparkInformation,
}) {
const validation = useMemo(() => {
// Initialize validation result
@@ -85,6 +88,7 @@ export default function usePaymentValidation({
finalPaymentMethod,
determinePaymentMethod,
selectedPaymentMethod,
'payment methods',
);
if (isUsingLRC20) {
@@ -185,6 +189,10 @@ export default function usePaymentValidation({
return result;
}
}
if (!sparkInformation?.didConnectToFlashnet) {
result.errors.push('FLASHNET_NOT_INITIALIZED');
return result;
}
}
const hasSufficientBalance =
@@ -225,6 +233,7 @@ export default function usePaymentValidation({
maxLNURLSatAmount,
isDecoding,
canEditAmount,
sparkInformation?.didConnectToFlashnet,
]);
/**
@@ -326,6 +335,9 @@ export default function usePaymentValidation({
ZERO_AMOUNT_INVOICE_SWAP_ERROR: t(
'wallet.sendPages.sendPaymentScreen.zeroAmountInvoiceDollarPayments',
),
FLASHNET_NOT_INITIALIZED: t(
'wallet.sendPages.acceptButton.flashnetOffineError',
),
};
return errorMessages[errorCode] || errorCode;
@@ -120,16 +120,19 @@ export default async function processBolt11Invoice(input, context) {
if (usdPromiseIndex !== -1) {
const paymentQuote = results[usdPromiseIndex];
if (!paymentQuote.didWork) throw new Error(paymentQuote.error);
swapPaymentQuote = {
...paymentQuote.quote,
bitcoinBalance,
dollarBalanceSat,
};
fee = {
fee: paymentQuote.quote.fee,
supportFee: 0,
};
if (!paymentQuote.didWork && !needBtcFee)
throw new Error(paymentQuote.error);
if (paymentQuote.didWork) {
swapPaymentQuote = {
...paymentQuote.quote,
bitcoinBalance,
dollarBalanceSat,
};
fee = {
fee: paymentQuote.quote.fee,
supportFee: 0,
};
}
}
if (btcPromiseIndex !== -1) {
@@ -177,14 +177,17 @@ export default async function processLNUrlPay(input, context) {
// Process USD quote result
if (usdPromiseIndex !== -1) {
const paymentQuote = results[usdPromiseIndex];
if (!paymentQuote.didWork) throw new Error(paymentQuote.error);
swapPaymentQuote = {
...paymentQuote.quote,
bitcoinBalance,
dollarBalanceSat,
};
paymentFee = paymentQuote.quote.fee;
supportFee = 0;
if (!paymentQuote.didWork && !needBtcFee)
throw new Error(paymentQuote.error);
if (paymentQuote.didWork) {
swapPaymentQuote = {
...paymentQuote.quote,
bitcoinBalance,
dollarBalanceSat,
};
paymentFee = paymentQuote.quote.fee;
supportFee = 0;
}
}
// Process BTC fee result
@@ -91,6 +91,18 @@ export default async function processSparkAddress(input, context) {
((!usablePaymentMethod || usablePaymentMethod === 'user-choice') &&
bitcoinBalance >= amountMsat / 1000);
// Determine if non-swap fallbacks are available
const canDoDirectBtcPayment =
needBtcPath &&
!(
addressInfo.expectedReceive === 'tokens' &&
addressInfo.expectedToken === USDB_TOKEN_ID
);
const canDoDirectUsdPayment =
needUsdPath &&
addressInfo.expectedReceive === 'tokens' &&
(!addressInfo.expectedToken ||
addressInfo.expectedToken === USDB_TOKEN_ID);
// Check if we have cached values
const hasCachedQuote =
typeof paymentInfo.swapPaymentQuote === 'object' &&
@@ -214,26 +226,30 @@ export default async function processSparkAddress(input, context) {
addressInfo.supportFee = paymentInfo.supportFee;
} else if (results.usdSwap) {
const { result, usdAmount } = results.usdSwap;
if (!result.didWork) throw new Error(result.error);
// Only throw if USD swap failed AND we don't have a BTC fallback
if (!result.didWork && !canDoDirectBtcPayment)
throw new Error(result.error);
const fees = result.simulation.feePaidAssetIn;
const satFee = dollarsToSats(
fees / 1000000,
poolInfoRef.currentPriceAInB,
);
if (result.didWork) {
const fees = result.simulation.feePaidAssetIn;
const satFee = dollarsToSats(
fees / 1000000,
poolInfoRef.currentPriceAInB,
);
addressInfo.paymentFee = satFee;
addressInfo.supportFee = 0;
swapPaymentQuote = {
warn: parseFloat(result.simulation.priceImpact) > 3,
poolId: poolInfoRef.lpPublicKey,
assetInAddress: USD_ASSET_ADDRESS,
assetOutAddress: BTC_ASSET_ADDRESS,
amountIn: usdAmount,
satFee,
bitcoinBalance,
dollarBalanceSat,
};
addressInfo.paymentFee = satFee;
addressInfo.supportFee = 0;
swapPaymentQuote = {
warn: parseFloat(result.simulation.priceImpact) > 3,
poolId: poolInfoRef.lpPublicKey,
assetInAddress: USD_ASSET_ADDRESS,
assetOutAddress: BTC_ASSET_ADDRESS,
amountIn: usdAmount,
satFee,
bitcoinBalance,
dollarBalanceSat,
};
}
}
}
}
@@ -251,23 +267,28 @@ export default async function processSparkAddress(input, context) {
addressInfo.supportFee = paymentInfo.supportFee;
} else if (results.btcSwap) {
const { result, satAmount } = results.btcSwap;
if (!result.didWork) throw new Error(result.error);
const fees = Number(result.simulation.feePaidAssetIn);
const satFee = fees;
// Only throw if BTC swap failed AND we don't have a USD fallback
if (!result.didWork && !canDoDirectUsdPayment)
throw new Error(result.error);
addressInfo.paymentFee = fees;
addressInfo.supportFee = 0;
swapPaymentQuote = {
warn: parseFloat(result.simulation.priceImpact) > 3,
poolId: poolInfoRef.lpPublicKey,
assetInAddress: BTC_ASSET_ADDRESS,
assetOutAddress: USD_ASSET_ADDRESS,
amountIn: satAmount,
satFee,
bitcoinBalance,
dollarBalanceSat,
};
if (result.didWork) {
const fees = Number(result.simulation.feePaidAssetIn);
const satFee = fees;
addressInfo.paymentFee = fees;
addressInfo.supportFee = 0;
swapPaymentQuote = {
warn: parseFloat(result.simulation.priceImpact) > 3,
poolId: poolInfoRef.lpPublicKey,
assetInAddress: BTC_ASSET_ADDRESS,
assetOutAddress: USD_ASSET_ADDRESS,
amountIn: satAmount,
satFee,
bitcoinBalance,
dollarBalanceSat,
};
}
}
} else {
// If we are just using BTC
@@ -230,6 +230,7 @@ export default function SendPaymentScreen(props) {
useFullTokensDisplay,
selectedPaymentMethod: userPaymentMethod,
didSelectPaymentMethod,
sparkInformation,
});
useEffect(() => {
@@ -364,6 +365,7 @@ export default function SendPaymentScreen(props) {
masterInfoObject,
fiatStats,
inputDenomination,
sparkInformation,
});
console.log(paymentValidation);
+19 -3
View File
@@ -23,6 +23,9 @@ export default function usePaymentMethodSelection({
// Pre-selected method (from navigation params)
selectedPaymentMethod = '',
didSelectPaymentMethod = false,
// for swap validation
sparkInformation,
}) {
const isBitcoinPayment = paymentInfo?.paymentNetwork === 'Bitcoin';
const isSparkPayment = paymentInfo?.paymentNetwork === 'spark';
@@ -63,7 +66,11 @@ export default function usePaymentMethodSelection({
// USD → BTC Spark (requires swap, check minimums)
const canPayUSDtoBTC = hasUSDBalance && meetsUSDMinimum;
if (canPayBTCtoBTC && canPayUSDtoBTC) {
if (
canPayBTCtoBTC &&
canPayUSDtoBTC &&
sparkInformation?.didConnectToFlashnet
) {
return 'user-choice';
}
return canPayBTCtoBTC ? 'BTC' : canPayUSDtoBTC ? 'USD' : 'BTC';
@@ -76,7 +83,11 @@ export default function usePaymentMethodSelection({
// BTC → USD Spark (requires swap, check minimums)
const canPayBTCtoUSD = hasBTCBalance && meetsBTCMinimum;
if (canPayUSDtoUSD && canPayBTCtoUSD) {
if (
canPayUSDtoUSD &&
canPayBTCtoUSD &&
sparkInformation?.didConnectToFlashnet
) {
return 'user-choice';
}
return canPayUSDtoUSD ? 'USD' : canPayBTCtoUSD ? 'BTC' : 'USD';
@@ -90,7 +101,11 @@ export default function usePaymentMethodSelection({
// USD → BTC (requires swap, check minimums)
const canPayUSDtoBTC = hasUSDBalance && meetsUSDMinimum;
if (canPayBTCtoBTC && canPayUSDtoBTC) {
if (
canPayBTCtoBTC &&
canPayUSDtoBTC &&
sparkInformation?.didConnectToFlashnet
) {
return 'user-choice';
}
@@ -114,6 +129,7 @@ export default function usePaymentMethodSelection({
useFullTokensDisplay,
hasBothUSDAndBitcoinBalance,
isSparkPayment,
sparkInformation?.didConnectToFlashnet,
]);
/**
+1 -1
View File
@@ -679,7 +679,7 @@ export const WebViewProvider = ({ children }) => {
// Queue messages during reset/background
if (
(isResetting.current || AppState.currentState !== 'active') &&
(isResetting.current || AppState.currentState === 'background') &&
action !== 'handshake:init' &&
action !== 'initializeSparkWallet'
) {
+2 -1
View File
@@ -964,7 +964,8 @@
"lnurlPayError": "Der Zahlungsbetrag muss zwischen dem {{overFlowType}} von {{amount}} liegen",
"lrc20FeeError": "Sie benötigen mindestens {{amount}}, um die Transaktionsgebühr zu bezahlen. Ihr Aktuelles Guthaben: {{balance}}",
"balanceError": "Unzureichendes Guthaben, um diese Zahlung abzuschließen",
"swapMinimumError": "{{currency1}}-zu-{{currency2}}-Swaps erfordern einen Mindestbetrag von {{amount}}"
"swapMinimumError": "{{currency1}}-zu-{{currency2}}-Swaps erfordern einen Mindestbetrag von {{amount}}",
"flashnetOffineError": "Umwandlungen zwischen Dollar und Bitcoin sind derzeit nicht verfügbar. Bitte versuche es später erneut."
}
},
"switchOption": {
+2 -1
View File
@@ -982,7 +982,8 @@
"lnurlPayError": "Payment amount must be between the {{overFlowType}} of {{amount}}",
"lrc20FeeError": "You need at least {{amount}} to pay the transaction fee. Current balance: {{balance}}",
"balanceError": "Insufficient balance to complete this payment",
"swapMinimumError": "{{currency1}} to {{currency2}} swaps require a minimum of {{amount}}"
"swapMinimumError": "{{currency1}} to {{currency2}} swaps require a minimum of {{amount}}",
"flashnetOffineError": "Conversions between Dollars and Bitcoin are currently offline. Please try again later."
}
},
"switchOption": {
+2 -1
View File
@@ -829,7 +829,8 @@
"lnurlPayError": "El monto del pago debe estar entre el {{overFlowType}} de {{amount}}",
"lrc20FeeError": "Necesitas al menos {{amount}} para pagar la comisión de transacción. Saldo actual: {{balance}}",
"balanceError": "Saldo insuficiente para completar este pago",
"swapMinimumError": "Los swaps de {{currency1}} a {{currency2}} requieren un mínimo de {{amount}}"
"swapMinimumError": "Los swaps de {{currency1}} a {{currency2}} requieren un mínimo de {{amount}}",
"flashnetOffineError": "Las conversiones entre dólares y Bitcoin no están disponibles en este momento. Inténtalo de nuevo más tarde."
}
},
"switchOption": {
+2 -1
View File
@@ -968,7 +968,8 @@
"lnurlPayError": "Le montant du paiement doit être compris dans le {{overFlowType}} de {{amount}}",
"lrc20FeeError": "Vous avez besoin dau moins {{amount}} pour payer les frais de transaction. Solde actuel : {{balance}}",
"balanceError": "Solde insuffisant pour finaliser ce paiement",
"swapMinimumError": "Les swaps {{currency1}} vers {{currency2}} nécessitent un minimum de {{amount}}"
"swapMinimumError": "Les swaps {{currency1}} vers {{currency2}} nécessitent un minimum de {{amount}}",
"flashnetOffineError": "Les conversions entre dollars et Bitcoin sont actuellement indisponibles. Veuillez réessayer plus tard."
}
},
"switchOption": {
+2 -1
View File
@@ -982,7 +982,8 @@
"lnurlPayError": "Limporto del pagamento deve rientrare nel {{overFlowType}} di {{amount}}",
"lrc20FeeError": "Hai bisogno di almeno {{amount}} per pagare la commissione di transazione. Saldo attuale: {{balance}}",
"balanceError": "Saldo insufficiente per completare questo pagamento",
"swapMinimumError": "Gli swap da {{currency1}} a {{currency2}} richiedono un minimo di {{amount}}"
"swapMinimumError": "Gli swap da {{currency1}} a {{currency2}} richiedono un minimo di {{amount}}",
"flashnetOffineError": "Le conversioni tra dollari e Bitcoin non sono attualmente disponibili. Riprova più tardi."
}
},
"switchOption": {
+2 -1
View File
@@ -980,7 +980,8 @@
"lnurlPayError": "O valor do pagamento deve estar dentro do {{overFlowType}} de {{amount}}",
"lrc20FeeError": "Você precisa de pelo menos {{amount}} para pagar a taxa da transação. Saldo atual: {{balance}}",
"balanceError": "Saldo insuficiente para concluir este pagamento",
"swapMinimumError": "Trocas de {{currency1}} para {{currency2}} exigem um mínimo de {{amount}}"
"swapMinimumError": "Trocas de {{currency1}} para {{currency2}} exigem um mínimo de {{amount}}",
"flashnetOffineError": "As conversões entre dólares e Bitcoin estão temporariamente indisponíveis. Tente novamente mais tarde."
}
},
"switchOption": {
+2 -1
View File
@@ -965,7 +965,8 @@
"lnurlPayError": "Сумма должна быть от {{amount}} ({{overFlowType}})",
"lrc20FeeError": "Нужно минимум {{amount}} для комиссии. Баланс: {{balance}}",
"balanceError": "Недостаточно средств",
"swapMinimumError": "Минимум для свопа {{currency1}} -> {{currency2}}: {{amount}}"
"swapMinimumError": "Минимум для свопа {{currency1}} -> {{currency2}}: {{amount}}",
"flashnetOffineError": "Конвертации между долларами и биткойном в настоящее время недоступны. Пожалуйста, попробуйте позже."
}
},
"switchOption": {
+2 -1
View File
@@ -968,7 +968,8 @@
"lnurlPayError": "Betalningsbeloppet måste ligga inom {{overFlowType}} på {{amount}}",
"lrc20FeeError": "Du behöver minst {{amount}} för att betala transaktionsavgiften. Nuvarande saldo: {{balance}}",
"balanceError": "Otillräckligt saldo för att slutföra betalningen",
"swapMinimumError": "Swaps från {{currency1}} till {{currency2}} kräver ett minimum på {{amount}}"
"swapMinimumError": "Swaps från {{currency1}} till {{currency2}} kräver ett minimum på {{amount}}",
"flashnetOffineError": "Växlingar mellan dollar och Bitcoin är för närvarande inte tillgängliga. Försök igen senare."
}
},
"switchOption": {