javascript-solid-server 0.0.134 → 0.0.136
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +7 -1
- package/docs/payments.md +35 -13
- package/package.json +1 -1
- package/src/handlers/pay.js +43 -0
- package/src/notifications/websocket.js +11 -4
|
@@ -343,7 +343,13 @@
|
|
|
343
343
|
"Bash(sed -i 's/\"currency\":\"sats\"/\"currency\":\"tbtc4\"/g' /home/melvin/articles/solid-402.html /home/melvin/articles/solid-pay.html /home/melvin/articles/solid-balance.html)",
|
|
344
344
|
"WebFetch(domain:lists.w3.org)",
|
|
345
345
|
"Bash(git:*)",
|
|
346
|
-
"Bash(npm publish:*)"
|
|
346
|
+
"Bash(npm publish:*)",
|
|
347
|
+
"WebFetch(domain:git-mark.com)",
|
|
348
|
+
"WebFetch(domain:w3id.org)",
|
|
349
|
+
"WebFetch(domain:webledgers.github.io)",
|
|
350
|
+
"Bash(\\\\\"git config:*)",
|
|
351
|
+
"Read(//usr/local/lib/node_modules/gitmark-test/**)",
|
|
352
|
+
"WebFetch(domain:nip98.com)"
|
|
347
353
|
]
|
|
348
354
|
}
|
|
349
355
|
}
|
package/docs/payments.md
CHANGED
|
@@ -73,8 +73,10 @@ jss start --pay --pay-cost 10 --pay-address your-address --pay-token PODS --pay-
|
|
|
73
73
|
| Method | Path | Description |
|
|
74
74
|
|--------|------|-------------|
|
|
75
75
|
| GET | `/pay/.info` | Public: cost, token info, chains, pool |
|
|
76
|
-
| GET | `/pay/.
|
|
77
|
-
|
|
|
76
|
+
| GET | `/pay/.address` | Public: pod's taproot deposit address (optional `?user=did:nostr:...` for per-user address) |
|
|
77
|
+
| GET | `/pay/.balance` | Check your balance (NIP-98 auth) — also auto-detects new deposits |
|
|
78
|
+
| POST | `/pay/.deposit` | Deposit sats via TXO URI, claim `{txid, vout, chain}`, or MRC20 state proof |
|
|
79
|
+
| POST | `/pay/.withdraw-sats` | Withdraw sats as a TXO voucher URI |
|
|
78
80
|
| POST | `/pay/.buy` | Buy tokens with sat balance (requires `--pay-token`) |
|
|
79
81
|
| POST | `/pay/.withdraw` | Withdraw balance as portable tokens (requires `--pay-token`) |
|
|
80
82
|
| GET | `/pay/.offers` | List open sell orders (secondary market) |
|
|
@@ -86,41 +88,61 @@ jss start --pay --pay-cost 10 --pay-address your-address --pay-token PODS --pay-
|
|
|
86
88
|
|
|
87
89
|
### How It Works
|
|
88
90
|
|
|
89
|
-
1.
|
|
90
|
-
2.
|
|
91
|
-
3.
|
|
91
|
+
1. Get your deposit address: `GET /pay/.address?user=did:nostr:YOUR_PUBKEY`
|
|
92
|
+
2. Send sats to that address from any Bitcoin wallet
|
|
93
|
+
3. Check balance at `/pay/.balance` — deposits are auto-detected
|
|
92
94
|
4. Access paid resources — each request deducts the configured cost
|
|
93
|
-
5.
|
|
94
|
-
6.
|
|
95
|
+
5. Withdraw sats as a portable voucher: `POST /pay/.withdraw-sats`
|
|
96
|
+
6. Optionally buy tokens (`/pay/.buy`) or withdraw as portable tokens (`/pay/.withdraw`)
|
|
97
|
+
7. Balance tracked in a [Web Ledger](https://webledgers.org/) at `/.well-known/webledgers/webledgers.json`
|
|
98
|
+
|
|
99
|
+
Each user gets a unique taproot deposit address derived from the pod's master key + their identity. The pod auto-detects deposits by scanning the mempool API when you check your balance.
|
|
95
100
|
|
|
96
101
|
### Example
|
|
97
102
|
|
|
98
103
|
```bash
|
|
99
|
-
#
|
|
104
|
+
# Get your deposit address
|
|
105
|
+
curl http://localhost:4443/pay/.address?chain=tbtc4&user=did:nostr:YOUR_PUBKEY
|
|
106
|
+
# → {"address": "tb1p...", "chain": "tbtc4", "pubkey": "02..."}
|
|
107
|
+
|
|
108
|
+
# Send sats to that address, then check balance (auto-detects deposits)
|
|
100
109
|
curl -H "Authorization: Nostr <base64-event>" http://localhost:4443/pay/.balance
|
|
101
110
|
|
|
102
|
-
#
|
|
111
|
+
# Or deposit manually with a TXO voucher URI
|
|
103
112
|
curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
104
113
|
http://localhost:4443/pay/.deposit \
|
|
105
|
-
-d "txid:vout"
|
|
114
|
+
-d "txo:tbtc4:txid:vout?amount=X&key=Y"
|
|
115
|
+
|
|
116
|
+
# Or claim a deposit to the pod's address
|
|
117
|
+
curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
118
|
+
-H "Content-Type: application/json" \
|
|
119
|
+
http://localhost:4443/pay/.deposit \
|
|
120
|
+
-d '{"txid": "abc...", "vout": 0, "chain": "tbtc4"}'
|
|
106
121
|
|
|
107
122
|
# Access paid resource
|
|
108
123
|
curl -H "Authorization: Nostr <base64-event>" http://localhost:4443/pay/my-resource
|
|
109
124
|
|
|
125
|
+
# Withdraw sats as a portable TXO voucher
|
|
126
|
+
curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
127
|
+
-H "Content-Type: application/json" \
|
|
128
|
+
http://localhost:4443/pay/.withdraw-sats \
|
|
129
|
+
-d '{"amount": 10000, "chain": "tbtc4"}'
|
|
130
|
+
# → {"voucher": "txo:tbtc4:txid:0?amount=10000&key=...", ...}
|
|
131
|
+
|
|
110
132
|
# Buy tokens with sat balance
|
|
111
133
|
curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
112
134
|
-H "Content-Type: application/json" \
|
|
113
135
|
http://localhost:4443/pay/.buy \
|
|
114
|
-
-d '{"amount": 100}'
|
|
136
|
+
-d '{"amount": 100, "currency": "tbtc4"}'
|
|
115
137
|
|
|
116
138
|
# Withdraw entire balance as portable tokens
|
|
117
139
|
curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
118
140
|
-H "Content-Type: application/json" \
|
|
119
141
|
http://localhost:4443/pay/.withdraw \
|
|
120
|
-
-d '{"all": true}'
|
|
142
|
+
-d '{"all": true, "currency": "tbtc4"}'
|
|
121
143
|
```
|
|
122
144
|
|
|
123
|
-
|
|
145
|
+
Three deposit methods: (1) send to your per-user address and check balance (auto-detected), (2) TXO voucher URI with private key, (3) claim a txid after sending to the pod's address. The `X-Balance`, `X-Cost`, and `X-Pay-Currency` headers are returned on successful paid requests. Buy and withdraw return portable MRC20 proofs with Bitcoin anchor data for independent verification.
|
|
124
146
|
|
|
125
147
|
### Secondary Market
|
|
126
148
|
|
package/package.json
CHANGED
package/src/handlers/pay.js
CHANGED
|
@@ -311,6 +311,49 @@ export function createPayHandler(options = {}) {
|
|
|
311
311
|
return reply.code(401).send({ error: 'NIP-98 authentication required' });
|
|
312
312
|
}
|
|
313
313
|
const didUri = pubkeyToDidNostr(pubkey);
|
|
314
|
+
|
|
315
|
+
// Auto-detect deposits to user's tweaked address (Phase 3)
|
|
316
|
+
if (payChains) {
|
|
317
|
+
try {
|
|
318
|
+
const kp = await loadOrCreateKeypair();
|
|
319
|
+
const utxos = await loadUtxos();
|
|
320
|
+
const ledger = await readLedger();
|
|
321
|
+
let credited = 0;
|
|
322
|
+
|
|
323
|
+
for (const chainId of payChains) {
|
|
324
|
+
const chain = CHAIN_REGISTRY[chainId];
|
|
325
|
+
const network = chainId === 'btc' ? 'mainnet' : (chainId === 'tbtc3' ? 'testnet' : 'testnet4');
|
|
326
|
+
const userAddr = btAddress(kp.pubkey, [didUri], network);
|
|
327
|
+
|
|
328
|
+
const resp = await fetch(`${chain.explorer}/address/${userAddr}/utxo`);
|
|
329
|
+
if (!resp.ok) continue;
|
|
330
|
+
const addrUtxos = await resp.json();
|
|
331
|
+
|
|
332
|
+
for (const u of addrUtxos) {
|
|
333
|
+
if (utxos.find(x => x.txid === u.txid && x.vout === u.vout)) continue;
|
|
334
|
+
// New UTXO — fetch tx for scriptpubkey, then auto-credit
|
|
335
|
+
let scriptpubkey = '';
|
|
336
|
+
try {
|
|
337
|
+
const txResp = await fetch(`${chain.explorer}/tx/${u.txid}`);
|
|
338
|
+
if (txResp.ok) {
|
|
339
|
+
const txData = await txResp.json();
|
|
340
|
+
scriptpubkey = txData.vout?.[u.vout]?.scriptpubkey || '';
|
|
341
|
+
}
|
|
342
|
+
} catch { /* best effort */ }
|
|
343
|
+
const currency = chain.unit;
|
|
344
|
+
credit(ledger, didUri, u.value, currency);
|
|
345
|
+
utxos.push({ txid: u.txid, vout: u.vout, amount: u.value, scriptpubkey, chain: chainId, tweak: didUri, spent: false });
|
|
346
|
+
credited += u.value;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (credited > 0) {
|
|
351
|
+
await writeLedger(ledger);
|
|
352
|
+
await saveUtxos(utxos);
|
|
353
|
+
}
|
|
354
|
+
} catch { /* scan failure is non-fatal */ }
|
|
355
|
+
}
|
|
356
|
+
|
|
314
357
|
const ledger = await readLedger();
|
|
315
358
|
const response = {
|
|
316
359
|
did: didUri,
|
|
@@ -204,11 +204,18 @@ export function broadcast(url) {
|
|
|
204
204
|
// Notify direct subscribers
|
|
205
205
|
notifySubscribers(url);
|
|
206
206
|
|
|
207
|
-
//
|
|
208
|
-
//
|
|
209
|
-
|
|
210
|
-
|
|
207
|
+
// Walk up all ancestor containers so subscribing to a root
|
|
208
|
+
// catches changes in nested paths (e.g. /db/mydata/ catches /db/mydata/issues/1)
|
|
209
|
+
// Stop at the origin root to avoid climbing past the hostname
|
|
210
|
+
let originRoot;
|
|
211
|
+
try { originRoot = new URL(url).origin + '/'; } catch (e) { return; }
|
|
212
|
+
|
|
213
|
+
let currentUrl = url;
|
|
214
|
+
let containerUrl = getParentContainer(currentUrl);
|
|
215
|
+
while (containerUrl && containerUrl !== currentUrl && containerUrl.length >= originRoot.length) {
|
|
211
216
|
notifySubscribers(containerUrl);
|
|
217
|
+
currentUrl = containerUrl;
|
|
218
|
+
containerUrl = getParentContainer(currentUrl);
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
|