Repository navigation
Conversation
aed1f9f to
82d6014
Compare
bb09eeb to
bf0e9cd
Compare
9d5e276 to
1883357
Compare
856799e to
7897f16
Compare
4534525 to
a9aa36f
Compare
|
QA: 🟢 Pay a Lightning invoice from iOSThe iPhone paid a plain Lightning invoice from the Pixel through Spark with no channel, and the Pixel received it at once. Ran on a9aa36f.
🟢 Restore Spark exit dataExit data backs up as its own category, and after a wipe the iPhone imported 16 leaves of Spark exit data before using Spark; its balance and claimed deposit came back. Ran on a9aa36f.
🟢 Move Spending to SavingsTransfer to Savings withdrew the Spark balance for a 1,470 sat fee and 4,917 sats arrived in Savings; it was listed as a plain received payment, fixed in 93aec43 to show as a transfer. Ran on a9aa36f.
🟢 Claim a depositSpark claimed the 7,500 sat deposit after 3 confirmations, keeping 198 sats, and Spending showed 7,302. Ran on an earlier head of this PR, before the 27 Oct rebase. 🟢 Transfer to SpendingTransfer to Spending sent 9,632 sats to Spark and Spending showed it as an incoming transfer. The confirm screen estimated a 99 sat claim fee where claims so far took 198 to 297; 7388559 now shows the most Spark may keep. Ran on f25edfe, a build of #1145 on an earlier head of this PR.
🔴 Pay a Paykit request over SparkThe iPhone paid the Pixel's 204 sat Paykit request over Spark and it arrived at once, but the iPhone stayed on Payment Pending: the payment settled inside the send call, before the send screen started watching for it. Fixed in 735e972 and synonymdev/bitkit-android@024b6bb85, not rerun on a device yet. Ran on 56d7adf, a build of #1145 on 9ddd96b. An earlier attempt kept loading for minutes while Paykit's shared state stayed locked; since a5d94ed the sheet stops at once and says the contact's payment details aren't ready, and the lock itself is a Paykit issue reported separately.
|
6104b95 to
23b8b66
Compare
|
Removed from the 3.1.0 milestone: this draft is the deliverable of a finished Spark experiment and does not ship in 3.1.0. |
|
Retargeted this draft to master before merging its base, #1127, so deleting that branch would not close it. |
735e972 to
a77cb76
Compare
|
Rebuilt this branch on master after the Blocktank sunset (#1127). Master removed the old Transfer to Spending flow, so Spark now has its own amount, confirm and deposit screens, reached from Funding and the transfer buttons while Spark is ready. The deposit screen offers Breez's early claim at the fee the provider quotes for that deposit. The history was rewritten into new commits, so the shas under Evidence predate the rebuild. This PR stays a draft. |
a77cb76 to
c72164c
Compare
3808d9d to
92a0376
Compare
…ghtning # Conflicts: # Bitkit/Services/BackupService.swift






Twin: synonymdev/bitkit-android#1697
This PR keeps Lightning in Bitkit after Blocktank stopped selling channels: on top of the Blocktank sunset (#1127), Spark becomes the easy option where Transfer to Spending now leads to manual setup. Savings to Spending funds a Breez SDK on Spark wallet, and Lightning sends and receives fall back to it whenever no channel can carry the payment. Existing channels keep working until they close, and ldk-node keeps running the on-chain wallet.
Description
claimDepositwith that fee as the deposit's own ceiling, so the SDK keeps retrying the early claim as confirmations arrive. An early quote within the cap is claimed by the SDK with no question; with no early quote, the screen shows the standard claim and its fee.breez-sdk-spark-swift0.27.2, exact) withSparkManager, so that a Spark wallet starts beside the node from the same recovery phrase and passphrase. Spark derives its own keys onm/8797555', so there is no new phrase to back up.LightningServicethat only the app sets, so the notification extension never loads Breez.BREEZ_API_KEYbuild setting, so that mainnet builds without a key keep Spark off; regtest builds use Lightspark's hosted regtest, which needs no key.BREEZ_API_KEYfrom an optional, gitignoredSecrets.xcconfigthrough a newBitkit.xcconfigbase config; copySecrets.xcconfig.templateand paste the key (README, Build secrets). Without the file the app builds with Spark off on mainnet. TestFlight is archived locally (Docs/RELEASE.md §2), so the releaser adds the file first; a Release-only build phase fails the build when the key is empty, so Spark can't ship switched off, and Docs/RELEASE.md and the release command carry the step.SPARK_EXIT, encrypted like the other backups. It is exported again when Spark connects and whenever the SDK reportsUnilateralExitStateChanged, and on restore it is imported right after Spark connects, without contacting the operators, so a wallet restored on a new device can still exit on-chain while the operators are unreachable. The category shows in Backup settings only where Spark runs.Out of Scope
prepare_recover_funds/recover_fundsyet.Risks for review
SPARK_EXIT; upstream VSS accepts request bodies up to 1 GiB by default, but a large wallet's upload against Synonym's VSS is untested.250 × sat/vB + 750sats.CLAIM_TX_SIZE_VBYTES,deposits.rs), so it is a flat cost that weighs heavily on small transfers. The confirm screen now shows it as Claim fee and shows what reaches Spending after it.Design
N/A — no design available. Figma has no Spark or unlimited-receiving state for the Lightning Connections header, so No limit uses the same style and arrow as the spending figure beside it; @aldertnl, a design for this state is still open.
Preview
Case 1 on a regtest simulator build without channels: the Spending invoice comes from Spark.
ios-receive-spark2x.mp4
Case 10 on a regtest simulator build, staged with a local hook (not in this PR) for the balance and the claimed deposit; the pending deposit is the wallet's real regtest transaction, and its confirmations went from 0/3 to 3/3 as blocks were mined.
ios-spark-screens2x.mp4
On a regtest simulator build with a staged 14,703 sat Spark balance and a pending deposit (its transaction id masked): Lightning Connections and the Spark detail with the full-width bar and receiving shown as No limit, and the Spark confirm screen with the claim fee and without the background toggle and Learn More.
QA Notes
Setup
BREEZ_API_KEYinSecrets.xcconfig(copySecrets.xcconfig.template), and small amounts.https://regtest-mempool.us-west-2.sparkinfra.net/api(the SDK's own regtest default,sdk_builder.rs). It lacks/fee-estimates, which ldk-node needs to start, so a dev build cannot point its node at Spark's chain as-is.Journeys
transfer/spark-transfer-to-spending.xml— with Spark ready, the transfer buttons and Funding open the Spark amount and confirm screens.transfer/transfer-unavailable-sheet.xml— now needs Spark not running, since a ready Spark replaces the sheet.Manual Tests
Imported Spark exit databefore Spark is used, and Spark's balance and deposits match — needs two installs.Automated Checks
SparkClaimOfferTests.swift— a quote maps to the standard claim, an automatic early claim within the cap, or a choice above the cap or with automatic claiming off, and each claim outcome maps to claimed, waiting, repriced or declinedChannelFreeRailTests.swift— rail events before the node callback are delivered, Paykit proofs and private Paykit sets read Spark payments, QuickPay reconciles them, and a Spark send honours the routing fee limitSparkLightningSendTests.swift— a Lightning invoice with no channels opens the payment when Spark can pay, is rejected without Spark, and a Spark send returns before completionSparkTransferActivityTests.swift— Spark deposits and withdrawals are marked as transfers, failed ones are not, and withdrawals are listed with the fee Spark keptSparkExitBackupTests.swift—SPARK_EXITuploads the exported state under its own key, a restored state is staged until Spark connects, and one from another network is skippedChannel-only audit
Every place that assumed channels, checked for a Spark wallet with no channels and compared with Android:
AppViewModelscan check: rejected every Lightning invoice without channels before considering Spark; now pays through Spark when its balance covers the amount, and the toast counts Spark's balance.SendSheetinsufficient toasts: count Spark's balance.SendSheetchannel wait: no longer waits for a reconnecting channel when Spark can pay the amount.SparkManagersend: returns as soon as the payment starts, so its completion event reaches the send screen and Paykit settlement; it showed "Payment Pending" before. Same fix on Android.LightningService.emitChannelFreeRailEvent: Spark events that arrive before the node's event callback is set are kept and delivered once it is; they were dropped.PrivatePaykitService+Invoicessettled and attempted payment sets: include Spark payments, so a private invoice Spark received isn't reused and a contact invoice Spark paid isn't paid again.PaykitLightningPaymentProofLookup: settles a Paykit proof from Spark's record when its completion event was missed, also before the node lists its payments.QuickPayPaymentCoordinatorreconcile: includes Spark payments.SparkManageractivity: recorded through the same path as LDK payments, so a Paykit contact and seen state survive Spark syncs.TransferViewModelswap to Savings: also withdraws Spark's balance, which the confirm screen already counted.PaykitAllowanceManager.lightningReady: Spark ready counts as ready.LightningService.sendon Spark: enforces the caller's routing fee limit (Paykit auto-pay's 1%) against Spark's fee estimate.LnurlPayAmount: caps and shows the larger rail balance, not the sum of both.AppStatusHelper.channelsStatus: a ready Spark wallet shows the Lightning connection as ready.GiftLoading: gift claims open a Blocktank channel, which Blocktank no longer sells.nodeLifecycleStategates on send and receive: the node runs beside Spark, as on Android.WalletViewModelbalance derivation while the node isn't up: keeps the last balances, which include Spark.maxSendLightningSats, receive invoice creation and tabs, Paykit Lightning endpoint publishing, the Spending card transfer button, the LNURL withdraw invoice.preferSpark) has no Lightning details, so it maps to no event; mainnet sends are Lightning payments.Evidence
92a03765: the mainnet Release build for device, SwiftFormat and the unit suite pass (run). The rebuilt flow and early claims (16 to 19) are not yet driven. Results below ran on the earlier flow before the rebuild; their revisions are kept as run.Debug simulator build of
4b58c22andf79c9f5(before the rebase onto55b8a47), iOS simulator, Spark's regtest:1: new wallet without channels; the log shows
Connected Spark wallet on 'regtest'. Receive shows Auto and Spending with a Lightning QR whose invoice names an explicit payee and a route hint to0202f87a…bb76, which an LDK node without channels does not add.The rebased head
aed1f9fbuilds for the simulator (arm64).10 on
34dfdef(nowbb09eeb): Lightning Connections listed Spark first with its balance and the pending deposit with live confirmations; the detail showed provider, trust note, pending and claimed deposits with the claim fee, and Transfer To Savings.10 and 2 on
5c3b6f7(nowcbc59e9): the Spark row and detail drew a full purple bar with no receiving side, the header read Receiving capacity No limit, and a 20,000 sat Spark transfer confirm showed network fee 458, Claim fee 99, To spending 19,901 and Total 20,458, with no background toggle, Learn More or Advanced.Gaps
a9aa36fd: the unit suite passes exceptSendConfirmationLifecycleTests.testCustomFeeWalletSwitchSurvivesFeeScreenNavigation, which fails the same way on the base branch at77b01a4a. Not yet driven on a device.8f6de109: built for the simulator only, not driven; the twin Android change was driven on an emulator.Models used
claude-opus-5-5(reasoning:low)claude-opus-5-5(reasoning:low)