diff --git a/examples/01-create-account.js b/examples/01-create-account.js deleted file mode 100644 index 3f82033..0000000 --- a/examples/01-create-account.js +++ /dev/null @@ -1,67 +0,0 @@ -/* - Example script creating a key-pair for a Nostr account and publishing profile metadata. - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure' -import * as nip19 from 'nostr-tools/nip19' -import { bytesToHex } from '@noble/hashes/utils.js' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// Generate keys -const sk = generateSecretKey() // `sk` is a Uint8Array -const nsec = nip19.nsecEncode(sk) -const skHex = bytesToHex(sk) - -const pk = getPublicKey(sk) // `pk` is a hex string -const npub = nip19.npubEncode(pk) - -console.log('private key:', skHex) -console.log('encoded private key:', nsec) -console.log() -console.log('public key:', pk) -console.log('encoded public key:', npub) -console.log() - -// Create profile metadata event (kind 0) -const profileMetadata = { - name: 'Alice', - about: 'Hello, I am Alice!', - picture: 'https://example.com/alice.jpg' -} - -const eventTemplate = { - kind: 0, - created_at: Math.floor(Date.now() / 1000), - tags: [], - content: JSON.stringify(profileMetadata) -} - -// Sign the event -const signedEvent = finalizeEvent(eventTemplate, sk) -console.log('Signed event:', JSON.stringify(signedEvent, null, 2)) - -// Publish to REST API -try { - const response = await fetch(`${API_URL}/event`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(signedEvent) - }) - - const result = await response.json() - console.log('Publish result:', result) - - if (result.accepted) { - console.log('Profile metadata published successfully!') - } else { - console.error('Failed to publish:', result.message) - } -} catch (err) { - console.error('Error publishing event:', err) -} diff --git a/examples/08-x402-axios-client.js b/examples/01-x402-axios-client.js similarity index 100% rename from examples/08-x402-axios-client.js rename to examples/01-x402-axios-client.js diff --git a/examples/02-read-posts.js b/examples/02-read-posts.js deleted file mode 100644 index 4776a13..0000000 --- a/examples/02-read-posts.js +++ /dev/null @@ -1,44 +0,0 @@ -/* - Example script for reading posts (kind 1 events) from a relay. - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// JB55's public key -const jb55 = '32e1827635450ebb3c5a7d12c1f8e7b2b514439ac10a67eef3d9fd9c5c68e245' - -// Create subscription ID -const subId = 'read-posts-' + Date.now() - -// Create filters - read posts from JB55 -const filters = { - limit: 2, - kinds: [1], - authors: [jb55] -} - -try { - // Query events using GET /req/:subId - const filtersJson = encodeURIComponent(JSON.stringify([filters])) - const url = `${API_URL}/req/${subId}?filters=${filtersJson}` - - console.log(`Querying events from ${API_URL}`) - console.log('Filters:', JSON.stringify(filters, null, 2)) - - const response = await fetch(url) - const events = await response.json() - - console.log(`\nReceived ${events.length} events:`) - events.forEach((ev, index) => { - console.log(`\nEvent ${index + 1}:`) - console.log(' ID:', ev.id) - console.log(' Author:', ev.pubkey) - console.log(' Created:', new Date(ev.created_at * 1000).toISOString()) - console.log(' Content:', ev.content) - }) -} catch (err) { - console.error('Error reading posts:', err) -} diff --git a/examples/03-write-post.js b/examples/03-write-post.js deleted file mode 100644 index 4721444..0000000 --- a/examples/03-write-post.js +++ /dev/null @@ -1,55 +0,0 @@ -/* - Example script for writing a post to a relay. - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { finalizeEvent, getPublicKey } from 'nostr-tools/pure' -import { hexToBytes } from '@noble/hashes/utils.js' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// Alice is our user making the post. -const alicePrivKeyHex = '3292a48aa331aeccce003d50d70fbd79617ba91860abbd2c78fa4a8301e36bc0' -const alicePrivKeyBin = hexToBytes(alicePrivKeyHex) -const alicePubKey = getPublicKey(alicePrivKeyBin) -console.log(`Alice Public Key: ${alicePubKey}`) - -const now = new Date() - -// Generate a post. -const eventTemplate = { - kind: 1, - created_at: Math.floor(Date.now() / 1000), - tags: [], - content: `This is a test message posted at ${now.toLocaleString()}` -} -console.log(`eventTemplate: ${JSON.stringify(eventTemplate, null, 2)}`) - -// Sign the post -const signedEvent = finalizeEvent(eventTemplate, alicePrivKeyBin) -console.log('signedEvent:', JSON.stringify(signedEvent, null, 2)) - -// Publish to REST API -try { - const response = await fetch(`${API_URL}/event`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(signedEvent) - }) - - const result = await response.json() - console.log('result:', result) - - if (result.accepted) { - console.log('Post published successfully!') - console.log('Event ID:', result.eventId) - } else { - console.error('Failed to publish:', result.message) - } -} catch (err) { - console.error('Error publishing post:', err) -} diff --git a/examples/04-read-alice-posts.js b/examples/04-read-alice-posts.js deleted file mode 100644 index 1636f18..0000000 --- a/examples/04-read-alice-posts.js +++ /dev/null @@ -1,49 +0,0 @@ -/* - Example script for reading posts from user Alice. - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { getPublicKey } from 'nostr-tools/pure' -import { hexToBytes } from '@noble/hashes/utils.js' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// Alice is our user. -const alicePrivKeyHex = '3292a48aa331aeccce003d50d70fbd79617ba91860abbd2c78fa4a8301e36bc0' -const alicePrivKeyBin = hexToBytes(alicePrivKeyHex) -const alicePubKey = getPublicKey(alicePrivKeyBin) -console.log(`Alice Public Key: ${alicePubKey}`) - -// Create subscription ID -const subId = 'read-alice-posts-' + Date.now() - -// Create filters - read posts from Alice -const filters = { - limit: 2, - kinds: [1], - authors: [alicePubKey] -} - -try { - // Query events using GET /req/:subId - const filtersJson = encodeURIComponent(JSON.stringify([filters])) - const url = `${API_URL}/req/${subId}?filters=${filtersJson}` - - console.log(`Querying events from ${API_URL}`) - console.log('Filters:', JSON.stringify(filters, null, 2)) - - const response = await fetch(url) - const events = await response.json() - - console.log(`\nReceived ${events.length} events from Alice:`) - events.forEach((ev, index) => { - console.log(`\nEvent ${index + 1}:`) - console.log(' ID:', ev.id) - console.log(' Created:', new Date(ev.created_at * 1000).toISOString()) - console.log(' Content:', ev.content) - }) -} catch (err) { - console.error('Error reading Alice posts:', err) -} diff --git a/examples/05-get-follow-list.js b/examples/05-get-follow-list.js deleted file mode 100644 index 7ef397d..0000000 --- a/examples/05-get-follow-list.js +++ /dev/null @@ -1,53 +0,0 @@ -/* - Example script for getting a follow list (kind 3 events). - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { getPublicKey } from 'nostr-tools/pure' -import { hexToBytes } from '@noble/hashes/utils.js' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// Alice is our user to get the follow list. -const alicePrivKeyHex = '3292a48aa331aeccce003d50d70fbd79617ba91860abbd2c78fa4a8301e36bc0' -const alicePrivKeyBin = hexToBytes(alicePrivKeyHex) -const alicePubKey = getPublicKey(alicePrivKeyBin) -console.log(`Alice Public Key: ${alicePubKey}`) - -// Create subscription ID -const subId = 'get-follow-list-' + Date.now() - -// Create filters - get follow list (kind 3) from Alice -const filters = { - limit: 5, - kinds: [3], - authors: [alicePubKey] -} - -try { - // Query events using GET /req/:subId - const filtersJson = encodeURIComponent(JSON.stringify([filters])) - const url = `${API_URL}/req/${subId}?filters=${filtersJson}` - - console.log(`Querying follow list from ${API_URL}`) - console.log('Filters:', JSON.stringify(filters, null, 2)) - - const response = await fetch(url) - const events = await response.json() - - if (events.length > 0) { - // Get the most recent follow list (kind 3 events are replaceable) - const followListEvent = events[0] - const aliceFollowList = followListEvent.tags.filter(tag => tag[0] === 'p') - console.log(`\nAlice Follow list (${aliceFollowList.length} followed users):`) - aliceFollowList.forEach((tag, index) => { - console.log(` ${index + 1}. ${tag[1]}${tag[3] ? ` (${tag[3]})` : ''}`) - }) - } else { - console.log('\nNo follow list found for Alice') - } -} catch (err) { - console.error('Error getting follow list:', err) -} diff --git a/examples/06-update-follow-list.js b/examples/06-update-follow-list.js deleted file mode 100644 index 292e241..0000000 --- a/examples/06-update-follow-list.js +++ /dev/null @@ -1,63 +0,0 @@ -/* - Example to update the follow list with a new list of people to follow. - Refactored to use REST API instead of WebSocket. - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { finalizeEvent, getPublicKey } from 'nostr-tools/pure' -import { hexToBytes } from '@noble/hashes/utils.js' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -// Alice wants to update her follow list -const alicePrivKeyHex = '3292a48aa331aeccce003d50d70fbd79617ba91860abbd2c78fa4a8301e36bc0' -const alicePrivKeyBin = hexToBytes(alicePrivKeyHex) -const alicePubKey = getPublicKey(alicePrivKeyBin) -console.log(`Alice Public Key: ${alicePubKey}`) - -// Bob is the person to be added to the new follow list -const bobPrivKeyHex = 'd2e71a977bc3900d6b0f787421e3d1a666cd12ca625482b0d9eeffd23489c99f' -const bobPrivKeyBin = hexToBytes(bobPrivKeyHex) -const bobPubKey = getPublicKey(bobPrivKeyBin) -console.log(`Bob Public Key: ${bobPubKey}`) - -const psf = 'wss://nostr-relay.psfoundation.info' - -const followList = [ - ['p', bobPubKey, psf, 'bob'] -] - -// Generate a follow list event (kind 3) -const eventTemplate = { - kind: 3, - created_at: Math.floor(Date.now() / 1000), - tags: followList, - content: '' -} -console.log(`eventTemplate: ${JSON.stringify(eventTemplate, null, 2)}`) - -// Sign the event -const signedEvent = finalizeEvent(eventTemplate, alicePrivKeyBin) - -// Publish to REST API -try { - const response = await fetch(`${API_URL}/event`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(signedEvent) - }) - - const result = await response.json() - console.log('Publish result:', result) - - if (result.accepted) { - console.log('Follow list updated successfully!') - } else { - console.error('Failed to update follow list:', result.message) - } -} catch (err) { - console.error('Error updating follow list:', err) -} diff --git a/examples/07-liking-event.js b/examples/07-liking-event.js deleted file mode 100644 index 5c0a685..0000000 --- a/examples/07-liking-event.js +++ /dev/null @@ -1,59 +0,0 @@ -/* - Example script for adding a reaction (like) to an event. - Refactored to use REST API instead of WebSocket. - https://github.com/nostr-protocol/nips/blob/master/25.md - - Run the server with `npm start` in the main directory, before running this example. -*/ - -import { hexToBytes } from '@noble/hashes/utils.js' -import { finalizeEvent, getPublicKey } from 'nostr-tools/pure' - -const API_URL = process.env.API_URL || 'http://localhost:5942' - -const bobPrivKeyHex = 'd2e71a977bc3900d6b0f787421e3d1a666cd12ca625482b0d9eeffd23489c99f' -const bobPrivKeyBin = hexToBytes(bobPrivKeyHex) -const bobPubKey = getPublicKey(bobPrivKeyBin) - -const psf = 'wss://nostr-relay.psfoundation.info' - -const evIdToLike = 'd09b4c5da59be3cd2768aa53fa78b77bf4859084c94f3bf26d401f004a9c8167' -const evIdAuthorPubKey = '2c7e76c0f8dc1dca9d0197c7d19be580a8d074ccada6a2f6ebe056ae41092e92' - -// Generate like event (kind 7) -const likeEventTemplate = { - kind: 7, - created_at: Math.floor(Date.now() / 1000), - pubkey: bobPubKey, - tags: [ - ['e', evIdToLike, psf], // "e" tag includes event id, relay reference - ['p', evIdAuthorPubKey, psf] // "p" tag includes author pubkey, relay reference - ], - content: '+' -} - -// Sign the event -const signedEvent = finalizeEvent(likeEventTemplate, bobPrivKeyBin) -console.log('signedEvent:', JSON.stringify(signedEvent, null, 2)) - -// Publish to REST API -try { - const response = await fetch(`${API_URL}/event`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(signedEvent) - }) - - const result = await response.json() - console.log('result:', result) - - if (result.accepted) { - console.log('Like published successfully!') - } else { - console.error('Failed to publish like:', result.message) - } -} catch (err) { - console.error('Error publishing like:', err) -} diff --git a/examples/README.md b/examples/README.md index d840c99..b407ffc 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,90 +1,7 @@ -# REST2NOSTR Examples +# x402 Examples -This directory contains examples refactored from the `nostr-sandbox/` directory to use the REST API instead of WebSocket connections. +This example uses the Base blockchain to talk to the psf-bch-api server at https://x402.fullstack.cash. -## Prerequisites - -1. Install dependencies: -```bash -npm install nostr-tools @noble/hashes -``` - -2. Start the REST2NOSTR proxy server: -```bash -npm start -``` - -3. Set the API_URL environment variable if the server is not running on localhost:3000: -```bash -export API_URL=http://localhost:3000 -``` - -## Examples - -### 01-create-account.js -Creates a new Nostr account keypair and publishes profile metadata (kind 0 event). - -```bash -node examples/01-create-account.js -``` - -### 02-read-posts.js -Reads posts (kind 1 events) from a specific author using GET /req/:subId. - -```bash -node examples/02-read-posts.js -``` - -### 03-write-post.js -Publishes a text post (kind 1 event) using POST /event. - -```bash -node examples/03-write-post.js -``` - -### 04-read-alice-posts.js -Reads posts from Alice's account using GET /req/:subId with author filter. - -```bash -node examples/04-read-alice-posts.js -``` - -### 14-get-follow-list.js -Retrieves a user's follow list (kind 3 event) using GET /req/:subId. - -```bash -node examples/05-get-follow-list.js -``` - -### 15-update-follow-list.js -Updates a user's follow list (kind 3 event) using POST /event. - -```bash -node examples/06-update-follow-list.js -``` - -### 17-liking-event.js -Adds a reaction/like to an event (kind 7 event) using POST /event. - -```bash -node examples/07-liking-event.js -``` - -## API Endpoints Used - -- **POST /event**: Publish events to the relay -- **GET /req/:subId**: Stateless query for events (returns immediately) - -## Differences from WebSocket Examples - -1. **No WebSocket connections**: All communication is via HTTP REST API -2. **Stateless queries**: GET /req/:subId returns events immediately rather than streaming -3. **Event publishing**: POST /event returns immediately with acceptance status -4. **No subscription management**: For stateless queries, subscriptions are automatically closed after EOSE - -## Notes - -- These examples use the same private keys as the original sandbox examples for consistency -- The REST API handles WebSocket connections to relays internally -- For real-time streaming, use POST /req/:subId which supports Server-Sent Events (SSE) +Get a private key for an Base address that contains some USDC. Then execute the example like this: +- `PRIVATE_KEY=0x8bff4962a50d43fe93ac2d7df4da12e6bd701bd1bf12b66d391365ae03058d4f node 08-x402-axios-client.js`