Dart Package
The `nwc` Dart package is a small client-side Nostr Wallet Connect helper. It parses connection URIs, opens relay connections, encrypts NIP-47 requests, signs events and deserializes wallet responses for Dart and Flutter apps.
The generic label hides a specific package
The Apps hub label says Dart Package, but the actual project is the `nwc` package on pub.dev. That matters because the label is too generic to be useful by itself. There are many Dart packages in the Nostr ecosystem, and this one has a very specific job: client-side Nostr Wallet Connect.
`nwc` is published by the verified Pub publisher `bringin.xyz` and links to the GitHub repository `bringinxyz/nwc`. The package description says it simplifies the integration of Nostr Wallet Connect into client applications. That is the correct category: this is app-side wallet connection plumbing.
It is not Dart NDK. Dart NDK is a broader Nostr toolkit with relay selection, signers, cache strategies, zaps, NWC and many other usecases. The `nwc` package is narrower. It focuses on parsing a connection URI, talking to the relay, encrypting commands and parsing wallet service responses.
It is also not the `nip47` package from Kumuly. That package takes care of the wallet-service side of NWC. The `nwc` package reviewed here is for a client application that wants to talk to a remote Lightning wallet service.
Why it exists
Nostr Wallet Connect lets an app interact with a Lightning wallet through Nostr relays. A user obtains a connection URI from a compatible wallet service, gives it to a client app, and the app can send encrypted requests such as balance checks, invoice creation or invoice payment.
A Dart or Flutter app could implement that flow manually. It would need to parse the URI, open websocket relay connections, build Nostr events, sign them with the connection secret, encrypt and decrypt payloads, subscribe to responses, map result types and handle timeout or relay failures.
`nwc` packages those pieces in a small Dart library. Its README presents a direct usage path: create an `NWC` instance, parse the connection URI, initialize the relay service, subscribe to kind 23195 response events, decrypt content with NIP-04 and send kind 23194 request events.
This makes sense for a mobile or desktop Flutter app that already has its own interface and simply needs remote wallet access. The package does not give you a payment product. It gives you enough protocol machinery to ask a compatible wallet service for wallet operations.
The project is small and quiet
GitHub metadata checked on June 14, 2026 showed `bringinxyz/nwc` as a small MIT-licensed Dart repository. It was created on May 10, 2024, last pushed on June 6, 2024 and not archived. The repository had 6 stars, 0 forks and no open issues.
Pub metadata showed version 1.0.1 as the latest release, published on June 6, 2024. The only stable versions were 1.0.0 from May 10, 2024 and 1.0.1 from June 6, 2024. The 1.0.1 changelog entry adds deserialization for `list_transactions`.
That quiet state is not automatically bad. A small protocol helper can be feature-complete for a narrow task. But wallet-adjacent code ages when the protocol evolves, and NIP-47 has moved toward NIP-44 encryption since the first NIP-04 era.
The practical posture is conservative. Treat `nwc` as a compact legacy-compatible client helper that should be inspected against the wallet services you plan to support. Do not treat its old publication date as proof that every current NWC feature is covered.
The package surface
The package exports `src/nwc_base.dart`, logger utilities, models, NWC result types, the NIP-47 result deserializer and concrete result classes. The main `NWC` class is a singleton-style entry point exposing services for keys, relays, general utilities, NIP-04 and NIP-47.
The dependency list is appropriately small for this job. Pub metadata lists `bip340`, `bech32`, `convert`, `crypto`, `equatable`, `hex`, `meta`, `pointycastle` and `web_socket_channel`. Those dependencies map to signing, encoding, encryption and relay websocket transport.
The package supports Dart and Flutter across Android, iOS, Linux, macOS, web and Windows according to the Pub page. That breadth comes from Dart package compatibility rather than from a visible platform-specific integration layer.
There is no bundled wallet UI, no permission screen, no connection database, no invoice verification backend and no app-specific policy. The app that imports `nwc` must provide product behavior around the protocol calls.
Connection URI parsing is simple
The URI parser is in `nosrt_wallet_connect_uri.dart`, including the misspelling in the file name. It parses a `nostr+walletconnect://` URI and extracts a public key, a single `relay` query parameter, a `secret` and optional `lud16` value.
That simple parser matches the examples and the older connection form. It does not support the newer multi-relay `relays` parameter that some current NWC implementations use. By contrast, current NIP-47 text allows relay information in the URI and current libraries may support multiple relays for redundancy.
A client using this package should test the exact wallet services it expects. If a wallet returns a single `relay` parameter, the parser path is straightforward. If a wallet returns `relays`, duplicate relay parameters or a richer connection URI, the app may need compatibility code or another library.
The secret in the URI is not a label. In the example, it is used as the private key for signing request events and as the input to NIP-04 encryption. If your app stores that URI, it is storing wallet access material.
The relay layer is direct
The README tells developers to initialize the relay service with the relay from the parsed URI. The relay source opens websocket connections through `web_socket_channel`, keeps a registry of relay sockets and events, and can start event subscriptions or send events to every registered relay.
The subscription example filters for kind 23195 responses from the wallet service public key and sets `since` to the current time. That is a sensible demo because it avoids pulling older response events, but a production app still needs a durable request-response model.
The relay sender waits for the first OK command from registered relays and uses a timeout. This is helpful, but it should not be confused with wallet success. A relay OK means the relay accepted an event. It does not mean the wallet service decrypted, authorized or executed the command.
The relay layer exposes operational risks. A relay can be unavailable, slow, misconfigured, rate-limited or shared with unrelated Nostr traffic. Apps should show the difference between relay connection failure, relay OK timeout, wallet response timeout and wallet-side error.
The request event path
The example builds NWC requests manually. For `get_balance`, it creates a JSON message with method `get_balance`, encrypts it with `nwc.nip04.encrypt`, wraps it in a kind 23194 event, adds a `p` tag pointing at the wallet service pubkey and signs with `KeyPairs(private: parsedUri.secret)`.
The same pattern appears for `make_invoice`, `pay_invoice` and `list_transactions`. Amounts are expressed in millisats where NIP-47 expects millisats. `pay_invoice` sends a BOLT11 invoice. `list_transactions` sends a limit parameter.
This gives the developer control, but it also means command construction is not fully hidden behind high-level methods. You can see the protocol. That is good for auditability, but it requires the app to build valid command payloads and choose safe parameters.
A serious integration should wrap these calls in its own product functions. Do not let random UI code construct wallet command JSON. Centralize command creation, logging rules, permission checks, timeouts, user prompts and response handling.
Response parsing is narrow
The 1.0.1 release added `list_transactions` deserialization, which is why the changelog matters. The result deserializer reads decrypted JSON, checks for `result_type`, maps it to an `NWCResultType` enum and builds result objects for `get_balance`, `make_invoice`, `lookup_invoice`, `pay_invoice` and `list_transactions`.
If there is no `result` field, the deserializer treats the payload as an error result. If the result type is unsupported, it throws. That is acceptable for a small package, but app code should catch and explain unsupported responses rather than crashing a payment flow.
NIP-47 has grown beyond only the core commands in this package. Current NIP-47 examples include `get_info`, notifications and additional command families. This package does not expose every current result path in the same way Dart NDK's newer NWC usecase does.
That does not make `nwc` useless. It means the package is best for the specific methods it models. When a wallet service advertises capabilities, compare them with the methods your package actually parses before showing a feature in the UI.
NIP-04 is the big compatibility caveat
The package README says secure communication is handled with NIP-04, and the source implements AES-CBC style NIP-04 encryption and decryption. That fits the initial version of NWC, but it is no longer the preferred path in the current NIP-47 specification.
The current NIP-47 text says request and response content is encrypted with NIP-44, while noting that the initial version used NIP-04 for backward compatibility. It also says client applications should prefer NIP-44 when the wallet service supports it and use encryption tags to negotiate support.
`nwc` does not appear to implement NIP-44 negotiation or request `encryption` tags. It assumes the NIP-04 style. That can still work with legacy wallet services or services that allow NIP-04 fallback, but it is a real protocol age marker.
This is the first integration test to run. Connect to the wallet service you actually plan to support, fetch or inspect its info event if possible, check its encryption support and confirm that NIP-04 requests are still accepted. If the wallet requires NIP-44, this package is not enough by itself.
Key handling is convenient and sensitive
The package includes a keys service that can generate key pairs, derive public keys from private keys, sign messages and cache `KeyPairs` objects by private key. The `KeyPairs` model expects a 64-character hex private key and derives the public key with `bip340`.
In the NWC example, the connection secret is used as the private key for request events. That matches NIP-47's connection model: each connection should have its own app-side key material, separate from the user's main Nostr identity key.
This separation is not optional trivia. NIP-47 explicitly avoids using the user's identity key so payment activity is not linked to the user's profile. An app that reuses a public social identity key for wallet requests weakens that privacy boundary.
Developers should store connection material as wallet permission material. Use platform secure storage where available, give users a disconnect path, avoid logging full URIs, never paste connection secrets into analytics and clear in-memory state when the user revokes a connection.
Permissions live outside the library
NWC safety depends heavily on permissions. A connection may allow only balance reads, invoice creation, payments, transaction listing or a broader method set. The app should know what it is asking for and what the wallet service will allow.
`nwc` helps build requests and parse responses, but it does not itself enforce a product permission model. It does not show a wallet-side budget, does not require a user confirmation screen and does not prevent the app from sending a `pay_invoice` request if the connection allows it.
This is why NWC can feel both powerful and dangerous. A client app never touches funds directly, but a wallet service can execute commands sent through relays when the connection is authorized. The user experience should make the scope visible before a connection is saved.
A careful app should read or request wallet info when available, display supported methods, make payment limits clear, separate receive-only flows from spending flows and require a user action before sending a payment command. The library gives plumbing, not consent design.
It is not a wallet service package
The `nwc` package is app-side. It helps a client talk to a remote wallet service. It does not host the wallet service, authorize incoming app connections, manage per-app budgets, expose wallet APIs or persist service-side connection records.
That distinction explains the separate `Flutter Package` entry in the Apps hub. The awesome-nwc list links that entry to a Flutter package for the wallet-service side of NWC. That is a different problem: a wallet app needs to listen for app requests, validate permissions and publish responses.
`nwc` is for the other side of the wire. It can be used by a Nostr client, utility app, payment app, checkout prototype or Flutter interface that wants to connect to a wallet service already created elsewhere.
If you are building a wallet, this package may help test the client perspective, but it is not enough to make your wallet NWC-compatible as a service. For that, review wallet-service libraries, NIP-47 command handling, connection storage and wallet API authorization.
Bringin context helps but does not define it
Bringin is the organization behind the repository and the verified Pub publisher. Its public site describes a Bitcoin app for Europe with Lightning, self-custody wallet features, bitcoin-to-euro flows, card spending and bank-account rails.
That context is useful because it explains why Bringin would publish NWC code. A Bitcoin app with Lightning surfaces may need app-to-wallet interoperability, and a small Dart package can extract reusable protocol work from an app team's internal needs.
The package should still be evaluated on its own. The repository is small, last pushed in 2024 and does not appear to be a broad SDK for every Bringin product. The package page, README, code and current NIP-47 spec are stronger evidence than marketing copy.
Use the publisher context as a maintainer signal, not as a security guarantee. Wallet-related libraries still need code review, protocol compatibility tests and clear application-level controls.
What to test with real wallets
Start with connection URI formats. Test a wallet service that returns a single `relay` parameter and one that returns modern relay formats. Confirm that the parser handles the exact URI you expect before you store it or show success.
Then test encryption compatibility. Try a wallet service that supports NIP-04 fallback and one that prefers or requires NIP-44. If the wallet info event advertises only `nip44_v2`, the package's NIP-04 request path should be considered incompatible unless you add your own layer.
Next test command coverage. Send `get_balance`, `make_invoice`, `pay_invoice`, `lookup_invoice` and `list_transactions` only where the wallet service advertises support. Check wallet-side permission errors, unsupported method errors, malformed invoices, expired invoices and repeated taps.
Finally test app behavior under relay trouble. Disconnect wifi, use a slow relay, use a relay that accepts requests but never returns a response, send to a wallet service that is offline and close subscriptions when screens are gone. Payment UX fails hardest when the difference between pending and impossible is invisible.
Logging and examples need discipline
The package includes logger utilities and the example prints balances, invoices, preimages, error messages and OK commands. That is normal for a demo, but production wallet code should review every log line before shipping.
A Lightning preimage proves payment. An invoice can identify a payment request. A connection URI contains wallet access material. A relay URL can reveal where a user's wallet service listens. A raw decrypted response can contain transaction metadata. None of those belong in analytics or crash logs by default.
The README examples are useful because they show the protocol shape. They are not a production application architecture. A product should add secret redaction, structured error categories, rate limiting, user confirmation and backend verification where payment state unlocks anything important.
Use examples as a learning path, then wrap them. The package makes it easy to send a `pay_invoice` event. A real app must decide when the user meant to pay, how to recover from ambiguous state and what evidence counts as settled.
How it compares with Dart NDK
Dart NDK and `nwc` overlap at the NWC layer, but they are not substitutes in every direction. Dart NDK is a broad Nostr toolkit with caching, relay selection, signers, accounts, zaps, Blossom, Cashu and a newer NWC implementation. `nwc` is a small focused client package.
The small package can be easier to inspect. You can read the URI parser, NIP-04 implementation, relay service and response deserializer quickly. That is useful when you need a narrow dependency for a narrow job.
The broad toolkit may be better when an app already needs many Nostr features beyond wallet requests. If your product must manage contacts, relay lists, signers, zaps, NIP-05 and NWC in one architecture, Dart NDK gives more shared structure.
The decision should come from the app, not the label. If you only need legacy-compatible NWC client calls in Dart, `nwc` may be enough after testing. If you need current NIP-44 negotiation and a wider Nostr application stack, compare it carefully with newer libraries.
What it is not
It is not a Lightning node. It does not open channels, hold funds, route payments or verify the Lightning network state. It sends wallet commands to a service that has access to a wallet API.
It is not a Nostr social client. It can create and parse some Nostr events because NWC rides on Nostr relays, but it does not give you feeds, profiles, lists, moderation, search or publishing workflows.
It is not a full NIP-47 conformance suite. It models the main request and response path shown in its README and examples, with a 1.0.1 addition for `list_transactions`. Current NWC features and encryption negotiation need separate review.
It is not a permission manager. If an app asks a wallet to pay an invoice through this package, the user-safety story depends on the wallet service, connection limits and product UI. The library can move the event; it cannot make the request wise.
The practical close
The `nwc` Dart package is useful because it keeps one small thing understandable: a Dart or Flutter client can talk to a remote Lightning wallet over Nostr Wallet Connect without manually writing every relay, event, signing and parsing primitive.
Its best use is a controlled integration where the app supports a known wallet service, accepts the NIP-04 compatibility boundary and wraps the examples in careful product code. In that setting, the package is light, readable and easy to audit.
Its risk is age. NIP-47 now prefers NIP-44, supports encryption negotiation, includes notification behavior and continues to evolve across wallet services. A package last published in June 2024 should be checked against the 2026 wallet ecosystem before it is trusted with money movement.
Use `nwc` with a precise compatibility statement: which wallets, which methods, which encryption mode, which relay assumptions and which storage rules. If you can answer those questions, the package can serve a small app well. If not, keep testing before connecting real sats.
Useful Nostr context
These pages help place the Dart package beside Flutter, NDK and broader developer tooling.
- Dart NDK: The closest Dart-side companion for Nostr development.
- Flutter Package: The mobile app route for Flutter projects.
- NDK: The JavaScript NDK reference point.
- 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
Start with the Pub package page, README, example and source files for URI parsing, NIP-04 encryption, relay handling and NIP-47 response deserialization. Then compare the implementation against the current NIP-47, NIP-04 and NIP-44 specifications.
- nwc package on Pub
- Pub API metadata for nwc
- nwc changelog on Pub
- nwc versions on Pub
- nwc example on Pub
- nwc Dart API documentation
- bringinxyz/nwc GitHub repository
- bringinxyz/nwc GitHub API metadata
- bringinxyz/nwc README
- bringinxyz/nwc pubspec
- bringinxyz/nwc changelog
- bringinxyz/nwc example file
- bringinxyz/nwc public exports
- bringinxyz/nwc NWC class source
- bringinxyz/nwc NIP-47 source
- bringinxyz/nwc NIP-47 result deserializer
- bringinxyz/nwc connection URI parser
- bringinxyz/nwc NIP-04 source
- bringinxyz/nwc relay service
- bringinxyz/nwc websocket service
- bringinxyz/nwc event model
- bringinxyz/nwc request model
- bringinxyz/nwc key service
- bringinxyz/nwc key pairs model
- bringinxyz/nwc result type enum
- bringinxyz/nwc result exports
- bringinxyz/nwc MIT license
- Bringin website
- Bringin GitHub organization API
- getAlby awesome-nwc list
- getAlby awesome-nwc raw README
- Nostr Wallet Connect official site
- Alby guide to Nostr Wallet Connect
- NIP-47 Nostr Wallet Connect
- NIP-04 encrypted direct message
- NIP-44 encrypted payloads
- Dart packages publisher page for bringin.xyz





