@@ -235,6 +235,11 @@ impl SendOutgoingPaymentDispatcher {
235235 /// The other half is reserved for the CCH to settle the incoming payment
236236 /// after receiving the preimage.
237237 ///
238+ /// The final expiry delta is extracted from the stored incoming invoice when
239+ /// available, so that persisted orders use the actual inbound HTLC/TLC terms
240+ /// even if the config has changed since order creation. Falls back to the
241+ /// current config values when the invoice does not carry the setting.
242+ ///
238243 /// Returns `None` if there is insufficient time remaining.
239244 fn compute_max_outgoing_expiry_seconds < S : CchOrderStore > (
240245 state : & CchState < S > ,
@@ -248,9 +253,21 @@ impl SendOutgoingPaymentDispatcher {
248253
249254 // The incoming TLC/HTLC was accepted with at least this many seconds of expiry.
250255 // Using `created_at` is conservative (the TLC was accepted after order creation).
256+ //
257+ // Prefer the final expiry delta encoded in the stored incoming invoice so
258+ // that persisted orders are checked against their actual terms, not values
259+ // that may have drifted if the config was updated between restart.
251260 let incoming_expiry_seconds = match & order. incoming_invoice {
252- CchInvoice :: Fiber ( _) => state. config . ckb_final_tlc_expiry_delta_seconds ,
253- CchInvoice :: Lightning ( _) => state. config . btc_final_tlc_expiry_delta_blocks * 600 ,
261+ CchInvoice :: Fiber ( inv) => {
262+ // CkbInvoice stores the delta in milliseconds; convert to seconds.
263+ inv. final_tlc_minimum_expiry_delta ( )
264+ . map ( |ms| ms / 1000 )
265+ . unwrap_or ( state. config . ckb_final_tlc_expiry_delta_seconds )
266+ }
267+ CchInvoice :: Lightning ( inv) => {
268+ // Bolt11Invoice stores the delta in blocks; convert to seconds.
269+ inv. min_final_cltv_expiry_delta ( ) . saturating_mul ( 600 )
270+ }
254271 } ;
255272 let remaining = incoming_expiry_seconds. checked_sub ( elapsed) ?;
256273
0 commit comments