Skip to content

feat: blocktank sunset - #1127

Merged
ovitrif merged 6 commits into
masterfrom
fix/blocktank-sunset-sheet
Oct 28, 2026
Merged

ovitrif merged 6 commits into
masterfrom
fix/blocktank-sunset-sheet

Conversation

@ovitrif

@ovitrif ovitrif commented Oct 26, 2026 •

Copy link
Copy Markdown
Collaborator

Twin: synonymdev/bitkit-android#1696

This PR turns Blocktank channel purchases off in the app. Blocktank no longer sells channels, and its order, CJIT and gift endpoints now return 404, so every path that used to buy a channel shows the Transfer Unavailable sheet and leads to Manual Setup.

Description

  • Adds the "Transfer Unavailable" sheet: "MANUAL SETUP REQUIRED" with Learn More and Continue. Continue opens the existing Manual Setup flow, and Learn More opens https://blog.bitkit.to/ in the browser.
  • Shows the sheet on every path that used to buy a channel:
    • Savings wallet › Transfer To Spending
    • Spending wallet › transfer button
    • Hardware wallet › Transfer To Spending
    • Home › Lightning suggestion card
    • Receive › Spending › Receive Lightning Funds, and a Spending invoice edited above the wallet's inbound capacity, which no longer create a CJIT
  • Shows "Fund your spending balance" as the sunset design: Manual Setup first, Transfer from Savings, Transfer from HW wallet and Use Other Wallet disabled, and Learn More at the bottom.
  • Removes what only served buying a channel: the transfer-to-spending screens (intro, amount, receiving capacity, confirm, liquidity, setting up, funding advanced), the hardware-wallet funding screens, order and CJIT creation, fee estimates and the refund address they needed, opening and tracking channels for paid orders and their transfer-in-progress banner, the CJIT receive screens (amount, confirm, liquidity), the gift-code claim flow (Blocktank has taken its gift API down, so a scanned gift code now gets the scanner's regular unsupported-code error), the onboarding note that Bitkit offered no Lightning services in the user's country, and their settings flags, strings in every locale, tests and journeys.
  • Keeps what existing users need:
    • existing orders and CJIT entries still load in Channel Orders and activity;
    • Payment Pending offers Cancel for a Blocktank funding that never reached the network, which sends its coins back to savings so on-chain sends work again, and Retry original opens the sheet and broadcasts nothing.
  • Blocktank is no longer a trusted peer, so the wallet broadcasts and fee-bumps its own force closes.
  • Startup disconnects every stored peer with no channel and no claimable balance, which also drops it from the peer store, and skips that while a closed channel's sweep is pending.
  • When a cooperative close gives up, Bitkit offers Force Transfer for every channel, Blocktank ones included.
  • Repurposes Background Payments for Paykit notifications, since every push it relied on came from Blocktank: the settings screen, its intro, the Settings row, the Home card and "Keep Bitkit active in background" stay, reworded for subscription reminders and payment requests. The prompt shows only to users with a Paykit subscription. Proposing or accepting a subscription, or accepting an allowance, asks for notification permission if it isn't granted. The push registration with Blocktank and the handling of its pushes are removed.

Out of Scope

  • Existing Blocktank channels: closing them.
  • Refunds for paid Blocktank orders: Blocktank no longer opens channels for them; unopened orders expire within hours, and their on-chain refunds are handled manually on Blocktank's side (automatic refunds were removed in synonymdev/blocktank-lsp-ln2#285).
  • Funding option labels: the design writes "Manual setup" and "Use other wallet"; the existing "Manual Setup" and "Use Other Wallet" strings stay.

Design

Bitkit Wallet › Bitkit - Final Review v63 › Blocktank sunsetting:

Preview

Figma (left) next to the app (right):

Transfer Unavailable Fund your spending balance

Recordings of every path on the current head follow in the QA comment.

QA Notes

Setup

  • Debug build (regtest, staging LSP).
  • Fresh wallet funded on-chain only: ./lsp POST /regtest/chain/deposit '{"address":"<savings address>","amountSat":100000}', then ./lsp POST /regtest/chain/mine '{"count":3}' from a bitkit-android checkout.
  • Test 2 needs a refused Blocktank funding attempt, which staging can't produce now that it creates no orders: a local debug build tags an on-chain send as a Blocktank funding and prepares it at a zero fee rate, so the backend refuses its real broadcast ("min relay fee not met"); the PR build then runs the steps.

Journeys

  • J1: transfer-unavailable-sheet.xml — the wallet screens' Transfer To Spending buttons open the sheet, Learn More opens https://blog.bitkit.to/, Continue opens Manual Setup, the Funding screen leads with Manual Setup with the other options disabled, and Receive's Lightning screen shows the sheet's content

Manual Tests

  • 2. With a refused Blocktank funding attempt, start a new on-chain send → Payment Pending opens; Retry original opens the sheet and broadcasts nothing; Cancel shows the transfer note and the fee prompt, then sends the coins back; a new on-chain send then goes through
  • 4. Open the sheet from Savings, Spending and the Lightning suggestion card → it opens at once, and the app log shows no Blocktank request on the way; scan bitkit://gift-abc123-3000 → the scanner's regular unsupported-code error, no sheet

Automated Checks

  • rewritten ReceiveLiquidityDecisionTests.swift, ReceiveSheetSessionTests.swift and TabBarReceiveTests.swift — a Spending receive above inbound capacity, or with none, leads to Transfer Unavailable
  • updated PaykitPaymentProofServiceTests — a refused Blocktank funding is releasable
  • updated TransferServiceActivityTests and OnchainSendAttemptServiceTests — an accepted Blocktank funding resolves without a transfer record and no longer blocks sends
  • added a ScannerManagerTests case — a scanned gift code gets the regular invalid-code toast and no sheet
  • added testConcurrentTransferCreationKeepsEveryRecord in TransferServiceActivityTests — transfers created concurrently are all kept
  • added LightningServiceConfigTests.swift — the node config trusts no peer for zero-conf or force-close broadcasts; only stored peers with no channel and no claimable balance count as idle, and none while a sweep is pending
  • removed the tests of the removed code: TransferViewModelTests purchase and limits cases, TransferViewModelHwTests, BlocktankViewModelCjitTests, SpendingConfirmTotalTests, the channel-purchase integration test, the refund-address tests, the order and CJIT cases in BlocktankTests, and the fee-increase toast and route-replace cases
  • built the app and ran the unit-tests.yml selection at 86d820a on an iPhone simulator — pass, except PaykitPaymentRequestServiceTests.testCapabilityDiscoveryBoundsConcurrentReadsWithoutWaitingForSlowPeer, which times out the same way before this rewrite and drives only a mocked Paykit SDK; swiftformat --lint and validate-translations.js pass

Side effects checked

  • Anchor reserve: perChannelReserveSats stays 1, so each open Blocktank channel holds 1 sat of on-chain reserve. Spendable balance is effectively unchanged.
  • Force-close broadcast: anchor commitments usually need a CPFP through the anchor, which spends a confirmed on-chain UTXO. A wallet with no confirmed savings coin retries on each new block until it has one.
  • Peer reconnects: checked at ldk-node v0.7.0-rc.77, which both apps pin. The node reconnects persisted peers, re-persists channel counterparties from the network graph after each RGS sync, and disconnect removes the peer from the store.
  • A Blocktank peer with a pending force close stays connected until its claim and sweep finish, which can take days for the timelock.

Evidence

J1 and tests 2 and 4 ran on 86d820a, a Debug build on an iPhone 16 Pro simulator (iOS 18.5) against the staging regtest LSP, and pass: QA comment. The refused funding was staged with a local debug build; every other step ran on the unmodified build. 4634789 only serializes transfer creation again.

Gaps

  • Hardware wallet Transfer To Spending was not run: no Trezor emulator. It opens the same sheet as the other buttons.

Models used

  • Planning/scoping: claude-opus-5-5 (reasoning: Not exposed)
  • Implementation: claude-opus-5-5 (reasoning: Not exposed)
  • Review: claude-sonnet-5-5 (reasoning: Not exposed)

Metadata note: the session records name the models but do not expose their reasoning settings.

@ovitrif ovitrif added this to the 3.1.0 milestone Oct 26, 2026
@ovitrif ovitrif self-assigned this Oct 26, 2026
@ovitrif
ovitrif marked this pull request as ready for review October 26, 2026 13:40
@ovitrif
ovitrif requested review from a team, ben-kaufman and pwltr and removed request for a team October 26, 2026 13:40
@chatgpt-codex-connector

This comment has been minimized.

talosmachina

This comment was marked as outdated.

@ovitrif ovitrif mentioned this pull request Oct 26, 2026
21 tasks
@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif
ovitrif force-pushed the fix/blocktank-sunset-sheet branch from 9d5e276 to 1883357 Compare October 26, 2026 17:25
@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

@ovitrif

This comment has been minimized.

piotr-iohk

This comment was marked as outdated.

@ovitrif

This comment has been minimized.

@ovitrif
ovitrif requested a review from piotr-iohk October 27, 2026 00:38
piotr-iohk

This comment was marked as outdated.

@ovitrif

This comment has been minimized.

@ovitrif
ovitrif requested a review from piotr-iohk October 27, 2026 09:31
jvsena42

This comment was marked as outdated.

@ovitrif
ovitrif marked this pull request as draft October 27, 2026 16:40
Turn off Blocktank channel purchases outright: every transfer-to-spending entry and the Lightning receive paths that bought liquidity open the Transfer Unavailable sheet, then Manual Setup, with no network call. Remove the geocheck, the purchase flow, order and CJIT creation, and their tests, strings and journeys. Keep paid-order recovery, the refused-funding release, reading existing orders, the trusted-peers removal and the idle-peer sweep.
@jvsena42

This comment has been minimized.

Remove Blocktank gift-code claims: a scanned gift code now shows the Transfer Unavailable sheet. Drop the GEO workflow env, and restore the Fund Spending Balance layout with the purchase options shown disabled.
jvsena42

This comment was marked as outdated.

@ovitrif
ovitrif force-pushed the fix/blocktank-sunset-sheet branch from 6104b95 to 23b8b66 Compare October 27, 2026 17:39
@ovitrif
ovitrif marked this pull request as ready for review October 27, 2026 18:02
@ovitrif
ovitrif requested review from piotr-iohk and removed request for pwltr October 27, 2026 18:02
@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

Device gate (partial): 6104b95, iPhone 17 simulator, regtest.

@jvsena42 thanks. This PR has since been rewritten at 23b8b66. Blocktank's order, CJIT and gift endpoints return 404, so the app no longer has a geocheck: every former purchase path shows the Transfer Unavailable sheet, and Cancel is offered for any Blocktank funding that never reached the network. Item 2 (an allowed region showing no Cancel) therefore no longer applies. The description covers the new head, and device results on it will follow in the QA comment.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23b8b6638b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Bitkit/Resources/Localization/en.lproj/Localizable.strings
ben-kaufman
ben-kaufman previously approved these changes Oct 27, 2026

@ben-kaufman ben-kaufman left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed 23b8b6638b77bde5ec1ba0d8942011929bbae3da. No new material correctness issues found.

E2E is still running. The linked device QA report covers 6104b956, so device verification of the current head remains unconfirmed. Independent security coverage is incomplete.

Reviewer model: gpt-6-astra
Reasoning effort: high

Drop what nothing reaches once Blocktank is read-only: paid-order watching and channel opening, order transfer records and their balance, the refund-address store, the transfer intro flags, and helpers the purchase flow left behind. An accepted Blocktank funding still resolves so later sends are not blocked. A scanned gift code gets the scanner's invalid-code error. Translate the transfer cancel note.
@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

This PR was refactored into a proper Blocktank sunset. Blocktank no longer sells channels and its order, CJIT and gift endpoints return 404, so the app now turns channel purchases off outright: no geocheck call and no geo state remain, and every former purchase path leads to the sunset sheet and Manual Setup. The description is rewritten for the new head.

Comments and reviews from before the refactor are hidden as outdated: they reviewed the geocheck-based version, which no longer exists.

Now:

  • Head 86d820a9 is pushed, rebased on master. Compile, unit tests and lint pass.
  • Since the last head: a scanned Blocktank gift code gets the scanner's regular unsupported-code error, and the recovery for paid Blocktank orders is removed, since Blocktank no longer opens their channels (see Out of Scope).
  • Device validation of the new head is running on our side; results follow here.
  • Ready for review by @ben-kaufman and @piotr-iohk.

Comment thread Bitkit/Services/TransferService.swift
@ovitrif

ovitrif commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator Author

QA for 86d820a:
Tested on iPhone 16 Pro simulator (iOS 18.5).

🟢 Refused funding: Retry original, Cancel and a later send
A Blocktank funding whose broadcast the backend refused (`min relay fee not met, 0 < 141`, txid `164ff408…`) leaves Savings at 300 000 with no outgoing or pending transaction. A new send opens Payment Pending for it; Retry original opens the Transfer Unavailable sheet and broadcasts nothing (`Stopped a retry of a transfer to spending`); Cancel shows the transfer note, checks the network and offers a 422-sat fee, then sends the coins back (`Released Blocktank funding 164ff408… through b0442225…`); after one block a new 10 000-sat send goes through.
Refused broadcast (debug build) Retry original, Cancel, a new send
stage-refusal2x.mp4
case1-refused2x.mp4

🟢 Every former purchase path shows the sheet
The Home suggestion card, Savings › Transfer To Spending, Spending › Transfer To Spending and Receive › Spending › Receive Lightning Funds open Transfer Unavailable; Learn More opens blog.bitkit.to and Continue opens Manual Setup. Lightning Connections › + lists Manual Setup first and enabled, with the other three options disabled; tapping Transfer from Savings does nothing. The app log has no request to Blocktank's purchase API.
case2-entry-points2x.mp4

🟢 A scanned gift code gets the regular unsupported-code error
Opening `bitkit://gift-abc123-3000` shows the "Decoding Error" toast Bitkit gives for data it can't read, and no sheet opens.
case3-gift2x.mp4

Note

Staging creates no Blocktank orders, so a local debug build sent a fabricated-order funding prepared at a zero fee rate; the backend refused its real broadcast. The unmodified 86d820a build was then installed over that wallet and ran every other step shown.

@ovitrif
ovitrif requested a review from ben-kaufman October 27, 2026 20:05

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-reviewed to 4634789 (the Blocktank sunset commits and serialized transfer creation). No findings.

  • 4634789 holds an NSLock around a single insert, so there is no deadlock.
  • Dropping paid-order recovery keeps the sent activity, the order entry and refund detection.
  • A scanned or deep-linked gift code shows the "Decoding Error" toast, with no crash and nothing sent. Manual entry enables Continue and then shows the same toast, which matches Android's manual entry.
  • An order-backed transfer now settles straight away rather than waiting for the channel. No v3.0.1 wallet can still hold a pending Blocktank channel, so nobody sees a different balance.

The rewrite push dismissed my earlier approval at 6104b95.

Device gate (partial): 4634789, not run yet. Pending: transfer/transfer-unavailable-sheet.xml, the gift deep link, and a re-run of the refused-funding Cancel path on this head.

jvsena42
jvsena42 previously approved these changes Oct 28, 2026

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving. The re-review at 4634789 had no findings, and the device gate passed. One LOW inline on the journey text.

Device gate: 4634789, iPhone 17 simulator, regtest. Staging no longer creates Blocktank orders and this head creates none either, so I staged the refused funding on the earlier harness build and installed this head over it with data kept. That matches a user upgrading with a refused funding.

  • A refused funding blocks a new send and opens Payment Pending. Retry original shows the fee-rate dialog, then the Transfer Unavailable sheet, and nothing is broadcast.
  • Cancel shows the transfer note and the 703 sat fee, then logs Released Blocktank funding d3a8b3e2…5490 through d95472f8…b804 and the "Payment cancelled" toast. A new 20 000 sat send then went out. No PIN was set this run, so there was no PIN step.
  • transfer/transfer-unavailable-sheet.xml: all 11 steps hold, and the sheet opens 0.9 s after the tap with no Blocktank request. Step 6's identifier is the inline LOW.
  • Gift code by deep link (bitkit://gift-abc123-3000) and by scanner paste: the "Decoding Error" toast shows, with no sheet, no crash, no request and nothing sent.

Perf: not relevant.

The LOW is open: fix it here or in a follow-up issue, your choice; reply on the thread saying which.

Comment thread journeys/transfer/transfer-unavailable-sheet.xml
@ovitrif

ovitrif commented Oct 28, 2026

Copy link
Copy Markdown
Collaborator Author

Pushed 6966886: maps Android's add_connection_button to iOS NavigationAction in the journeys Identifiers table, answering the journey-identifier finding in the latest review. Docs only.

@jvsena42 jvsena42 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approving at 6966886. The only new commit adds the add_connection_button / NavigationAction row to the identifier table in journeys/README.md, which settles my LOW on step 6. It changes docs only, so the device gate at 4634789 still covers the app code.

Device gate: 6966886 (app code 4634789).

@ovitrif
ovitrif merged commit 91f82ba into master Oct 28, 2026
18 checks passed
@ovitrif
ovitrif deleted the fix/blocktank-sunset-sheet branch October 28, 2026 13:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants