Skip to content
Draft
Show file tree
Hide file tree
Changes from 144 commits
Commits
Show all changes
167 commits
Select commit Hold shift + click to select a range
191b013
wallet event
1-leo Jul 14, 2025
896f66d
data conversions
1-leo Jul 14, 2025
bd999bd
dedicated enitity for wallet content
1-leo Jul 15, 2025
b21be80
cashu token event
1-leo Jul 15, 2025
33cb7d3
spending history
1-leo Jul 15, 2025
68be1f7
cashu example api
1-leo Jul 15, 2025
774918c
repo setup
1-leo Jul 15, 2025
c57f8f6
swap signatures
1-leo Jul 16, 2025
0881235
swap models
1-leo Jul 16, 2025
2263809
Merge branch 'master' into cashu-wallet
1-leo Jul 18, 2025
31ce2c5
cashu domain seperator
1-leo Jul 18, 2025
581ce46
blindMessage, unblind sig
1-leo Jul 19, 2025
a65c1b8
create blinding msg for amounts
1-leo Jul 19, 2025
b6d7421
keysets endponts
1-leo Jul 21, 2025
e363d3e
fix parsing issue
1-leo Jul 21, 2025
5494425
mint keys cache logic
1-leo Jul 21, 2025
c80d0a2
mint quote
1-leo Jul 21, 2025
1d31bd0
mintTokens
1-leo Jul 21, 2025
3f2f723
cashu keypair, fund logic
1-leo Jul 22, 2025
d62649f
unblind signatures
1-leo Jul 22, 2025
e28b687
cashu token encoder v4
1-leo Jul 22, 2025
55dcbf6
use active keyset, polling for payment state
1-leo Jul 22, 2025
97f5c2f
Merge branches '202-gift-wrap-nip01event-parsing-err' and 'cashu-wall…
1-leo Jul 25, 2025
387a779
save proofs
1-leo Jul 25, 2025
6b4af9e
decode v4
1-leo Jul 25, 2025
961421a
receive usecase
1-leo Jul 25, 2025
1a97d19
receive own tokens
1-leo Jul 25, 2025
d603039
rename quoteSignature
1-leo Jul 25, 2025
e6954d2
proof select
1-leo Jul 26, 2025
959f627
spend usecase
1-leo Jul 26, 2025
322f1dc
melt quote, check melt state
1-leo Jul 28, 2025
ba0fa83
typo
1-leo Jul 28, 2025
852c3eb
melt tokens
1-leo Jul 28, 2025
d7b470a
redeem usecase
1-leo Jul 28, 2025
c9b5a77
refactor: filter keyset by active and unit
1-leo Jul 28, 2025
6460bcb
proof select with fees
1-leo Jul 28, 2025
6c87ee4
adr cashu wallet design
1-leo Jul 29, 2025
8719df3
adr: wallet cashu
1-leo Aug 4, 2025
d14e04b
wallet api proposal
1-leo Aug 4, 2025
cebd490
rename to mintUrl
1-leo Aug 4, 2025
2d4961d
remove generics
1-leo Aug 4, 2025
e9fcad6
cashu decorator for db transactions
1-leo Aug 4, 2025
994577e
simple mutex to remove dep
1-leo Aug 4, 2025
b0ba9c1
api design, remove generics
1-leo Aug 4, 2025
708da94
split fund code to add status reporting
1-leo Aug 5, 2025
89dc18d
objectbox db impl
1-leo Aug 5, 2025
74e391f
expose wallet usecase
1-leo Aug 5, 2025
e0457db
fix filter proof by unit
1-leo Aug 5, 2025
8addde7
fix: dont add total balance
1-leo Aug 5, 2025
549cb83
multiple currencies in wallet account
1-leo Aug 6, 2025
8658f19
refactor: remove wallet prefix from cashu
1-leo Aug 7, 2025
4da6091
refactor: remove acc prefix, clear seperation wallet
1-leo Aug 7, 2025
ec432d8
wallet shared operations setup
1-leo Aug 7, 2025
0c4a365
refactor wallets usecase to allow more generic cases
1-leo Aug 7, 2025
f3c767b
remove logic from entity
1-leo Aug 7, 2025
7447bf8
cache manager get all keysets
1-leo Aug 7, 2025
9b3d616
objectbox get all keysets
1-leo Aug 7, 2025
f41b527
wallets repo cashu balance stream
1-leo Aug 7, 2025
9a32a0d
cashu balance state, stream
1-leo Aug 7, 2025
a78ea16
mark dev class
1-leo Aug 7, 2025
94e2bba
WalletsRepoImpl description
1-leo Aug 7, 2025
bb30417
null for default wallet id
1-leo Aug 7, 2025
1f511fe
wallets init code
1-leo Aug 7, 2025
87c7a83
wallet entity, storage setup
1-leo Aug 8, 2025
7ca1d47
wallets in wallets repo
1-leo Aug 8, 2025
d201cb7
wallet transaction json conversion
1-leo Aug 8, 2025
e882e2d
storage transactions
1-leo Aug 8, 2025
cb4f33a
latest transaction state
1-leo Aug 8, 2025
4a8efd0
remove reference code
1-leo Aug 8, 2025
96dafe9
rename cashu
1-leo Aug 8, 2025
7ee8e19
fix: usecase name
1-leo Aug 8, 2025
ef94039
fix: type parsing
1-leo Aug 8, 2025
cd4742a
fix: add keysets to decorator
1-leo Aug 8, 2025
e726bd3
fix: store keysets from network
1-leo Aug 8, 2025
2337383
fix: null err
1-leo Aug 8, 2025
d119c1b
fix: cashu usecase naming
1-leo Aug 8, 2025
e654689
cashu test usecase naming
1-leo Aug 8, 2025
c3dbd34
filter streams for wallet usecase
1-leo Aug 9, 2025
3613e66
decorate transactions db operations
1-leo Aug 9, 2025
b5896a4
fix: save transaction to db
1-leo Aug 9, 2025
61be7f2
fix: objectbox transaction not async
1-leo Aug 9, 2025
074b9aa
fix: wallet transaction state storage
1-leo Aug 9, 2025
0c52e5f
mintInfo entity
1-leo Aug 9, 2025
87407f9
mint info storage impl
1-leo Aug 9, 2025
e67adf1
fix: cleanup on ndk destroy
1-leo Aug 9, 2025
19bacfd
cashu knownMints
1-leo Aug 11, 2025
6b79d7a
fix: mintUrl in CashuMintBalance
1-leo Aug 11, 2025
db2e467
fix: cashuMintInfo fromJson add mintUrl
1-leo Aug 11, 2025
3594499
fix: objectbox mintInfo parsing
1-leo Aug 11, 2025
88add12
feat: dynamically create wallets based on usecase data
1-leo Aug 11, 2025
4d63ef8
fix: cashu balances grouping
1-leo Aug 11, 2025
f0a0683
fix: wallet filter balances by mint
1-leo Aug 11, 2025
729f7e4
feat: spending with state
1-leo Aug 13, 2025
0061ce5
fix: filter keyset for unit
1-leo Aug 13, 2025
4ee9893
refactor dublicated code
1-leo Aug 13, 2025
7776356
mark proofs as spend
1-leo Aug 14, 2025
4ff2b4b
return token obj
1-leo Aug 18, 2025
512eb3a
receive ecash state
1-leo Aug 18, 2025
e64b737
cashu getBalance return zero values
1-leo Aug 19, 2025
025f701
cashu spend improved return type
1-leo Aug 19, 2025
61ec9cc
remove pending transaction via helper method
1-leo Aug 19, 2025
2ca8d8b
fix: swap split change, exact amount
1-leo Aug 20, 2025
1f66d52
fix: identify cashu proof by pubKey
1-leo Aug 20, 2025
68d6465
fix: getProofs mem cache manager
1-leo Aug 20, 2025
16e26aa
add dev test
1-leo Aug 20, 2025
0b5f454
expose mint info methods
1-leo Aug 21, 2025
a00525a
mint info parse gracefully
1-leo Aug 21, 2025
653ccf2
store mint info
1-leo Aug 22, 2025
0436c99
fix: correct balance with inactive keysets
1-leo Aug 22, 2025
6325f49
fix: sort swap outputs
1-leo Aug 22, 2025
d1d04e4
fix: dismiss large keyset amounts
1-leo Aug 23, 2025
167cd89
add offset to transactions, expose in wallets usecase
1-leo Aug 23, 2025
e733a6f
fix add transactionDate on err
1-leo Aug 24, 2025
6dcb4b5
update adr
1-leo Sep 3, 2025
d8b1c21
remove reference code
1-leo Sep 3, 2025
dbae2be
fix: test secret comparison
1-leo Sep 3, 2025
10d39f9
remove reference code
1-leo Sep 3, 2025
dec9df9
remove dev tests
1-leo Sep 3, 2025
96f2a20
wip: spend tests
1-leo Sep 3, 2025
ead09d3
cashu fund test
1-leo Sep 4, 2025
934ee1f
expired quote test
1-leo Sep 4, 2025
a803300
mock cashu http client
1-leo Sep 6, 2025
6bce5f2
test: mint returns no signatures
1-leo Sep 6, 2025
41265ac
test: integration fund e2e
1-leo Sep 6, 2025
748456e
test: cashu spend exceptions
1-leo Sep 6, 2025
3b03eac
test: spend integration test
1-leo Sep 8, 2025
e693a30
test: rcv integration test
1-leo Sep 8, 2025
9285ff8
redeem state
1-leo Sep 8, 2025
cce0654
test: redeem
1-leo Sep 9, 2025
6887145
test: recovery of funds on fail
1-leo Sep 9, 2025
f69c0cb
test: spend rcv check status
1-leo Sep 9, 2025
e765508
fix: check completion when transcation rcv
1-leo Sep 10, 2025
216b32d
skip blossom integration example
1-leo Sep 10, 2025
da8dc55
test: skip example
1-leo Sep 10, 2025
3775829
fix: typo
1-leo Sep 10, 2025
4084f4b
doc: cashu main methods
1-leo Sep 10, 2025
a826e57
doc: wallets usecase
1-leo Sep 10, 2025
9aa85bc
feat: cashu mnemonic
1-leo Sep 11, 2025
0229e0b
test: generating seed phrase
1-leo Sep 11, 2025
32594d9
feat: seed phrase user api
1-leo Sep 11, 2025
78bcd28
setup cashu secret storage counter
1-leo Sep 12, 2025
148703a
Merge branch 'master' into cashu-wallet
1-leo Sep 12, 2025
b6c6c63
deps: tmp url path for bip32_keys
1-leo Sep 12, 2025
44c262f
objectbox cashu secret counter
1-leo Sep 12, 2025
24d997a
cashu entity store token
1-leo Sep 30, 2025
4427d9f
demo app wallets+ cashu
1-leo Sep 30, 2025
9f129c3
add recent transactions
1-leo Oct 1, 2025
eec7f7c
demo app config, cashu seed phrase
1-leo Oct 1, 2025
bb6bee1
Merge branch 'fix-test-cache-read-any-order' into cashu-wallet
1-leo Oct 1, 2025
9b0c784
demo app scrollable ui
1-leo Oct 1, 2025
7d60214
nwc wallet proof of concept
Oct 1, 2025
81ee29e
fix typo
1-leo Oct 1, 2025
3567b0d
fix: allow fee reserve 0
1-leo Oct 15, 2025
960c86d
fix: melt change parsing
1-leo Oct 15, 2025
2ef650d
fix: melt map output blanks to change
1-leo Oct 15, 2025
c3d19ff
sembast cashu impl
1-leo Oct 17, 2025
742e645
improve demo wallet widgets with list of wallets
Oct 21, 2025
4dae87c
Merge branch 'master' into cashu-wallet
1-leo Oct 23, 2025
2f1d5fe
chore: fix merge issues
1-leo Oct 23, 2025
77f2d70
feat: nut13 deterministic secrets
1-leo Oct 29, 2025
b231d04
export types
1-leo Oct 31, 2025
de153c3
Merge branch 'master' into cashu-wallet
frnandu Dec 2, 2025
16dc3b5
use _tabs.length
frnandu Dec 2, 2025
22658e2
deps
frnandu Dec 2, 2025
e1eb3a3
make it more nwc and lazy friendly
frnandu Dec 2, 2025
72d3c35
close subjects
frnandu Dec 3, 2025
bd61982
Merge pull request #325 from relaystr/feat-wallets-usecase
frnandu Dec 3, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions doc/library-development/ADRs/wallet-cashu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Architecture Decision Record: Wallet Cashu API

Title: Wallet Cashu - api design

## status

completed

Updated on 03-09-2025

## contributors

- Main contributor(s): leo-lox

- Reviewer(s): frnandu, nogringo

- Final decision made by: frnandu, leo-lox, nogringo

## Context and Problem Statement

We want to introduce a wallet use-case. To support multiple types of wallets like NWC and Cashu, we need different implementations.
Depending on the specific needs of a wallet, the capabilities are different.
How can we achieve a wallet design for the Cashu wallet that works for our users as well as for the generic wallet use-case?

## Main Proposal

Give the users methods to start a action like [spend, mint, melt] and notify about pending transactions via BehaviorSubjects.
The objects, by the behavior subjects then have methods to confirm or cancel where appropriate.
This is needed so the end-user can check the fees (transaction summary) before making a transaction.

A pseudocode flow would look like this:

```dart
main(){
BehaviorSubject<Transaction> pendingTransactions = BehaviorSubject<Transaction>();


/// initiate a transaction
void spend(Unit 'sat', Reciever receiver) {
/// ...wallet implementation
}

/// user code listen to pending transactions
pendingTransactions.listen((transaction) {

/// tbd if we have a stauts pending or a diffrent subscription for done (sucessfull, err) transactions
if (transaction.type == TransactionType.spend && transaction.status == TransactionStatus.pending) {

/// display transaction summary to user
displayTransactionSummary(transaction.details);

// User confirms the transaction
if (userConfirms()) {
transaction.confirm()
} else {
transaction.cancel()
}
}

if(transaction.status == TransactionStatus.done) {
/// display result to user [sucess, error]
displayTransactionResult(transaction);
}

});
}
```

Flow:

1. Listen to pending transaction
2. Initiate the transaction by calling a function.
3. React to pending transactions and confirm/decline them
4. React to transaction completed

## Consequences

The reactive nature of transactions makes it necessary to use some form of subscriptions.
Using this approach, the available options to the user/dev are quite clear.

- Pros

- Clear separations of what options are available at a given time.
- Data is directly available; no need to call a getter
- Setup for the user/dev is structured
- Clear separation between pending and final.
- Does not necessarily require cashu/implementation knowledge

- Cons
- Requires subscription management for the user/dev
- More complex to implement (for us)
- less control for the user/dev, although we can expose methods if more control is needed.

## Alternative proposals

Use functions for each transaction step and user/dev uses them manualy.
pro: - a lot more control
con: - more complex, requires cashu knolege

## Final Notes
13-08-2025
Proposal dismissed
Proceeding with a simpler method-based approach in combination with transaction streams.
182 changes: 182 additions & 0 deletions doc/usecases/cashu.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
---
icon: fiscal-host
title: cashu - eCash
---

[!badge variant="primary" text="high level"]

!!!danger experimental
DO NOT USE IN PRODUCTION!
!!!

!!!
no recovery option, if the user deletes the db (by resetting the app) funds are lost \
This API is `experimental` you can try it and submit your feedback.
!!!

## When to use

Cashu usecase can manage eCash (digital cash) within your application. It provides functionalities for funding, spending, and receiving eCash tokens.

## Examples

## add mint url

!!!
When you receive tokens or initiate funding, the mint gets added automatically
!!!

```dart
/// adds to known mints
ndk.cashu.addMintToKnownMints(mintUrl: "https://example.mint");

/// stream [Set<CashuMintInfo>] of known mints
ndk.cashu.knownMints;

/// get [CashuMintInfo] without adding it to known mints
ndk.cashu.getMintInfoNetwork(mintUrl: "https://example.mint");

```

## fund (mint)

```dart
final initTransaction = await ndk.cashu.initiateFund(
mintUrl: "https://example.mint",
amount: "100",
unit: "sat",
method: "bolt11",
memo: "funding example",
);

/// pay the request (usually lnbc1...)
print(initTransaction.qoute!.request);

/// retrieve funds and listen for status
final resultStream =
ndk.cashu.retrieveFunds(draftTransaction: initTransaction);

await for (final result in resultStream) {
if (result.state == ndk_entities.WalletTransactionState.completed) {
/// transcation done
print(result.completionMsg);
} else if (result.state == ndk_entities.WalletTransactionState.pending) {
/// pending
}
else if (result.state == ndk_entities.WalletTransactionState.failed) {
/// transcation done
print(result.completionMsg);
}
}

```

## redeem (melt)

```dart

final draftTransaction = await ndk.cashu.initiateRedeem(
mintUrl: "https://example.mint",
request: "lnbc1...",
unit: "sat"
method: "bolt11",
);

/// check if everything is ok (fees etc)
print(draftTransaction.qouteMelt.feeReserve);

/// redeem funds and listen for status
final resultStream =
ndk.cashu.redeem(draftTransaction: draftTransaction);

await for (final result in resultStream) {
if (result.state == ndk_entities.WalletTransactionState.completed) {
/// transcation done
print(result.completionMsg);
} else if (result.state == ndk_entities.WalletTransactionState.pending) {
/// pending
}
else if (result.state == ndk_entities.WalletTransactionState.failed) {
/// transcation done
print(result.completionMsg);
}
}


```

## spend

```dart
final spendResult = await ndk.cashu.initiateSpend(
mintUrl: "https://example.mint",
amount: 5,
unit: "sat",
memo: "spending example",
);

print("token to spend: ${spendResult.token.toV4TokenString()}");
print("transaction id: ${spendResult.transaction}");

/// listen to pending transactions List<CashuWalletTransaction>
await for (final transaction in ndk.cashu.pendingTransactions) {
print("latest transaction: $transaction");
}

/// listen to recent transactinos List<CashuWalletTransaction>
await for (final transaction in ndk.cashu.latestTransactions) {
print("latest transaction: $transaction");
}


```

## receive

```dart

final rcvResultStream = _ndk.cashu.receive(tokenString);

await for (final rcvResult in rcvResultStream) {
if (rcvResult.state == ndk_entities.WalletTransactionState.pending) {
/// pending
} else if (rcvResult.state ==
ndk_entities.WalletTransactionState.completed) {
/// completed
} else if (rcvResult.state ==
ndk_entities.WalletTransactionState.failed) {
/// failed
print(result.completionMsg);
}
}

```

!!!
All transactions are also available via `pendingTransactions` and `latestTransactions` streams.\
As well as in the `Wallets` usecase
!!!

## check balance

```dart
/// balances for all mints [List<CashuMintBalance>]
final balances = await ndk.cashu.getBalances();
print(balances);

/// balance for one mint and unit [int]
final singleBalance = await getBalanceMintUnit(
mintUrl: "https://example.mint",
unit: "sat",
);

/// stream of [List<CashuMintBalance>]
ndk.cashu.balances;

```

!!!
balances are also available via `Wallets` usecase
!!!


2 changes: 1 addition & 1 deletion doc/usecases/lists.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ icon: list-unordered
print("set not found");
return;
}
print("recived a set with ${myset.elements.length} elements");
print("receive a set with ${myset.elements.length} elements");

```

Expand Down
92 changes: 92 additions & 0 deletions doc/usecases/wallets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
icon: credit-card
---

[!badge variant="primary" text="high level"]

!!!danger experimental
DO NOT USE IN PRODUCTION!
!!!

## When to use

`Wallets` usecase manages combines multiple wallets (e.g., Cashu, NWC) within your application. It provides functionalities for creating, managing, and transacting.
If you build a transaction history or other reporting, you are advised to use this use case. You can switch or use multiple wallets and still have a unified transaction history.

## Examples

### balances

```dart
/// balances of all wallets, split into walletId and unit
/// returns Stream<List<WalletBalance>>
final balances = await ndk.wallets.combinedBalances;

/// get combined balance of all wallets in a specific unit
/// returns int
final combinedSat = ndk.wallets.getCombinedBalance("sat");
```

### transactions

```dart
/// get all pending transactions, fires immediately and on every change
/// returns Stream<List<WalletTransaction>>
final pendingTransactions = await ndk.wallets.combinedPendingTransactions;


/// get all recent transactions, fires immediately and on every change
/// returns Stream<List<WalletTransaction>>
final recentTransactions = await ndk.wallets.combinedRecentTransactions;

/// get all transactions, with pagination and filtering options
/// returns Future<List<WalletTransaction>>
final transactions = await ndk.wallets.combinedTransactions(
limit: 100, // optional
offset: 0, // optional, pagination
walletId: "mywalletId", // optional
unit: "sat", // optional
walletType: WalletType.cashu, // optional
);
```

### wallets

```dart

/// get all wallets
/// returns Stream<List<Wallet>>
final wallets = ndk.wallets.walletsStream;

/// get default wallet
/// returns Wallet?
final defaultWallet = ndk.wallets.defaultWallet;


await ndk.wallets.addWallet(myWallet);

setDefaultWallet("myWalletId");


await ndk.wallets.removeWalet("myWalletId");

/// get all wallets supporting a specific unit
/// returns List<Wallet>
final walletsSupportingSat = ndk.wallets.getWalletsForUnit("sat");

```

### actions

The wallets usecase provides unified actions that work across different wallet types. (WIP)

```dart

///! WIP none of the params are final
final zapResult = await ndk.wallets.zap(
pubkey: "pubkeyToZap",
amount: 10,
comment: "Hello World",
);

```
Loading