Skip to content

Commit caea239

Browse files
author
Mostronator
committed
docs: add Exchange Rates event (NIP-33 kind 30078)
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
1 parent 3f47ba2 commit caea239

1 file changed

Lines changed: 105 additions & 0 deletions

File tree

src/other_events.md

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,111 @@ The operator of a Mostro instance decides which relays the events from that inst
224224

225225
The `r` label indicates the relays through which the Mostro instance is publishing its events.
226226

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.
236+
237+
### Event Structure
238+
239+
```json
240+
[
241+
"EVENT",
242+
"RAND",
243+
{
244+
"id": "<Event id>",
245+
"pubkey": "<Mostro's pubkey>",
246+
"kind": 30078,
247+
"tags": [
248+
["d", "mostro-rates"],
249+
["published_at", "1732546800"],
250+
["source", "yadio"],
251+
["expiration", "1732550400"]
252+
],
253+
"content": "{\"BTC\": {\"USD\": 50000.0, \"EUR\": 45000.0, \"VES\": 850000000.0, \"ARS\": 105000000.0, ...}}",
254+
"sig": "<Mostro's signature>",
255+
"created_at": 1732546800
256+
}
257+
]
258+
```
259+
260+
### Tags
261+
262+
- **d**: `"mostro-rates"` — NIP-33 identifier that makes this event replaceable. Each new rate update replaces the previous one.
263+
- **published_at**: Unix timestamp when the daemon published the event (daemon time, not source timestamp).
264+
- **source**: Rate source identifier (e.g., `"yadio"`).
265+
- **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`:
292+
293+
```toml
294+
[mostro]
295+
# Enable Nostr exchange rate publishing (default: true)
296+
publish_exchange_rates_to_nostr = true
297+
298+
# 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+
227332
# Development Fee
228333

229334
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

Comments
 (0)