Skip to content

Conversation

@ekzyis
Copy link
Member

@ekzyis ekzyis commented Sep 23, 2025

Description

close #2538

TODO

  • ✅ Spark send via lightning
  • ✅ Spark receive via lightning
  • 🪲 fix "Channel has been shut down" thrown by wallet.createLightningInvoice() wasn't an issue with the SDK but with grpc_proxy
  • 🚧 'next' button of send form does not always pulse to indicate loading unrelated to this PR
  • ✅ test error handling with insufficient funds
  • ✅ add privacy banner

TODOs out-of-scope of this PR

  • make sure user created backup of passphrase
  • withdrawals to L1 (cooperative or unilateral)

Privacy

We could deduct a small randomized amount from the amount we forward, but I think the anonymity set on the statechain will be too small (for now?) anyway, especially for small forwarded amounts, and this will be terrible UX and requires explaining it to the user.

Also, it's a bandaid and it should be fixed on the protocol level by not reusing addresses or buildonspark/sip#2.

What works in our favor here is that we don't tell senders which wallet a receiver is using.

So maybe all we can do here is to warn users that their balance is basically public.

Screenshot

localhost_3000_wallets_10_step=send(iPhone SE)

Checklist

Are your changes backward compatible? Please answer below:

yes

On a scale of 1-10 how well and how have you QA'd this change and any features it might affect? Please answer below:

7. I tested with Phoenix on mainnet and this patch:

diff --git a/wallets/client/protocols/spark.js b/wallets/client/protocols/spark.js
index be833364..3f7f313e 100644
--- a/wallets/client/protocols/spark.js
+++ b/wallets/client/protocols/spark.js
@@ -6,6 +6,7 @@ export const name = 'SPARK'
 const SPARK_PAYMENT_STATUS_POLL_INTERVAL_MS = 250

 export async function sendPayment (bolt11, { mnemonic }, { signal }) {
+  bolt11 = '<insert mainnet invoice here>'
   return await withSparkWallet(
     mnemonic,
     async wallet => {
diff --git a/wallets/server/protocols/spark.js b/wallets/server/protocols/spark.js
index 5c690972..40ab846f 100644
--- a/wallets/server/protocols/spark.js
+++ b/wallets/server/protocols/spark.js
@@ -11,11 +11,13 @@ export async function createInvoice ({ msats, description }, { identityPublicKey
         memo: description,
         receiverIdentityPubkey: identityPublicKey
       })
+      // pay this mainnet invoice for 100 sats with any lightning wallet
+      console.log('bolt11', bolt11)
       return bolt11
     }
   )
 }

 export async function testCreateInvoice ({ identityPublicKey }, { signal }) {
-  return await createInvoice({ msats: 1000, description: 'SN test invoice' }, { identityPublicKey }, { signal })
+  return await createInvoice({ msats: 100_000, description: 'SN test invoice' }, { identityPublicKey }, { signal })
 }

For frontend changes: Tested on mobile, light and dark mode? Please answer below:

n/a

Did you introduce any new environment variables? If so, call them out explicitly here:

no

Did you use AI for this? If so, how much did it assist you?

no


Note

Add Spark wallet support (send via mnemonic, receive via identity public key) with schema/models, client/server integration, validation, and UI warning.

  • Backend:
    • GraphQL: add upsertWalletSendSpark, upsertWalletRecvSpark, testWalletRecvSpark; extend WalletProtocolConfig with WalletSendSpark/WalletRecvSpark types.
    • Prisma/Migration: add SPARK to enums; create tables WalletSendSpark (mnemonic vault) and WalletRecvSpark (identityPublicKey); insert WalletTemplate for SPARK; map resolve types.
    • Server Protocol: implement SPARK createInvoice/test using Spark SDK and random wallet.
  • Frontend:
    • Protocols: implement SPARK send (poll status, return preimage) and test (returns identityPublicKey).
    • Forms/UI: support field initial; prefill/propagate SPARK identityPublicKey; add wallet warning banner; include Spark in selector.
    • GQL Fragments/Queries: add SPARK upsert/test ops; include encryptedMnemonic and identityPublicKey in wallet fragments.
  • Crypto/Validation:
    • Switch mnemonic generation/validation to bip39; add identityPublicKey validator.
  • Assets/Config:
    • Add spark.svg (and *-dark.svg) and entry in wallets.json with warning.
    • Add deps: @buildonspark/spark-sdk, bip39.

Written by Cursor Bugbot for commit 0ba7b8f. This will update automatically on new commits. Configure here.

@ekzyis ekzyis added feature new product features that weren't there before wallets labels Sep 23, 2025
@ekzyis ekzyis marked this pull request as draft September 23, 2025 08:18
@socket-security
Copy link

socket-security bot commented Sep 23, 2025

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Added@​buildonspark/​spark-sdk@​0.4.29010080100100
Addedbip39@​3.1.010010010081100
Updatedacorn@​8.12.1 ⏵ 8.15.010010010083100

View full report

@ekzyis
Copy link
Member Author

ekzyis commented Sep 28, 2025

Hey @dangeross, since you mentioned to keep you updated, I hope you can help me with this:

I read in the TG group that the public beta is next week.

Is this related to the error I'm seeing in this video when calling sendPayment:

Uncaught Error: missing field useSpark at line 1 column 923

2025-09-28.23-23-39.mp4

In the video, I am using a regtest invoice but it also happens with a mainnet invoice.

The relevant code is in wallets/client/protocols/spark.js of this PR.

@dangeross
Copy link

Uncaught Error: missing field useSpark at line 1 column 923

Hey. It changed to preferSpark and doesn't error if there is no spark address included if you enable it

@ekzyis
Copy link
Member Author

ekzyis commented Sep 29, 2025

But I am using preferSpark. I guess this means I need to update the SDK?

@dangeross
Copy link

dangeross commented Sep 29, 2025

Yep, I think only the latest includes it

@ekzyis
Copy link
Member Author

ekzyis commented Sep 29, 2025

Ok, cool, this worked! I updated to v0.2.1.

I also had to change mnemonic to seed because of breez/spark-sdk#271 but I saw you also updated the documentation here.

I now get this expected error for regtest invoices:

SparkSdkError: Service error: validation error: Invoice network does not match

When I try a mainnet invoice, sendPayment() throws this:

The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type object

but it actually succeeds to pay the invoice!

@dangeross
Copy link

When I try a mainnet invoice, sendPayment() throws this:

The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type object

Can you raise this in Telegram if this is still an issue?

@ekzyis ekzyis force-pushed the spark branch 2 times, most recently from 3afe69a to e0ea694 Compare October 3, 2025 17:45
Base automatically changed from wallets to master October 11, 2025 21:49
@ekzyis ekzyis force-pushed the spark branch 4 times, most recently from 8e61892 to e13ecf3 Compare October 22, 2025 11:31
@ekzyis ekzyis force-pushed the spark branch 2 times, most recently from 02eab51 to 8066f79 Compare October 28, 2025 20:52
@ekzyis ekzyis changed the title Breez Wallet (Spark) Spark Wallet Oct 28, 2025
@ekzyis ekzyis force-pushed the spark branch 3 times, most recently from 5c9bb26 to 7697875 Compare October 29, 2025 21:05
@ekzyis ekzyis marked this pull request as ready for review October 29, 2025 21:06
cursor[bot]

This comment was marked as outdated.

@ekzyis ekzyis force-pushed the spark branch 2 times, most recently from b2bc871 to 8ff4785 Compare October 29, 2025 21:13
@ekzyis ekzyis requested a review from huumn October 29, 2025 21:19
@ekzyis ekzyis marked this pull request as draft October 30, 2025 07:00
@ekzyis ekzyis marked this pull request as ready for review October 30, 2025 20:02
@huumn
Copy link
Member

huumn commented Oct 31, 2025

add privacy banner

We proxy all invoices so it should be relatively cool ... timing analysis is still possible though, but that's harder to communicate.

afaict so long as they aren't importing their seed into another wallet, and assuming someone does timing analysis and our feature set remains the same, the privacy loss they might suffer is limited to:

  1. someone (the public) knowing how much/when they receive payments that aren't zaps (rewards, lnurl, etc) ... folks already know how much they receive for zaps
  2. someone (the public) knowing how much/when they are spending sats on SN
  3. someone (the public) knowing if they are using an alt with the same Spark wallet
  4. the API endpoint (Spark) knowing the IP address, etc, of the stacker issuing a payment

I might be missing something but it'd be nice to enumerate all these. (1) and (2) probably can't be fixed by us. (4) we might be able to proxy. (3) we can outright prevent if need be.

@huumn
Copy link
Member

huumn commented Oct 31, 2025

To give stackers the option to unilaterally exit we might need to store additional data: https://docs.spark.money/learn/withdrawals#unilateral-exit

The user broadcasts the branch transaction that was signed during the deposit process.

Then step 3:

the user broadcasts the exit transaction that was signed during the deposit or most recent transfer.

To provide unilateral exits, it sounds like we need to store this additional state after each payment.

@ekzyis
Copy link
Member Author

ekzyis commented Oct 31, 2025

I might be missing something but it'd be nice to enumerate all these.

Sounds pretty complete to me, but I will think more about it.

Fwiw, I just verified that anyone can indeed find out my balance using my Blitz lightning address ([email protected]) as described here. I sent 444 sats to it and then waited (might take a few minutes) until the tx shows up here with my Spark address next to it.


To give stackers the option to unilaterally exit we might need to store additional data: https://docs.spark.money/learn/withdrawals#unilateral-exit

Ohhh, good catch

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature new product features that weren't there before wallets

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Breez wallet (Spark)

4 participants