A Go module that provides functionality for creating and managing Nostr events for Ethereum transaction logs. This serves as the reference implementation for an upcoming Nostr Improvement Proposal (NIP) that will standardize how blockchain transaction logs are stored in Nostr-native format.
This module establishes a foundation for storing blockchain transaction logs in Nostr format, starting with Ethereum smart contract events. When smart contracts execute transactions, they emit events (logs) that contain structured data about the transaction. This implementation provides a standardized way to store these logs as Nostr events, enabling:
- Decentralized Storage: Store transaction logs in Nostr relays
- Cross-Platform Compatibility: Access logs from any Nostr client
- Rich Querying: Use Nostr's tag-based filtering system
- Event Relationships: Link related transactions and updates
go get github.com/citizenwallet/nostr-ethThe current implementation focuses on Ethereum transaction logs (kind 30000), which are generated when smart contracts emit events after transaction execution. This serves as the first use case and will be referenced in the upcoming NIP.
- Ethereum Log Events: Create Nostr events from Ethereum smart contract logs
- Status Tracking: Track transaction status changes (pending, confirmed, failed, cancelled)
- Rich Tagging: Comprehensive tag system for filtering and indexing
- Event Parsing: Parse Nostr events back into structured log data
- Flexible Data Interface: JSON-based interface for different data sources
package main
import (
"fmt"
"time"
"github.com/citizenwallet/nostr-eth/pkg/eth/log"
)
func main() {
// Example Ethereum log data from a smart contract event
logData := map[string]interface{}{
"hash": "0x1234567890abcdef1234567890abcdef12345678", // Log hash
"tx_hash": "0xabcdef1234567890abcdef1234567890abcdef12", // Transaction hash
"sender": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6", // From address
"to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b7", // To address
"value": "1000000000000000000", // Value in wei (1 ETH)
"nonce": 12345,
"status": "pending",
"chain_id": "1", // Ethereum mainnet
"created_at": time.Now().Unix(),
"data": map[string]interface{}{
"topic": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925", // Event signature
"from": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
"to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b7",
"value": "1000000000000000000",
},
}
// Create a JSON outputter
jsonOutputter := log.NewGenericJSONOutputter(logData)
// Create a Nostr event for the Ethereum log
event, err := log.CreateTxLogEvent(jsonOutputter, "your_private_key_here")
if err != nil {
panic(err)
}
fmt.Printf("Created Ethereum log event with kind: %d\n", event.Kind)
fmt.Printf("Event ID: %s\n", event.ID)
}// Update the log status when transaction is confirmed
logData["status"] = "confirmed"
logData["updated_at"] = time.Now().Unix()
// Create an update event
updateEvent, err := log.UpdateTxLogEvent(logData, "your_private_key_here")
if err != nil {
panic(err)
}// Update log status with reference to original event
updateEvent, err := log.UpdateLogStatusEvent(logData, "confirmed", "your_private_key_here", "original_event_id")
if err != nil {
panic(err)
}// Parse a Nostr event back into structured log data
parsedEvent, err := log.ParseTxLogEvent(nostrEvent)
if err != nil {
panic(err)
}
fmt.Printf("Event type: %s\n", parsedEvent.EventType)
fmt.Printf("Log hash: %s\n", parsedEvent.LogData["hash"])
fmt.Printf("Transaction hash: %s\n", parsedEvent.LogData["tx_hash"])Represents a Nostr event for transaction logs:
type TxLogEvent struct {
LogData map[string]interface{} `json:"log_data"`
EventType string `json:"event_type"`
Tags []string `json:"tags,omitempty"`
}Interface for outputting JSON data:
type JSONOutputter interface {
ToJSON() []byte
}Simple implementation of JSONOutputter for map[string]interface{}:
type GenericJSONOutputter json.RawMessage-
CreateTxLogEvent(log JSONOutputter, privateKey string) (*nostr.Event, error)- Creates a new Nostr event for an Ethereum transaction log
-
UpdateTxLogEvent(logData map[string]interface{}, privateKey string, originalEventID ...string) (*nostr.Event, error)- Creates a Nostr event for updating a transaction log status
-
UpdateLogStatusEvent(logData map[string]interface{}, newStatus string, privateKey string, originalEventID ...string) (*nostr.Event, error)- Creates a Nostr event for updating log status with new status and timestamp
-
ParseTxLogEvent(evt *nostr.Event) (*TxLogEvent, error)- Parses a Nostr event back into structured data
-
GetTransferData(logData map[string]interface{}) (map[string]interface{}, error)- Extracts transfer data from a log
-
NewGenericJSONOutputter(data map[string]interface{}) GenericJSONOutputter- Creates a new GenericJSONOutputter instance
The module creates Nostr events with kind 30000 for Ethereum transaction logs:
The event content contains a JSON object with:
log_data: The complete Ethereum log dataevent_type: Either "tx_log_created" or "tx_log_updated"tags: Additional tags for categorization
d: Log hash (unique identifier)t: Type tags for filteringtx_log: Identifies this as a transaction logethereum: Identifies the blockchainupdate: Present in update events{chain_id}: Chain identifier (e.g., "1" for mainnet){status}: Transaction status (pending, confirmed, failed, cancelled)
r: Transaction hash as referencep: Address tags for sender and recipient (0x addresses)amount: Value amount for filteringtimestamp: Created timestamp for time-based filteringe: Reference to original event (for updates)
The data field from the Ethereum log is flattened into tags:
- Ethereum addresses (0x...) are tagged with
p - Event topics (hashes) are tagged with their key name
- Numeric values are converted to strings
- Boolean values are converted to strings
{
"hash": "0x1234567890abcdef1234567890abcdef12345678",
"tx_hash": "0xabcdef1234567890abcdef1234567890abcdef12",
"sender": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
"to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b7",
"value": "1000000000000000000",
"nonce": 12345,
"status": "pending",
"chain_id": "1",
"created_at": 1640995200,
"data": {
"topic": "0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925",
"from": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b6",
"to": "0x742d35Cc6634C0532925a3b8D4C9db96C4b4d8b7",
"value": "1000000000000000000"
}
}const (
EventTypeTxLogCreated = "tx_log_created"
EventTypeTxLogUpdated = "tx_log_updated"
)This implementation serves as the foundation for multiple upcoming NIPs:
-
NIP-XX: Ethereum Transaction Logs (Current implementation)
- Standardizes kind 30000 for Ethereum logs
- Defines tag structure and content format
- Establishes event relationships
-
Future NIPs (Planned)
- Additional blockchain support (Bitcoin, Solana, etc.)
- Different event types (transfers, swaps, governance, etc.)
- Advanced filtering and querying capabilities
Run the tests with:
go test ./pkg/eth -vSee the example/ directory for a complete working example that demonstrates:
- Creating Ethereum log data from smart contract events
- Generating Nostr events with proper tagging
- Updating transaction status
- Parsing events back into structured data
- Using the JSON outputter interface
Run the example with:
go run example/main.goThis is a reference implementation for an upcoming NIP. Contributions should focus on:
- Improving the Ethereum log implementation
- Adding comprehensive test coverage
- Enhancing documentation
- Preparing for NIP submission
This project is licensed under the MIT License - see the LICENSE file for details.