Flutter Package
This is not another client-side NWC helper. It is the Flutter-facing wallet-service package that lets a Lightning wallet app create NWC connections, listen for encrypted NIP-47 requests and answer them after the wallet handles the payment operation.
The label points to wallet-service code
The Apps hub says Flutter Package, which sounds vague until you follow the source trail. In getAlby's awesome-nwc list, the label points to `kumulynja/nwc_wallet` and describes a Flutter package that takes care of the wallet-service side of Nostr Wallet Connect. That is the key distinction.
The previous page, Dart Package, covered the client side: an app that already has a NWC connection string and wants to ask a remote wallet to do something. Flutter Package is the other side of the conversation. It is for a Lightning wallet app that wants to become the wallet service other apps can connect to.
That means the reader should not expect a consumer app, a hosted wallet, a payment processor or a Nostr social client. This is developer plumbing for wallet builders. Its output is a wallet-service capability inside a Flutter wallet, not a standalone product someone installs from an app store.
There is also a history wrinkle. The original `nwc_wallet` repository is visible and useful for context, but the package people now find on Pub is `nip47` from the Dartstr monorepo. The article treats those as the same project line: original Flutter package idea, current Dartstr package implementation.
What the current package is
The current public package is `nip47` on Pub. Pub metadata checked on June 14, 2026 showed version 0.5.1+2 as latest, published on August 2, 2025, under the publisher `kumuly.dev`. The package description says it implements Nostr Wallet Connect as described in NIP-47.
The repository field points into `kumulynja/dartstr`, which redirects on GitHub to `LtbLightning/dartstr`. That monorepo describes itself as modular pure Dart packages for different Nostr NIPs, built with giving Nostr powers to Bitcoin wallets in mind. The NIP-47 package is one of those modules.
The original `kumulynja/nwc_wallet` repository still matters because awesome-nwc links to it. GitHub metadata showed it as a small MIT-licensed Dart repository created on June 2, 2024, last pushed on August 18, 2024, with 11 stars, 1 fork and no open issues. Its README warns that basic functionality should work but gives no guarantees yet.
The Dartstr package is the more current trail. GitHub metadata for `LtbLightning/dartstr` showed a Dart repository created on August 10, 2024, last pushed on August 2, 2025, with 6 stars and 1 open issue. That matches the package's later Pub releases and the source layout used in this review.
Why a wallet needs this layer
Nostr Wallet Connect lets a client app ask a Lightning wallet service for actions such as `pay_invoice`, `make_invoice`, `get_balance` or `lookup_invoice`. The request travels as an encrypted Nostr event through a relay. The wallet service decrypts it, checks the connection and permissions, talks to the wallet, then publishes an encrypted response.
A Flutter wallet can implement this from first principles, but the moving parts are easy to get wrong. It needs a wallet-service key, client connection keys, relay subscriptions, NIP-47 event kinds, request parsing, response mapping, error codes, connection persistence, and product policy around limits and approval.
`nip47` packages much of that wallet-service protocol layer. It gives a wallet app a way to create or resume connections, subscribe to requests, validate that the request comes from a known active connection, expose the request to the wallet code, and publish a response after the wallet has handled the Lightning operation.
The package does not make the Lightning decision. It is a protocol adapter between Nostr relays and the wallet. The wallet app still needs the node, liquidity, invoice creation, payment execution, failure handling, secure storage and user-facing permission model.
The public API is intentionally narrow
The public export file `lib/nip47.dart` exposes the database layer, data sources, repositories, entities and use cases. That tells you the library is organized more like a wallet-service toolkit than a single function helper.
The README centers the `WalletService` concept for developers. It says a wallet should provide a Nostr keypair and a NIP-01 repository that manages relay connections. Then the wallet listens to a stream of requests, handles each method with its own Lightning wallet code, and calls the corresponding response method.
The method enum covers `pay_invoice`, `multi_pay_invoice`, `pay_keysend`, `multi_pay_keysend`, `make_invoice`, `lookup_invoice`, `list_transactions`, `get_balance`, `get_info` and `custom`. That is a broad NIP-47 surface, but implementation details still matter because response parsing and request handling are not identical for every method.
The source also includes use cases for creating wallet connections, connecting through wallet-auth style flows, resuming connections, removing connections, updating connections and sending responses. A builder can compose those pieces, but must still decide how they fit into the wallet's own architecture.
Connection creation is the first product boundary
The `CreateWalletConnectionUseCase` takes the wallet-service keypair, relays, optional budget renewal, name, maximum amount, expiry, methods, notifications, custom methods, categories and `lud16`. It creates a connection, subscribes to requests for the derived client pubkey, and publishes an info event.
That is the place where a wallet app turns a user decision into a NWC connection. A good wallet UI should let the user understand which app is being connected, which methods are permitted, which relay is used, when the connection expires and what spending controls apply.
The code can carry fields such as `maxAmountSat`, `budgetRenewal`, `expiresAt`, permitted methods and notification support. But a field in a connection object is not the same thing as a complete safety policy. The wallet must check budgets and limits before paying, not merely store them.
The README is explicit about this. It says the package does not set or verify budget limits, auto-approval, manual approval or expiry date for NWC connections. That responsibility belongs to the wallet builder at request-handling time.
The wallet-auth path is newer and more ambitious
The `ConnectionUri` entity supports both `nostr+walletconnect` and `nostr+walletauth` schemes. Wallet Connect URIs include the client secret, wallet-service pubkey, relay list and optional `lud16`. Wallet Auth URIs carry a client pubkey, relays, name, icon, return URL, expiry, max amount, budget renewal, requested methods, notifications and isolation flags.
The `ConnectWalletAuthUseCase` reads the wallet-auth URI and creates a client-created connection. It can use the client's relays or an explicit wallet relay, apply requested methods and notifications, and publish an info event back to client relays.
This matters because the broader NWC ecosystem is moving beyond pasted connection strings. App-initiated authorization flows can improve onboarding, but they also increase the need for clear consent screens. A wallet should not blindly accept every requested method or amount simply because a URI asks for it.
For users, wallet-auth should feel like connecting an app with visible permissions. For developers, it is an input that must be normalized, constrained and stored with the same seriousness as any other payment authorization.
Relay behavior is part of the wallet UX
The `NostrDataSource` publishes info events and response events, subscribes to requests, unsubscribes from request streams and can subscribe to info events. The request subscription filters kind 23194 events by the client pubkey and a `p` tag for the wallet-service pubkey.
That is the correct NIP-47 shape: relays carry the event envelope, while encrypted content hides the command from the relay. But the relay still sees event kinds, tags, public keys and timing. A privacy-conscious wallet should use dedicated wallet-service keys and avoid linking them to a user's public Nostr profile.
The code throws if it fails to subscribe on any relays. That is useful because a wallet-service connection that cannot listen is not useful. Product UI should surface this state clearly instead of leaving the user with a connection URI that appears valid but never receives requests.
Relay choice is also reliability choice. A mobile wallet may sleep, lose network, reconnect late or miss events if relay retention is poor. A production integration should test offline intervals, duplicate events, stale events, relay OK behavior, request timeouts and reconnection after app restart.
Request validation is helpful but not sufficient
The request repository keeps a broadcast request stream, subscription maps and connection keypairs. When a request event arrives, it maps the event into a request entity, validates it, stores it locally and adds it to the stream for wallet code to handle.
The validation checks a few important things. It looks up the connection by client pubkey, rejects expired requests, rejects frozen connections and rejects methods that are not in the permitted standard or custom method lists. Those are the right first gates.
The TODO in the catch path is important. If request processing fails, the source logs the error and notes that it still needs specific error response handling. A wallet app should not leave a client waiting forever when a request is rejected; it should publish a clear NIP-47 error when possible.
The validation also does not replace payment policy. A `pay_invoice` request that passes method permission still needs budget checks, approval checks, fee policy, invoice decoding, amount sanity, route failure handling and user visibility before funds move.
The Lightning work remains yours
The README's usage example makes the separation plain. For `get_info`, the wallet must fill in alias, color, network, block height and methods. For `get_balance`, it must query its wallet. For `make_invoice`, it must generate the invoice. For `pay_invoice`, it must check limits, pay the invoice and return the preimage.
That is the right division of labor. The package should not know whether your wallet is backed by LDK, LND, Core Lightning, Breez SDK, phoenixd, a hosted API or a custom mobile node. It should carry NWC messages; the wallet should decide how payments actually happen.
A developer should therefore test the wallet layer and the NWC layer together. If the wallet pays an invoice but the response publish fails, the user may have paid while the client sees a timeout. If the response says paid before final settlement evidence is durable, the client may unlock something too early.
Payment code needs idempotency. The same request event may be seen more than once after reconnect or replay. The wallet app should store request IDs and payment hashes, avoid double payment, and give the client a consistent answer for retries.
Storage moved from the old README to SQLite
The original `nwc_wallet` README said the package did not persist the wallet-service keypair or created NWC connections between app restarts. The current `nip47` README still says the wallet must persist sensitive keys and connection data, but the code now includes a Drift and SQLite-backed local storage layer for connections, requests and responses.
Pub dependencies confirm that shift: `drift`, `sqlite3` and related generated database code are part of the package. The README tells Flutter apps to add `sqlite3_flutter_libs` so the package can persist connections, requests and responses in SQLite.
That is useful, but it is not the same as complete secure storage. A wallet-service private key and the connection metadata around permissions, names, budgets and expiry need careful handling. On mobile, that may mean platform secure storage for secrets plus SQLite for non-secret protocol state.
Review the actual storage split before shipping. If the database contains connection secrets, payment metadata or decrypted request details, it may require encryption-at-rest, backup rules and deletion behavior that ordinary app caches do not need.
Encryption is the main compatibility caveat
The source imports and uses `nip04` for request and response encryption. Request mapping decrypts incoming event content with NIP-04 using the wallet-service private key and client pubkey. Response mapping encrypts response content with the wallet-service private key and client pubkey.
Current NIP-47 text says request and response content is encrypted with NIP-44, while NIP-04 remains a backwards-compatibility assumption when encryption tags are absent. NIP-04 itself is marked deprecated and unrecommended in the NIPs repository.
This does not make `nip47` useless. NWC has a legacy installed base, and NIP-04 compatibility still matters. But it makes protocol compatibility a test item. A wallet that uses this package should know whether the client apps it supports expect NIP-04, NIP-44, or an explicit encryption negotiation path.
If the wallet wants current NIP-44 behavior, the developer should verify whether the package version in use supports it. The reviewed source and dependencies point to NIP-04. That should be stated in product docs so app developers are not surprised during integration.
Notifications need a version check
The event kind enum in the package defines info as 13194, request as 23194, response as 23195 and notification as 23196. The changelog says version 0.4.0+1 fixed the wrong kind for NIP-47 notification, and version 0.2.0 added 23196 notification event support.
Current NIP-47 names kind 23197 for notification events, with 23196 retained for backwards compatibility with NIP-04. That aligns with the broader encryption caveat: the package appears to focus on the legacy-compatible notification lane.
A wallet builder should test notification behavior with the exact apps it plans to support. Payment received and payment sent notifications are helpful for app UX, but a notification mismatch can create confusing partial compatibility where requests work but status updates do not.
Do not treat notification support as a binary checkbox. Check event kind, encryption mode, `p` tags, relay selection, app subscription behavior and whether notifications are permitted on the specific connection.
The response mapper shows what is mature and what is not
The response mapper covers common result shapes such as `get_info`, `get_balance`, `make_invoice`, `pay_invoice`, `lookup_invoice`, `list_transactions`, custom responses and error responses. It also maps sats to millisats for fields such as balance, amount and fees paid.
The same file leaves `multi_pay_invoice`, `pay_keysend` and `multi_pay_keysend` response parsing as unimplemented in one direction. That is not necessarily fatal for a wallet that does not advertise those methods, but it is important when selecting permitted methods.
A conservative wallet should only publish methods it can handle end to end. If the wallet cannot safely parse, execute and respond to keysend or multipay flows, those methods should stay off the connection until tests cover them.
The package supports custom methods, which is useful for experimentation. Custom methods also increase interoperability risk. If a wallet uses them, the app and wallet should document payload shape, error mapping and downgrade behavior.
Testing should include the unhappy paths
The package includes a test that creates a connection, uses a public relay, publishes an info event and verifies standard methods, custom methods and notification markers in the info event. That is useful as a smoke test for connection creation and relay publication.
Production testing needs more. Use a test wallet with fake funds or tiny limits. Try expired requests, frozen connections, unauthorized client keys, missing `p` tags, malformed encrypted content, unsupported methods, relay disconnects, duplicate requests and wallet errors during payment.
Also test ambiguous payment state. A relay may accept the request, the wallet may pay, and the response may fail to publish. Or the wallet may publish a response, but the client may miss it. The app should make those states visible and recoverable rather than silently retrying dangerous actions.
The most important test is permission reality. Create a connection that allows only `get_balance` and `make_invoice`, then send `pay_invoice`. Create a connection with an expired timestamp, then send a request. Create a budget boundary, then try to cross it. The user-safety story lives in those edges.
How it differs from a client package
The Dart `nwc` package and the Flutter `nip47` package sit on opposite sides of NWC. A client package consumes a connection URI and sends requests to a wallet service. A wallet-service package creates or accepts connections, listens for requests and turns wallet results into responses.
That difference matters for risk. A client-side app mainly protects a connection secret and decides when to ask for a payment. A wallet-service app sits next to funds and needs stronger policy controls because it may execute payment commands coming from remote apps.
A client library can often be stateless apart from the connection URI. A wallet-service library has to care about connection lifecycle, request stream management, response history, relays, local database state and user-granted permissions.
That is why Flutter Package belongs in Developer Tools & Libraries, not Wallet Interfaces. It helps wallet developers expose NWC, but it is not itself the user's wallet surface.
What to check before using it
First, confirm the version. The package reviewed here is `nip47` 0.5.1+2. If you use a different version or a git dependency from the old `nwc_wallet` repository, compare README, dependencies, event kinds, storage and encryption behavior before assuming the same conclusions.
Second, confirm encryption compatibility. Test with an NWC client that expects NIP-04 and another that expects NIP-44. Check whether info events advertise encryption modes, whether requests include encryption tags, and how the wallet behaves when the client sends something unsupported.
Third, confirm storage. Know which secrets are stored where, how they are restored after app restart, how a user revokes a connection, what happens to old request records, and whether app backups can leak connection data.
Fourth, confirm the money policy outside the library. A connection should have named context, expiry, permitted methods, spending limits, approval mode and clear revocation. The wallet should enforce those checks immediately before paying, not only when creating the connection.
Where it fits in the ecosystem
Flutter Package is small infrastructure for a specific developer audience: builders of Flutter Lightning wallets that want their wallet to connect to NWC apps. It is not competing with Alby Hub, LNbits, Zeus or a hosted NWC node. It is a package those kinds of wallet projects could evaluate if their architecture is Flutter/Dart.
It also sits next to Dartstr's other Nostr modules. The package depends on `nip01` for relay and event basics and `nip04` for encryption. That modularity can be attractive when a wallet wants only the NIPs it needs instead of a full social-client toolkit.
The cost of modularity is integration work. A wallet has to wire repositories, data sources, database setup, relay managers and use cases into its own app lifecycle. Mobile app lifecycle is not trivial; backgrounding, app sleep and network changes can affect every NWC connection.
For a serious wallet, this package is a starting point for implementation, not the whole user promise. The real product question is whether users can safely connect apps, see what those apps can do, revoke them, and understand every payment that leaves the wallet.
The practical close
Flutter Package is worth opening because it names a real gap in NWC: wallet builders need libraries too. Client apps get attention because they are visible, but the wallet-service side is where permissions, funds, relays and payment evidence meet.
The current `nip47` package gives Flutter wallet developers a concrete route into that work. It can create connections, subscribe to NWC requests, validate known clients and methods, publish info events and return encrypted responses through a Dart architecture that fits mobile wallet apps.
Its limits are equally concrete. The package leans on NIP-04, not the current NIP-44-preferred path; some response paths need careful review; budget and approval policy remain wallet responsibilities; and mobile relay reliability must be tested in real conditions.
Use it when you are prepared to own the wallet layer around it. If your wallet can enforce permissions, store secrets safely, handle relays honestly and test the exact NWC clients you support, Flutter Package can save implementation work. If those answers are still fuzzy, keep it in a lab wallet before connecting real money.
Useful Nostr context
These pages give mobile developers a clean path from Flutter into Dart, SDK and signing context.
- Dart NDK: A direct Dart-side Nostr development reference.
- Dart Package: Adjacent package context for Dart users.
- NIP-07 signing model: Useful contrast for signing models outside mobile apps.
- Developer Tools & Libraries: The broader shelf for SDKs, protocol helpers and implementation references.
- NIPs: Protocol standards and their product consequences.
- Keys and identity: A useful companion when an article touches signing, secrets or custody boundaries.
Sources worth opening
Read the original `nwc_wallet` README to understand why the hub label exists, then use the current `nip47` Pub package, Dartstr source files and NIP-47 specification to judge what the library actually does today.
- getAlby awesome-nwc raw README
- getAlby awesome-nwc GitHub repository
- Original nwc_wallet GitHub repository
- Original nwc_wallet GitHub API metadata
- Original nwc_wallet README
- nip47 package on Pub
- Pub API metadata for nip47
- nip47 versions on Pub
- nip47 changelog on Pub
- nip47 score on Pub
- nip47 API documentation
- Dartstr GitHub repository
- Dartstr GitHub API metadata
- Dartstr package README for nip47
- Dartstr package changelog for nip47
- Dartstr package pubspec for nip47
- nip47 public exports
- nip47 connection URI entity
- nip47 method enum
- nip47 event kind enum
- nip47 notification entity
- nip47 error code entity
- nip47 request mapper
- nip47 response mapper
- nip47 Nostr data source
- nip47 request repository implementation
- nip47 create wallet connection use case
- nip47 connect wallet auth use case
- nip47 test file
- NWC documentation home
- NWC docs for Bitcoin Lightning wallets
- Nostr Wallet Connect official site
- NIP-47 Nostr Wallet Connect
- NIP-04 encrypted direct message
- NIP-44 encrypted payloads
- NIP-06 key derivation
- nip01 package on Pub
- nip04 package on Pub
- sqlite3_flutter_libs package on Pub
- ldk-node-flutter GitHub repository





