refactor: unify custodial and self-custodial under adapter pattern #3777
Draft
esaugomez31 wants to merge 6 commits into
Draft
refactor: unify custodial and self-custodial under adapter pattern
#3777esaugomez31 wants to merge 6 commits into
esaugomez31 wants to merge 6 commits into
Conversation
f48343a to
9b9c4af
Compare
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.

Overview
This PR completes the adapter pattern across the mobile app so that custodial and self-custodial accounts share the same code paths at the screen and hook layer. Each flow exposes a single interface through
usePayments(), and the mode-specific implementations (Apollo for custodial, Breez SDK for self-custodial) live behind that interface.Conversion flow — applied
Adapter unification
ConvertAdapteris now an object that exposes a two-stepgetQuotethenexecutepipeline. Both the custodial and self-custodial implementations conform to this interface, so screens and hooks no longer branch onisSelfCustodialto decide which mutation or SDK call to run.Custodial conversion
createCustodialConvertis now a real implementation that performs the BTC and USD intra-ledger swap through Apollo mutations. The previous implementation returned a hard-coded "not supported" failure.Self-custodial conversion
The SDK-driven quote and execute logic moved from
app/self-custodial/bridge/convert.tstoapp/self-custodial/adapters/payment.ts(createSelfCustodialConvert). The bridge now only exposes the low-level SDK primitives that the adapter composes (prepareConversion,recordConvertError,PreparedConversion).Provider-agnostic hooks
useSelfCustodialConversiontouseConversionExecutionuseSelfCustodialConversionGuardtouseConversionDustGuarduseNonCustodialConversionLimitstouseConversionLimits(moved toapp/hooks/; returns no-op limits for custodial and real limits for self-custodial)These hooks no longer take an
enabledflag, and the screens no longer wire mode-specific instances. They consume the adapter directly.Auto-convert with dependency injection
executeAutoConvertnow receives aConvertAdapteras a parameter instead of importing the self-custodial implementation directly.useAutoConvertListenerobtains it fromusePayments()and passes it down. The executor is now decoupled from the concrete implementation.Shared payment failure helper
Three identical
failed(message)declarations across adapter files have been collapsed into a singlefailedPaymentexported fromapp/types/payment.ts.Fee row regression fix
The conversion confirmation screen was hiding the fee row while the quote was loading. The row now stays visible with the activity indicator during the quoting phase, and only hides when the quote resolves with a zero fee (the new custodial intra-ledger case).
Polish
sleepredeclaration replaced with the existingapp/utils/sleephelper.Receive flow — applied
Adapter unification
ReceiveLightningAdapterandReceiveOnchainAdapterare now contracts that take awalletCurrencyparameter and return a unified result shape. Both the custodial and self-custodial implementations conform to these interfaces, so the screen and hook layers no longer branch onisSelfCustodialor read Apollo mutations directly.Custodial receive
createCustodialReceiveLightningandcreateCustodialReceiveOnchainare real implementations that wrap the Apollo mutations (lnInvoiceCreate,lnNoAmountInvoiceCreate,lnUsdInvoiceCreate,onChainAddressCurrent). Lightning routing is driven bywalletCurrencyso a USD-wallet receiver routes tolnUsdInvoiceCreatewith the USD walletId, BTC routes tolnInvoiceCreate/lnNoAmountInvoiceCreatewith the BTC walletId, and onchain routes to the matching walletId.Self-custodial receive
The SDK-driven Bolt11 and on-chain logic moved from
app/self-custodial/bridge/receive.tstoapp/self-custodial/adapters/payment.ts(createSelfCustodialReceiveLightning/createSelfCustodialReceiveOnchain). The bridge now exposes only the low-level SDK primitives that the adapter composes (prepareReceiveBolt11,prepareReceiveOnchain,recordReceiveError).Provider-agnostic hooks
useOnChainAddressconsumesreceiveOnchainfromusePayments()and takeswalletCurrencyas a required parameter. The Apollogqltemplate anduseOnChainAddressCurrentMutationreference are gone from this file.useOnchainResolverdrops theisSelfCustodialbranch and the BTC/USD walletId lookup; it just forwardsonchainWalletCurrencyfrom the carousel to the adapter.usePaymentRequestand the self-custodialuseSelfCustodialPaymentRequestboth readreceiveLightning(andreceiveOnchainwhere needed) fromusePayments()instead of constructing or importing adapters themselves.Dispatcher consolidation
use-payments.tsextractsbuildCustodialPayments(walletIds, mutations)andbuildSelfCustodialPayments(sdk)helpers. The Apollo mutation hooks for the receive path (useLnInvoiceCreateMutation,useLnNoAmountInvoiceCreateMutation,useLnUsdInvoiceCreateMutation,useOnChainAddressCurrentMutation) and theirgqltemplates now live alongside the convert and send mutations in this single dispatcher.Shared receive error helper
failedReceive(message)andextractApolloErrorMessage(apolloErrors, payloadErrors, fallback)are exported fromapp/types/payment.tsand reused by both adapter implementations. The custodial adapter no longer hand-rolls theapolloErrorMessage ?? payloadErrors[0]?.message ?? fallbackpattern at each call site.State machine preserved
useInvoiceLifecycle'sIdle → Loading → Created → Paid / Error / Expiredtransitions and the regenerate path remain byte-equivalent to pre-refactor. Internal type-narrowing was extracted into agetLightningInvoiceDatahelper, and thePaymentRequestfield exposed by the self-custodial hook was renamed fromprtopaymentRequest. The self-custodial hook keeps its crashlytics observability with amount and currency context on adapter failures, no-invoice payloads, andaddPendingAutoConvertrejections, and propagates each toPaymentRequestState.Error.Polish
generateQuotewas split intobuildOnchainInfo/buildLightningInfo/buildPayCodeInfodispatched bybuildInfoForType, removing the original 130-line if/else chain.DEFAULT_MEMO,LNURL_BECH32_LIMIT,LNURL_RENDER_DELAY_MS.prparameter renamed torequestinsidepayment-request.tsfor readability.formatPayCodeAmountQueryandencodeLnurlextracted as named helpers.PaymentRequestInformation'sapplicationErrorsandgqlErrorsfields collapsed into a singleerrors: PaymentError[].