You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add documentation for the new exchange rates publishing feature via Nostr.
## Overview
Mostro instances can now publish Bitcoin/fiat exchange rates as NIP-33
addressable events (kind 30078) to provide censorship-resistant price
data for clients.
## Key Features
- **Event kind**: 30078 (NIP-33 addressable, replaceable)
- **d tag**: "mostro-rates" (unique identifier)
- **Content**: Full Yadio API format {\BTC\: {\USD\: 50000.0, ...}}
- **Expiration**: NIP-40 tag (min(interval × 2, 3600))
- **Configurable**: Update interval (default 300s, min 60s)
- **Optional**: Can be disabled via config
## Why Nostr?
In censored regions (Venezuela, Cuba, Iran), HTTP exchange rate APIs
are often blocked. Nostr's decentralized relay network provides:
- Censorship resistance
- Zero scaling cost
- Geographic distribution
- No single point of failure
## Security
Clients MUST:
- Verify event.pubkey matches expected Mostro instance
- Verify signature (NIP-01)
- Check expiration timestamp
- Fall back to HTTP API if Nostr fails
## Implementation
- Daemon PR: MostroP2P/mostro#685
- Client spec: MostroP2P/app#36
Related to the Venezuela censorship resistance use case discussed in:
MostroP2P/mostro#684
Copy file name to clipboardExpand all lines: src/other_events.md
+105Lines changed: 105 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -224,6 +224,111 @@ The operator of a Mostro instance decides which relays the events from that inst
224
224
225
225
The `r` label indicates the relays through which the Mostro instance is publishing its events.
226
226
227
+
## Exchange Rates (NIP-33 Kind 30078)
228
+
229
+
Each Mostro instance can optionally publish Bitcoin/fiat exchange rates to Nostr relays as [NIP-33](https://github.com/nostr-protocol/nips/blob/master/33.md) addressable events (kind 30078). This enables:
230
+
231
+
-**Censorship resistance**: Mobile clients in censored regions (Venezuela, Cuba, etc.) can fetch rates via Nostr relays
232
+
-**Zero scaling cost**: Relays distribute events; no per-request infrastructure needed for the rate provider
233
+
-**Backward compatibility**: HTTP API remains available as fallback for clients
234
+
235
+
The daemon fetches rates from a price API (e.g., Yadio) at a configurable interval and publishes the complete payload to Nostr. This is a **replaceable event** (NIP-33), meaning relays keep only the latest version identified by the `d` tag.
-**expiration**: Unix timestamp for event expiration ([NIP-40](https://github.com/nostr-protocol/nips/blob/master/40.md)). Prevents stale rates from being served. Calculated as `min(update_interval × 2, 3600)` to allow for delays while capping at 1 hour.
266
+
267
+
### Content Format
268
+
269
+
The `content` field contains the full rate response in JSON format matching the source API structure (Yadio format):
270
+
271
+
```json
272
+
{
273
+
"BTC": {
274
+
"BTC": 1,
275
+
"USD": 50000.0,
276
+
"EUR": 45000.0,
277
+
"VES": 850000000.0,
278
+
"ARS": 105000000.0,
279
+
"AED": 260491.35,
280
+
"..."
281
+
}
282
+
}
283
+
```
284
+
285
+
**Rate semantics**: Each value under `"BTC"` represents the price of 1 BTC in that currency.
286
+
287
+
**Example**: `"USD": 50000.0` means 1 BTC = 50,000 USD.
288
+
289
+
### Configuration
290
+
291
+
Exchange rate publishing is configured in `settings.toml`:
# Update interval in seconds (default: 300, minimum: 60)
299
+
exchange_rates_update_interval_seconds = 300
300
+
```
301
+
302
+
If `publish_exchange_rates_to_nostr` is `false`, no exchange rate events are published.
303
+
304
+
### Client Usage
305
+
306
+
Clients should:
307
+
308
+
1.**Subscribe** to events with filter: `{"kinds": [30078], "#d": ["mostro-rates"], "authors": [<mostro_pubkey>]}`
309
+
2.**Verify** the event signature and that `event.pubkey` matches the expected Mostro instance pubkey (prevents price manipulation)
310
+
3.**Parse** the `content` field as JSON
311
+
4.**Check** the `expiration` tag to ensure rates are not stale
312
+
5.**Fall back** to HTTP API if Nostr fails or rates are expired
313
+
314
+
### Security Considerations
315
+
316
+
**⚠️ CRITICAL**: Clients MUST verify `event.pubkey == expected_mostro_pubkey` before using the rates. Without this check, a malicious actor could publish fake rates to manipulate prices.
317
+
318
+
**Signature verification**: Use standard Nostr signature verification (NIP-01) to ensure the event was signed by the Mostro instance's private key.
319
+
320
+
**Expiration**: Clients should reject events where `current_time > expiration` to avoid using stale rates.
321
+
322
+
### Why Nostr?
323
+
324
+
In countries with government censorship (Venezuela, Cuba, Iran, etc.), HTTP access to exchange rate APIs is often blocked. Nostr's decentralized relay network makes censorship impractical:
325
+
326
+
-**Geographic distribution**: Relays are hosted worldwide
327
+
-**No single point of failure**: Blocking one relay doesn't affect others
328
+
-**Standard Nostr infrastructure**: No special infrastructure needed; uses existing Nostr relay network
329
+
330
+
This aligns with Mostro's mission to provide censorship-resistant peer-to-peer Bitcoin trading.
331
+
227
332
# Development Fee
228
333
229
334
The development fee mechanism provides sustainable funding for Mostro development by automatically sending a configurable percentage of the Mostro fee to a lightning address on each successful order, this a regular event with kind 8383 which is expected to be stored by relays.
0 commit comments