javascript-solid-server 0.0.105 → 0.0.107
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/PAY.md +364 -0
- package/README.md +40 -1
- package/bin/jss.js +2 -0
- package/package.json +2 -1
- package/src/config.js +2 -0
- package/src/handlers/pay.js +495 -9
- package/src/server.js +2 -1
- package/src/token.js +8 -8
- package/src/webledger.js +43 -15
- package/test/pay.test.js +486 -0
- package/test/webledger.test.js +62 -0
package/PAY.md
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
# PAY.md — HTTP 402 Payment System
|
|
2
|
+
|
|
3
|
+
## What This Is
|
|
4
|
+
|
|
5
|
+
JSS has a built-in payment system. Resources under `/pay/*` cost satoshis to access. Users authenticate with a Nostr key, deposit sats, and spend them on API requests. Optionally, the pod mints its own token (MRC20 on Bitcoin) that users can buy, sell, and trade.
|
|
6
|
+
|
|
7
|
+
## Architecture
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
User (Nostr keypair)
|
|
11
|
+
│
|
|
12
|
+
├── POST /pay/.deposit → credit sat balance (multi-chain: txo:tbtc3:, txo:tbtc4:, etc.)
|
|
13
|
+
├── GET /pay/.balance → check balance (includes per-chain balances)
|
|
14
|
+
├── GET /pay/* → spend 1 sat, get resource
|
|
15
|
+
├── POST /pay/.buy → spend sats, get tokens (Bitcoin TX)
|
|
16
|
+
├── POST /pay/.withdraw → spend balance, get tokens back
|
|
17
|
+
├── POST /pay/.sell → list tokens for sale
|
|
18
|
+
├── POST /pay/.swap → buy someone's sell order
|
|
19
|
+
├── GET /pay/.pool → AMM pool state (multi-chain)
|
|
20
|
+
└── POST /pay/.pool → AMM: swap, add-liquidity, remove-liquidity
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
All state lives in two places:
|
|
24
|
+
- **Webledger** — `/.well-known/webledgers/webledgers.json` — sat balances per `did:nostr:<pubkey>`
|
|
25
|
+
- **Token trail** — `/.well-known/token/<ticker>.json` — MRC20 state chain anchored to Bitcoin
|
|
26
|
+
|
|
27
|
+
## Authentication
|
|
28
|
+
|
|
29
|
+
Every request to `/pay/*` (except `.info` and `.offers`) requires a NIP-98 auth header:
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Authorization: Nostr <base64-encoded-signed-event>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
The event is kind 27235 with tags `["u", "<url>"]` and `["method", "<METHOD>"]`, signed with the user's Nostr private key. The server extracts the pubkey and maps it to `did:nostr:<pubkey>` for balance lookup.
|
|
36
|
+
|
|
37
|
+
## Endpoints
|
|
38
|
+
|
|
39
|
+
### GET /pay/.info
|
|
40
|
+
**Public. No auth required.**
|
|
41
|
+
|
|
42
|
+
Returns pod payment configuration.
|
|
43
|
+
|
|
44
|
+
Response:
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"cost": 1,
|
|
48
|
+
"unit": "sat",
|
|
49
|
+
"deposit": "/pay/.deposit",
|
|
50
|
+
"balance": "/pay/.balance",
|
|
51
|
+
"token": {
|
|
52
|
+
"ticker": "PODS",
|
|
53
|
+
"rate": 10,
|
|
54
|
+
"buy": "/pay/.buy",
|
|
55
|
+
"withdraw": "/pay/.withdraw",
|
|
56
|
+
"supply": 10000,
|
|
57
|
+
"issuer": "025e60b6..."
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The `token` field is only present when `--pay-token` is configured. `rate` is sats per token.
|
|
63
|
+
|
|
64
|
+
When `--pay-chains` is configured, the response also includes:
|
|
65
|
+
```json
|
|
66
|
+
{
|
|
67
|
+
"chains": [
|
|
68
|
+
{ "id": "tbtc3", "unit": "tbtc3", "name": "Bitcoin Testnet3" },
|
|
69
|
+
{ "id": "tbtc4", "unit": "tbtc4", "name": "Bitcoin Testnet4" }
|
|
70
|
+
],
|
|
71
|
+
"pool": "/pay/.pool"
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### GET /pay/.balance
|
|
76
|
+
**Requires NIP-98 auth.**
|
|
77
|
+
|
|
78
|
+
Returns the caller's sat balance.
|
|
79
|
+
|
|
80
|
+
Response:
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"did": "did:nostr:4fa459ad...",
|
|
84
|
+
"balance": 1041588,
|
|
85
|
+
"cost": 1,
|
|
86
|
+
"unit": "sat"
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### POST /pay/.deposit
|
|
91
|
+
**Requires NIP-98 auth.**
|
|
92
|
+
|
|
93
|
+
Credits the caller's balance. Two deposit types:
|
|
94
|
+
|
|
95
|
+
**Sats (TXO URI):**
|
|
96
|
+
```
|
|
97
|
+
POST /pay/.deposit
|
|
98
|
+
Content-Type: text/plain
|
|
99
|
+
Body: <txid>:<vout>
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
The server calls the mempool API to verify the UTXO exists and reads its value. The sat amount is credited to the caller's webledger balance.
|
|
103
|
+
|
|
104
|
+
**MRC20 tokens (state proof):**
|
|
105
|
+
```json
|
|
106
|
+
POST /pay/.deposit
|
|
107
|
+
Content-Type: application/json
|
|
108
|
+
{
|
|
109
|
+
"type": "mrc20",
|
|
110
|
+
"state": { ... },
|
|
111
|
+
"prevState": { ... },
|
|
112
|
+
"anchor": {
|
|
113
|
+
"pubkey": "<issuer-compressed-pubkey>",
|
|
114
|
+
"stateStrings": ["<jcs-of-each-state>"],
|
|
115
|
+
"network": "testnet4"
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The server verifies: state chain integrity (`state.prev == SHA256(JCS(prevState))`), transfer to the pod's `payAddress`, and optionally anchor verification (derives expected taproot address from pubkey + state chain, checks mempool for UTXO). Replay protection rejects duplicate state hashes.
|
|
121
|
+
|
|
122
|
+
### POST /pay/.buy
|
|
123
|
+
**Requires NIP-98 auth. Requires `--pay-token` configured.**
|
|
124
|
+
|
|
125
|
+
Buy tokens from the pod at the configured `payRate` (sats per token).
|
|
126
|
+
|
|
127
|
+
Request (pick one):
|
|
128
|
+
```json
|
|
129
|
+
{ "amount": 100 } // buy 100 tokens
|
|
130
|
+
{ "sats": 1000 } // spend 1000 sats worth
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
The server:
|
|
134
|
+
1. Checks caller's sat balance >= cost
|
|
135
|
+
2. Loads the pod's MRC20 token trail
|
|
136
|
+
3. Calls `transferToken()` — creates a new MRC20 state transferring tokens to the buyer's pubkey, derives a new taproot address via BIP-341 key chaining, builds and broadcasts a Bitcoin transaction
|
|
137
|
+
4. Debits sats from caller's webledger balance
|
|
138
|
+
|
|
139
|
+
Response includes a portable proof:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"bought": 100,
|
|
143
|
+
"ticker": "PODS",
|
|
144
|
+
"cost": 1000,
|
|
145
|
+
"rate": 10,
|
|
146
|
+
"balance": 1040588,
|
|
147
|
+
"txid": "c3183f41...",
|
|
148
|
+
"proof": {
|
|
149
|
+
"state": { ... },
|
|
150
|
+
"prevState": { ... },
|
|
151
|
+
"anchor": {
|
|
152
|
+
"pubkey": "025e60b6...",
|
|
153
|
+
"stateStrings": ["<jcs>", "<jcs>"],
|
|
154
|
+
"network": "testnet4"
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
The proof is independently verifiable: anyone can derive the expected taproot address from the pubkey + stateStrings and check the Bitcoin UTXO.
|
|
161
|
+
|
|
162
|
+
### POST /pay/.withdraw
|
|
163
|
+
**Requires NIP-98 auth. Requires `--pay-token` configured.**
|
|
164
|
+
|
|
165
|
+
Convert sat balance back to portable tokens. Same mechanism as buy.
|
|
166
|
+
|
|
167
|
+
Request (pick one):
|
|
168
|
+
```json
|
|
169
|
+
{ "tokens": 50 } // withdraw 50 tokens
|
|
170
|
+
{ "sats": 500 } // withdraw 500 sats worth
|
|
171
|
+
{ "all": true } // drain entire balance
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Response is identical to buy, with `"withdrawn"` instead of `"bought"`.
|
|
175
|
+
|
|
176
|
+
### GET /pay/.offers
|
|
177
|
+
**Public. No auth required.**
|
|
178
|
+
|
|
179
|
+
Returns open sell orders from the secondary market.
|
|
180
|
+
|
|
181
|
+
Response:
|
|
182
|
+
```json
|
|
183
|
+
[
|
|
184
|
+
{
|
|
185
|
+
"id": "uuid",
|
|
186
|
+
"seller": "<pubkey>",
|
|
187
|
+
"ticker": "PODS",
|
|
188
|
+
"amount": 100,
|
|
189
|
+
"price": 1500,
|
|
190
|
+
"rate": 15,
|
|
191
|
+
"status": "pending",
|
|
192
|
+
"created": 1773259044534
|
|
193
|
+
}
|
|
194
|
+
]
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### POST /pay/.sell
|
|
198
|
+
**Requires NIP-98 auth. Requires `--pay-token` configured.**
|
|
199
|
+
|
|
200
|
+
Create a sell order. The seller must have tokens on the pod's MRC20 trail.
|
|
201
|
+
|
|
202
|
+
Request:
|
|
203
|
+
```json
|
|
204
|
+
{
|
|
205
|
+
"amount": 100,
|
|
206
|
+
"price": 1500
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`amount` = tokens to sell, `price` = total sats asked. The server verifies the seller's token balance on the trail before creating the order.
|
|
211
|
+
|
|
212
|
+
### POST /pay/.swap
|
|
213
|
+
**Requires NIP-98 auth. Requires `--pay-token` configured.**
|
|
214
|
+
|
|
215
|
+
Execute a swap against an open sell order.
|
|
216
|
+
|
|
217
|
+
Request:
|
|
218
|
+
```json
|
|
219
|
+
{ "id": "<offer-uuid>" }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The server:
|
|
223
|
+
1. Finds the pending offer
|
|
224
|
+
2. Checks buyer's sat balance >= offer price
|
|
225
|
+
3. Transfers tokens from seller to buyer on the MRC20 trail (Bitcoin TX)
|
|
226
|
+
4. Debits buyer's sats, credits seller's sats on the webledger
|
|
227
|
+
5. Marks offer as filled
|
|
228
|
+
|
|
229
|
+
Response includes the portable proof, same as buy.
|
|
230
|
+
|
|
231
|
+
### GET /pay/*
|
|
232
|
+
**Requires NIP-98 auth.**
|
|
233
|
+
|
|
234
|
+
Access a paid resource. The server deducts `cost` sats from the caller's balance. If balance < cost, returns 402:
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
{
|
|
238
|
+
"error": "Payment Required",
|
|
239
|
+
"balance": 0,
|
|
240
|
+
"cost": 1,
|
|
241
|
+
"unit": "sat",
|
|
242
|
+
"deposit": "/pay/.deposit"
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
On success, the resource is served normally with headers:
|
|
247
|
+
```
|
|
248
|
+
X-Balance: 1040587
|
|
249
|
+
X-Cost: 1
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### PUT /pay/*
|
|
253
|
+
|
|
254
|
+
Standard upload — goes through normal WAC auth, not the pay middleware. Only the pod owner can write to `/pay/`.
|
|
255
|
+
|
|
256
|
+
## Configuration
|
|
257
|
+
|
|
258
|
+
### CLI flags
|
|
259
|
+
```
|
|
260
|
+
--pay Enable HTTP 402 for /pay/* routes
|
|
261
|
+
--pay-cost <n> Cost per request in satoshis (default: 1)
|
|
262
|
+
--pay-mempool-url <url> Mempool API URL (default: testnet4)
|
|
263
|
+
--pay-address <addr> Address for receiving MRC20 deposits
|
|
264
|
+
--pay-token <ticker> Token ticker (enables buy/withdraw/sell/swap)
|
|
265
|
+
--pay-rate <n> Sats per token for buy/withdraw (default: 1)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Environment variables
|
|
269
|
+
```
|
|
270
|
+
JSS_PAY=true
|
|
271
|
+
JSS_PAY_COST=1
|
|
272
|
+
JSS_PAY_MEMPOOL_URL=https://mempool.space/testnet4
|
|
273
|
+
JSS_PAY_ADDRESS=tb1q...
|
|
274
|
+
JSS_PAY_TOKEN=PODS
|
|
275
|
+
JSS_PAY_RATE=10
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
## Token Management (CLI)
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
# Mint a new token (requires funded Bitcoin UTXO)
|
|
282
|
+
jss token mint --ticker PODS --supply 10000 \
|
|
283
|
+
--voucher "txo:btc:<txid>:<vout>?amount=<sats>&key=<privkey-hex>"
|
|
284
|
+
|
|
285
|
+
# Transfer tokens
|
|
286
|
+
jss token transfer --ticker PODS --to <pubkey> --amount 100
|
|
287
|
+
|
|
288
|
+
# Show token info
|
|
289
|
+
jss token info PODS
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Data Files
|
|
293
|
+
|
|
294
|
+
| File | Contents |
|
|
295
|
+
|------|----------|
|
|
296
|
+
| `/.well-known/webledgers/webledgers.json` | Balances per DID — multi-currency array format (webledgers.org spec) |
|
|
297
|
+
| `/.well-known/webledgers/replay.json` | Seen MRC20 state hashes (replay protection) |
|
|
298
|
+
| `/.well-known/webledgers/offers.json` | Open sell orders (secondary market) |
|
|
299
|
+
| `/.well-known/webledgers/pool.json` | AMM pool state (reserves, LP shares, k) |
|
|
300
|
+
| `/.well-known/token/<ticker>.json` | MRC20 token trail (state chain, keys, UTXO) |
|
|
301
|
+
|
|
302
|
+
## Source Files
|
|
303
|
+
|
|
304
|
+
| File | Purpose |
|
|
305
|
+
|------|---------|
|
|
306
|
+
| `src/handlers/pay.js` | All `/pay/*` route handling |
|
|
307
|
+
| `src/webledger.js` | Balance read/write/credit/debit |
|
|
308
|
+
| `src/mrc20.js` | MRC20 verification, JCS, BIP-341 key chaining, bech32m |
|
|
309
|
+
| `src/token.js` | Token mint/transfer, Bitcoin TX building, trail persistence |
|
|
310
|
+
| `src/auth/nostr.js` | NIP-98 auth extraction |
|
|
311
|
+
|
|
312
|
+
## Key Concepts
|
|
313
|
+
|
|
314
|
+
**Webledger**: A JSON file mapping URIs to numerical balances, following the [webledgers.org](https://webledgers.org/) spec. The URI format is `did:nostr:<pubkey>`.
|
|
315
|
+
|
|
316
|
+
**MRC20**: A token profile on blocktrails. Each state is a JSON object with `profile`, `prev` (hash link to previous state), `seq`, `ticker`, `balances`, and `ops`. States form a hash chain.
|
|
317
|
+
|
|
318
|
+
**BIP-341 Key Chaining**: Each MRC20 state is hashed and used as a taproot tweak scalar. The scalar is added to the issuer's public key via elliptic curve addition to derive a unique P2TR address per state. This anchors the state chain to Bitcoin — anyone can verify by re-deriving the address and checking the UTXO.
|
|
319
|
+
|
|
320
|
+
**JCS (RFC 8785)**: JSON Canonicalization Scheme — sorted keys, no whitespace, deterministic serialization. Used for hashing states.
|
|
321
|
+
|
|
322
|
+
**NIP-98**: Nostr HTTP Authentication. A signed event (kind 27235) with the request URL and method in tags, base64-encoded in the Authorization header.
|
|
323
|
+
|
|
324
|
+
**NIP-69 (kind 38383)**: P2P order events for trading. Used as the convention for sell orders in the secondary market.
|
|
325
|
+
|
|
326
|
+
## Flow Examples
|
|
327
|
+
|
|
328
|
+
### Agent buys API access
|
|
329
|
+
```
|
|
330
|
+
1. Agent has a funded TXO (Bitcoin UTXO with known private key)
|
|
331
|
+
2. GET /pay/.info → learns cost=1, deposit endpoint
|
|
332
|
+
3. POST /pay/.deposit → posts TXO URI, gets 1M sats credited
|
|
333
|
+
4. GET /pay/data/feed.json → costs 1 sat, returns data
|
|
334
|
+
5. (repeat step 4 up to 1M times)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### User buys and trades tokens
|
|
338
|
+
```
|
|
339
|
+
1. POST /pay/.deposit → deposit sats
|
|
340
|
+
2. POST /pay/.buy → buy 100 PODS for 1000 sats, get Bitcoin proof
|
|
341
|
+
3. POST /pay/.sell → list 50 PODS for sale at 750 sats
|
|
342
|
+
4. (another user)
|
|
343
|
+
5. GET /pay/.offers → sees the sell order
|
|
344
|
+
6. POST /pay/.swap → buys the 50 PODS, seller gets 750 sats credited
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
### Cross-chain AMM trading
|
|
348
|
+
```
|
|
349
|
+
1. Configure pod: jss start --pay --pay-chains "tbtc3,tbtc4"
|
|
350
|
+
2. User A deposits: POST /pay/.deposit "txo:tbtc3:<txid>:<vout>" → gets tbtc3 balance
|
|
351
|
+
3. User B deposits: POST /pay/.deposit "txo:tbtc4:<txid>:<vout>" → gets tbtc4 balance
|
|
352
|
+
4. User A adds liquidity: POST /pay/.pool { "action": "add-liquidity", "tbtc3": 1000, "tbtc4": 5000 }
|
|
353
|
+
5. User B swaps: POST /pay/.pool { "action": "swap", "sell": "tbtc4", "amount": 500 }
|
|
354
|
+
→ receives ~90 tbtc3 (constant product formula, 0.3% fee)
|
|
355
|
+
6. User A removes liquidity: POST /pay/.pool { "action": "remove-liquidity", "all": true }
|
|
356
|
+
→ gets back proportional share of both currencies + earned fees
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### Full exit
|
|
360
|
+
```
|
|
361
|
+
1. POST /pay/.withdraw { "all": true } → converts entire balance to portable tokens
|
|
362
|
+
2. User now holds MRC20 proof, independently verifiable on Bitcoin
|
|
363
|
+
3. Can deposit on another pod, or trade peer-to-peer
|
|
364
|
+
```
|
package/README.md
CHANGED
|
@@ -158,6 +158,7 @@ jss --help # Show help
|
|
|
158
158
|
| `--pay-address <addr>` | Address for receiving deposits | - |
|
|
159
159
|
| `--pay-token <ticker>` | Token to sell (enables primary market + withdrawal) | - |
|
|
160
160
|
| `--pay-rate <n>` | Sats per token for buy/withdraw | 1 |
|
|
161
|
+
| `--pay-chains <ids>` | Multi-chain deposits + AMM (e.g. "tbtc3,tbtc4") | - |
|
|
161
162
|
| `--mongo` | Enable MongoDB-backed /db/ route | false |
|
|
162
163
|
| `--mongo-url <url>` | MongoDB connection URL | mongodb://localhost:27017 |
|
|
163
164
|
| `--mongo-database <name>` | MongoDB database name | solid |
|
|
@@ -834,11 +835,16 @@ jss start --pay --pay-cost 10 --pay-address your-address --pay-token PODS --pay-
|
|
|
834
835
|
|
|
835
836
|
| Method | Path | Description |
|
|
836
837
|
|--------|------|-------------|
|
|
837
|
-
| GET | `/pay/.info` | Public: cost, token info,
|
|
838
|
+
| GET | `/pay/.info` | Public: cost, token info, chains, pool |
|
|
838
839
|
| GET | `/pay/.balance` | Check your balance (NIP-98 auth) |
|
|
839
840
|
| POST | `/pay/.deposit` | Deposit sats via TXO URI or MRC20 state proof |
|
|
840
841
|
| POST | `/pay/.buy` | Buy tokens with sat balance (requires `--pay-token`) |
|
|
841
842
|
| POST | `/pay/.withdraw` | Withdraw balance as portable tokens (requires `--pay-token`) |
|
|
843
|
+
| GET | `/pay/.offers` | List open sell orders (secondary market) |
|
|
844
|
+
| POST | `/pay/.sell` | Create a sell order (requires `--pay-token`) |
|
|
845
|
+
| POST | `/pay/.swap` | Execute a swap against a sell order |
|
|
846
|
+
| GET | `/pay/.pool` | AMM pool state (requires `--pay-chains`) |
|
|
847
|
+
| POST | `/pay/.pool` | AMM swap, add/remove liquidity |
|
|
842
848
|
| GET | `/pay/*` | Paid resource access (deducts balance) |
|
|
843
849
|
|
|
844
850
|
### How It Works
|
|
@@ -879,6 +885,39 @@ curl -X POST -H "Authorization: Nostr <base64-event>" \
|
|
|
879
885
|
|
|
880
886
|
Deposit verification uses the mempool API (default: testnet4). The `X-Balance` and `X-Cost` headers are returned on successful paid requests. Buy and withdraw return portable MRC20 proofs with Bitcoin anchor data for independent verification.
|
|
881
887
|
|
|
888
|
+
### Secondary Market
|
|
889
|
+
|
|
890
|
+
Users can trade tokens peer-to-peer through the pod. Sell orders are created via `/pay/.sell` and filled via `/pay/.swap`. The pod acts as escrow — transferring tokens on the Bitcoin-anchored MRC20 trail and settling sats in the webledger.
|
|
891
|
+
|
|
892
|
+
### Multi-Chain AMM
|
|
893
|
+
|
|
894
|
+
Enable multi-chain deposits and an automated market maker:
|
|
895
|
+
|
|
896
|
+
```bash
|
|
897
|
+
jss start --pay --pay-chains "tbtc3,tbtc4"
|
|
898
|
+
```
|
|
899
|
+
|
|
900
|
+
Deposits detect the chain from the TXO URI prefix (`txo:tbtc3:txid:vout`). Each chain's balance is tracked separately. The AMM uses a constant-product formula (x × y = k) with a 0.3% fee.
|
|
901
|
+
|
|
902
|
+
```bash
|
|
903
|
+
# Add liquidity
|
|
904
|
+
curl -X POST -H "Authorization: Nostr <token>" \
|
|
905
|
+
-H "Content-Type: application/json" \
|
|
906
|
+
http://localhost:3000/pay/.pool \
|
|
907
|
+
-d '{"action": "add-liquidity", "tbtc3": 1000, "tbtc4": 5000}'
|
|
908
|
+
|
|
909
|
+
# Swap
|
|
910
|
+
curl -X POST -H "Authorization: Nostr <token>" \
|
|
911
|
+
-H "Content-Type: application/json" \
|
|
912
|
+
http://localhost:3000/pay/.pool \
|
|
913
|
+
-d '{"action": "swap", "sell": "tbtc3", "amount": 100}'
|
|
914
|
+
|
|
915
|
+
# Check pool state
|
|
916
|
+
curl http://localhost:3000/pay/.pool
|
|
917
|
+
```
|
|
918
|
+
|
|
919
|
+
Supported chains: `btc`, `tbtc3`, `tbtc4`, `ltc`, `signet`.
|
|
920
|
+
|
|
882
921
|
## Authentication
|
|
883
922
|
|
|
884
923
|
### Simple Tokens (Development)
|
package/bin/jss.js
CHANGED
|
@@ -87,6 +87,7 @@ program
|
|
|
87
87
|
.option('--pay-address <addr>', 'Address for receiving deposits')
|
|
88
88
|
.option('--pay-token <ticker>', 'Token to sell (enables primary market)')
|
|
89
89
|
.option('--pay-rate <n>', 'Sats per token for primary market (default: 1)', parseInt)
|
|
90
|
+
.option('--pay-chains <chains>', 'Comma-separated chain IDs for multi-chain deposits/AMM (e.g. "tbtc3,tbtc4")')
|
|
90
91
|
.option('--mongo', 'Enable MongoDB-backed /db/ route')
|
|
91
92
|
.option('--no-mongo', 'Disable MongoDB-backed /db/ route')
|
|
92
93
|
.option('--mongo-url <url>', 'MongoDB connection URL (default: mongodb://localhost:27017)')
|
|
@@ -159,6 +160,7 @@ program
|
|
|
159
160
|
payAddress: config.payAddress,
|
|
160
161
|
payToken: config.payToken,
|
|
161
162
|
payRate: config.payRate,
|
|
163
|
+
payChains: config.payChains,
|
|
162
164
|
mongo: config.mongo,
|
|
163
165
|
mongoUrl: config.mongoUrl,
|
|
164
166
|
mongoDatabase: config.mongoDatabase,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "javascript-solid-server",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.107",
|
|
4
4
|
"description": "A minimal, fast Solid server",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
"benchmark": "node benchmark.js"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
+
"@noble/curves": "^1.2.0",
|
|
27
28
|
"@fastify/middie": "^8.3.3",
|
|
28
29
|
"@fastify/rate-limit": "^9.1.0",
|
|
29
30
|
"@fastify/websocket": "^8.3.1",
|
package/src/config.js
CHANGED
|
@@ -90,6 +90,7 @@ export const defaults = {
|
|
|
90
90
|
payAddress: null,
|
|
91
91
|
payToken: null,
|
|
92
92
|
payRate: 1,
|
|
93
|
+
payChains: null, // comma-separated chain IDs, e.g. "tbtc3,tbtc4"
|
|
93
94
|
|
|
94
95
|
// MongoDB-backed /db/ route
|
|
95
96
|
mongo: false,
|
|
@@ -152,6 +153,7 @@ const envMap = {
|
|
|
152
153
|
JSS_PAY_ADDRESS: 'payAddress',
|
|
153
154
|
JSS_PAY_TOKEN: 'payToken',
|
|
154
155
|
JSS_PAY_RATE: 'payRate',
|
|
156
|
+
JSS_PAY_CHAINS: 'payChains',
|
|
155
157
|
JSS_MONGO: 'mongo',
|
|
156
158
|
JSS_MONGO_URL: 'mongoUrl',
|
|
157
159
|
JSS_MONGO_DATABASE: 'mongoDatabase',
|