Skip to content

Commit e7e4a4d

Browse files
committed
Use lightning address for receiving
1 parent 764236d commit e7e4a4d

File tree

7 files changed

+69
-72
lines changed

7 files changed

+69
-72
lines changed

prisma/migrations/20250923052230_spark/migration.sql

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,11 @@ ALTER TYPE "WalletName" ADD VALUE 'SPARK'; COMMIT;
44
-- AlterEnum
55
ALTER TYPE "WalletProtocolName" ADD VALUE 'SPARK'; COMMIT;
66

7-
-- AlterEnum
8-
ALTER TYPE "WalletRecvProtocolName" ADD VALUE 'SPARK'; COMMIT;
9-
107
-- AlterEnum
118
ALTER TYPE "WalletSendProtocolName" ADD VALUE 'SPARK'; COMMIT;
129

1310
INSERT INTO "WalletTemplate" ("name", "sendProtocols", "recvProtocols")
14-
VALUES ('SPARK', '{SPARK}', '{SPARK}');
11+
VALUES ('SPARK', '{SPARK}', '{LN_ADDR}');
1512

1613
-- CreateTable
1714
CREATE TABLE "WalletSendSpark" (
@@ -24,35 +21,18 @@ CREATE TABLE "WalletSendSpark" (
2421
CONSTRAINT "WalletSendSpark_pkey" PRIMARY KEY ("id")
2522
);
2623

27-
-- CreateTable
28-
CREATE TABLE "WalletRecvSpark" (
29-
"id" SERIAL NOT NULL,
30-
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
31-
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
32-
"protocolId" INTEGER NOT NULL,
33-
"address" TEXT NOT NULL,
34-
35-
CONSTRAINT "WalletRecvSpark_pkey" PRIMARY KEY ("id")
36-
);
37-
3824
-- CreateIndex
3925
CREATE UNIQUE INDEX "WalletSendSpark_protocolId_key" ON "WalletSendSpark"("protocolId");
4026

4127
-- CreateIndex
4228
CREATE UNIQUE INDEX "WalletSendSpark_mnemonicVaultId_key" ON "WalletSendSpark"("mnemonicVaultId");
4329

44-
-- CreateIndex
45-
CREATE UNIQUE INDEX "WalletRecvSpark_protocolId_key" ON "WalletRecvSpark"("protocolId");
46-
4730
-- AddForeignKey
4831
ALTER TABLE "WalletSendSpark" ADD CONSTRAINT "WalletSendSpark_protocolId_fkey" FOREIGN KEY ("protocolId") REFERENCES "WalletProtocol"("id") ON DELETE CASCADE ON UPDATE CASCADE;
4932

5033
-- AddForeignKey
5134
ALTER TABLE "WalletSendSpark" ADD CONSTRAINT "WalletSendSpark_mnemonicVaultId_fkey" FOREIGN KEY ("mnemonicVaultId") REFERENCES "Vault"("id") ON DELETE RESTRICT ON UPDATE CASCADE;
5235

53-
-- AddForeignKey
54-
ALTER TABLE "WalletRecvSpark" ADD CONSTRAINT "WalletRecvSpark_protocolId_fkey" FOREIGN KEY ("protocolId") REFERENCES "WalletProtocol"("id") ON DELETE CASCADE ON UPDATE CASCADE;
55-
5636
CREATE TRIGGER wallet_to_jsonb
5737
AFTER INSERT OR UPDATE ON "WalletSendSpark"
5838
FOR EACH ROW

prisma/schema.prisma

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,6 @@ enum WalletRecvProtocolName {
12411241
CLN_REST
12421242
LND_GRPC
12431243
CLINK
1244-
SPARK
12451244
}
12461245

12471246
enum WalletProtocolStatus {
@@ -1346,7 +1345,6 @@ model WalletProtocol {
13461345
walletRecvCLNRest WalletRecvCLNRest?
13471346
walletRecvLNDGRPC WalletRecvLNDGRPC?
13481347
walletRecvClink WalletRecvClink?
1349-
walletRecvSpark WalletRecvSpark?
13501348
13511349
@@unique(name: "WalletProtocol_walletId_send_name_key", [walletId, send, name])
13521350
@@index([walletId])
@@ -1530,12 +1528,3 @@ model WalletRecvClink {
15301528
protocol WalletProtocol @relation(fields: [protocolId], references: [id], onDelete: Cascade)
15311529
noffer String
15321530
}
1533-
1534-
model WalletRecvSpark {
1535-
id Int @id @default(autoincrement())
1536-
createdAt DateTime @default(now()) @map("created_at")
1537-
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
1538-
protocolId Int @unique
1539-
protocol WalletProtocol @relation(fields: [protocolId], references: [id], onDelete: Cascade)
1540-
address String
1541-
}

wallets/client/components/form/hooks.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,11 @@ function useProtocolFormState (protocol) {
8484

8585
export function useProtocolForm (protocol) {
8686
const [formState, setFormState] = useProtocolFormState(protocol)
87+
8788
const [complementaryFormState] = useProtocolFormState({ name: protocol.name, send: !protocol.send })
8889
const [nwcSendFormState] = useProtocolFormState({ name: 'NWC', send: true })
90+
const [sparkSendFormState] = useProtocolFormState({ name: 'SPARK', send: true })
91+
8992
const wallet = useWallet()
9093
const lud16Domain = walletLud16Domain(wallet.name)
9194
const fields = protocolFields(protocol)
@@ -103,10 +106,13 @@ export function useProtocolForm (protocol) {
103106
}
104107

105108
if (protocol.name === 'LN_ADDR' && field.name === 'address' && lud16Domain) {
106-
// automatically set lightning addresses from NWC urls if lud16 parameter is present
107109
if (nwcSendFormState?.config?.url) {
110+
// automatically set lightning addresses from NWC urls if lud16 parameter is present ...
108111
const { lud16 } = parseNwcUrl(nwcSendFormState.config.url)
109112
if (lud16?.split('@')[1] === lud16Domain) value = lud16
113+
} else if (sparkSendFormState?.config?.username) {
114+
// ... or from spark usernames
115+
value = `${sparkSendFormState.config.username}@${lud16Domain}`
110116
}
111117
// remove domain part since we will append it automatically if lud16Domain is set
112118
if (lud16Domain && value) {

wallets/client/protocols/spark.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import init, { defaultConfig, connect } from '@breeztech/breez-sdk-spark'
2+
import { WalletValidationError } from '@/wallets/client/errors'
3+
import { getUsername } from '@/wallets/lib/protocols/spark'
24

35
export const name = 'SPARK'
46

@@ -23,5 +25,22 @@ export async function testSendPayment ({ mnemonic }, { signal }) {
2325

2426
await sdk.getInfo({})
2527

28+
const { paymentRequest: sparkAddress } = await sdk.receivePayment({
29+
// this will always return the same spark address for the same seed
30+
paymentMethod: { type: 'sparkAddress' }
31+
})
32+
33+
// check and register lightning address here so we can test it in the next step
34+
// https://sdk-doc-spark.breez.technology/guide/receive_lnurl_pay.html
35+
const username = getUsername(sparkAddress)
36+
const available = await sdk.checkLightningAddressAvailable({ username })
37+
if (!available) {
38+
// TODO: better error message for user? but this should never happen with randomly generated mnemonics, right?
39+
throw new WalletValidationError('lightning address unavailable')
40+
}
41+
await sdk.registerLightningAddress({ username, description: 'Running Spark SDK' })
42+
2643
sdk.disconnect()
44+
45+
return { username }
2746
}

wallets/lib/protocols/index.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import phoenixdSuite from './phoenixd'
88
import blinkSuite from './blink'
99
import webln from './webln'
1010
import clinkSuite from './clink'
11-
import sparkSuite from './spark'
11+
import spark from './spark'
1212

1313
/**
1414
* Protocol names as used in the database
@@ -51,5 +51,5 @@ export default [
5151
...blinkSuite,
5252
webln,
5353
...clinkSuite,
54-
...sparkSuite
54+
spark
5555
]

wallets/lib/protocols/spark.js

Lines changed: 36 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,43 @@
1-
import { bip39Validator, externalLightningAddressValidator } from '@/wallets/lib/validate'
1+
import { bip39Validator } from '@/wallets/lib/validate'
22
import { generateRandomPassphrase } from '@/wallets/lib/crypto'
3+
import { bech32m } from 'bech32'
34

45
// Spark
56
// https://github.com/breez/spark-sdk
67
// https://sdk-doc-spark.breez.technology/
78
// https://breez.github.io/spark-sdk/breez_sdk_spark/
89

9-
export default [
10-
{
11-
name: 'SPARK',
12-
send: true,
13-
displayName: 'Spark',
14-
fields: [
15-
{
16-
name: 'mnemonic',
17-
label: 'mnemonic',
18-
type: 'password',
19-
required: true,
20-
validate: bip39Validator(),
21-
encrypt: true,
22-
initial: generateRandomPassphrase,
23-
disabled: true
24-
}
25-
],
26-
relationName: 'walletSendSpark'
27-
},
28-
{
29-
name: 'SPARK',
30-
send: false,
31-
displayName: 'Spark',
32-
fields: [
33-
{
34-
name: 'address',
35-
label: 'address',
36-
type: 'text',
37-
required: true,
38-
validate: externalLightningAddressValidator
39-
}
40-
],
41-
relationName: 'walletRecvSpark'
42-
}
43-
]
10+
export default {
11+
name: 'SPARK',
12+
send: true,
13+
displayName: 'Spark',
14+
fields: [
15+
{
16+
name: 'mnemonic',
17+
label: 'mnemonic',
18+
type: 'password',
19+
required: true,
20+
validate: bip39Validator(),
21+
encrypt: true,
22+
initial: generateRandomPassphrase,
23+
disabled: true
24+
},
25+
{
26+
name: 'username'
27+
}
28+
],
29+
relationName: 'walletSendSpark'
30+
}
31+
32+
function getIdentityPublicKey (sparkAddress) {
33+
const decoded = bech32m.decode(sparkAddress)
34+
const pubkey = Buffer.from(bech32m.fromWords(decoded.words)).toString('hex').slice(4)
35+
return pubkey
36+
}
37+
38+
export function getUsername (sparkAddress) {
39+
const identityPublicKey = getIdentityPublicKey(sparkAddress)
40+
// max lnurl username length is 64 characters
41+
// https://github.com/breez/spark-sdk/blob/71b8cb097d2bc846be427599b1bf44eae897786f/crates/breez-sdk/lnurl/src/routes.rs#L326
42+
return identityPublicKey.slice(0, 64)
43+
}

wallets/lib/wallets.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,9 @@
171171
},
172172
{
173173
"name": "SPARK",
174-
"displayName": "Spark"
174+
"displayName": "Spark",
175+
"url": {
176+
"lud16Domain": "breez.tips"
177+
}
175178
}
176179
]

0 commit comments

Comments
 (0)