Add Unstoppable Wallet as an OpenCryptoPay payment-link wallet - #4437
Open
joshuakrueger-dfx wants to merge 3 commits into
Open
Add Unstoppable Wallet as an OpenCryptoPay payment-link wallet#4437joshuakrueger-dfx wants to merge 3 commits into
joshuakrueger-dfx wants to merge 3 commits into
Conversation
Register Unstoppable Wallet (Horizontal Systems) in the wallet_app catalog so it is offered on payment-link pages. Unstoppable ships native OpenCryptoPay support on iOS and Android; the eleven supported transfer methods were verified against the app sources (iOS OpenCryptoPayBroadcasterFactory.unstoppable, Android OcpTransferAmount.supportedBlockchainTypes). The six EVM chains are listed explicitly instead of the EvmBlockchains placeholder, because Unstoppable does not OCP-broadcast Gnosis, Haqq or Citrea. hasActionDeepLink stays NULL: OpenCryptoPayUrl.detect only accepts an https URL with a lightning query, so the deepLink+lightning: action link does not apply here. Idempotent SELECT-then-skip guard against the UNIQUE(name) constraint, matching the existing migration convention.
…gration spec up() skips an existing 'Unstoppable Wallet' row via its SELECT guard, so a name-only DELETE in down() would remove a row this migration never created - a hand-created or later edited row would be destroyed by an up/down cycle. down() now additionally matches the characteristic values up() writes (deepLink + blockchains), the same exact-value idiom the sibling migrations use. The new spec covers both layers, mirroring AddSavingZchfAsset: mocked queryRunner for the SQL content (all eleven methods in order, no hasActionDeepLink/recommended/semiCompatible/assets, no INSERT when the row exists) and a real-Postgres suite that creates a pre-existing foreign row before up() and asserts an up/down cycle leaves it intact.
joshuakrueger-dfx
marked this pull request as ready for review
July 29, 2026 10:38
joshuakrueger-dfx
requested review from
TaprootFreak and
davidleomay
as code owners
July 29, 2026 10:38
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Register Unstoppable Wallet (Horizontal Systems) in the
wallet_appcatalog so it is offered as a payment app on the payment-link pages (app.dfx.swiss/pl). The wallet list on those pages is served from this table viaGET /v1/paymentLink/walletApp.Supported transfer methods
Unstoppable ships native OpenCryptoPay support on iOS and Android. The eleven methods below were read off the app sources on 2026-07-28, not taken from documentation:
OpenCryptoPayBroadcasterFactory.unstoppableregistersEvmHex,TronHash,BitcoinHex,SolanaHash,ZanoHashandMoneroHashbroadcasters; the union of theirsupportedChainsmaps yields the method names.OcpTransferAmount.supportedBlockchainTypes()inOpenCryptoPayRepository.ktlists the same eleven.Ethereum;BinanceSmartChain;Polygon;Arbitrum;Optimism;Base;Bitcoin;Solana;Tron;Zano;MoneroThe six EVM chains are listed explicitly rather than via the
EvmBlockchainsplaceholder:EvmBlockchainsadditionally contains Gnosis, Haqq, Citrea and CitreaTestnet, which Unstoppable does not OCP-broadcast. SeeWalletApp.supportedBlockchainList.Columns left unset
recommended,semiCompatibleandassetsstay NULL — a normal, fully compatible wallet with no asset restriction.hasActionDeepLinkalso stays NULL, and that is deliberate. PerSetRealUnitHasActionDeepLink, the flag means a non-Lightning wallet takes the payment through<deepLink>lightning:<LNURL>. For Unstoppable that path does not exist:OpenCryptoPayUrl.detectaccepts only URLs withscheme == "https"carrying alightningquery parameter, sounstoppable.money:lightning:…falls throughDeepLinkManager.handleinto the address parser and ends in the "cannot recognize" banner. Behaviour matches Cake Wallet (flag NULL), not RealUnit.deepLinkis the bare custom schemeunstoppable.money:, registered on both platforms (iOSInfo.plistCFBundleURLTypes,AndroidManifest.xml), so the frontend's open-app action works.down() ownership
up()skips an existingUnstoppable Walletrow via its SELECT guard. A name-onlyDELETEindown()would therefore remove a row this migration never created — a hand-created or later edited row would be destroyed by an up/down cycle.down()additionally matches the characteristic valuesup()writes (deepLink+blockchains), the same exact-value idiom used bySetRealUnitHasActionDeepLinkandPopulateNativeCoinDecimals.Test
add-unstoppable-wallet-app.migration.spec.tsmirrorsAddSavingZchfAsset: a mocked-queryRunnersuite for the SQL content and a real-Postgres suite gated onMIGRATION_TEST_PG(set inapi-pr.yaml).The ownership test creates a foreign
Unstoppable Walletrow with a differentdeepLinkbeforeup()and asserts that anup()→down()cycle leaves it intact. Verified to fail without the guard: revertingdown()to the unconditional form turns the suite red — 1 of 1 suite, 2 of 7 tests, the Postgres one withExpected length: 1, Received length: 0. With the guard: 7 of 7 pass.Precedent
wallet_appis maintained by migration, not by hand: AQUA (#3909), urble (#3674) and RealUnit (#4339) all landed that way and none was ever withdrawn (git log --diff-filter=Dovermigration/*allet*is empty). The id-lessINSERTis equally established — RealUnit went in through the same shape and was assigned id 27, the highest in production, so the sequence is not lagging.Not verified
The migration has not been run against the production database; idempotency and the up/down cycle are covered by the Postgres spec against a fixture table. No real payment was made from the Unstoppable app — this PR makes the wallet selectable, it does not exercise a payment.