Rule-based transaction filtering for Cardano. Connects to Ogmios, applies configurable rules, and emits CloudEvents via Dapr pub/sub.
- Real-time chain sync via Ogmios
- Pluggable rule engine with built-in rules
- CloudEvents 1.0 output with Cardano extensions
- Dual delivery: HTTP (Dapr pub/sub) and gRPC server-streaming
- At-least-once delivery with configurable retry and dead letter queues
- Dapr state store checkpointing with ETag concurrency
- OpenAPI/Swagger API documentation
- SDK-less event consumption (any language)
- Architecture Overview - System design and components
- Event Schema - CloudEvents specification
- Integration Guide - Building custom rules
- Benchmark Results - Performance and delivery guarantees
- OpenAPI Spec - API specification
- Contributing - Development guidelines
src/
├── BlockchainEvents.Domain/ # Models, abstractions, events
├── BlockchainEvents.Engine/ # Rule engine and implementations
└── BlockchainEvents.Worker/ # .NET Worker service (HTTP + gRPC)
protos/
└── blockchain_events.proto # gRPC service definitions
tests/
└── BlockchainEvents.Tests/ # Unit tests (76 tests)
tools/
└── milestone-2-demo.sh # Guided demo (AC-1 through AC-3)
docs/
├── architecture.md # System architecture
├── event-schema.md # Event schema specification
├── integration-guide.md # Custom rule development
├── benchmarks.md # Performance benchmarks
└── openapi.json # OpenAPI 3.0 spec
| Rule | Purpose |
|---|---|
| AddressMatch | Filter by wallet addresses or prefixes |
| PolicyIdAsset | Filter by policy IDs and asset names |
| MetadataKeyValue | Filter by metadata labels and patterns |
| GovernanceTreasury | Filter governance actions, votes, treasury |
| AllTransactions | Match all transactions (testing/capture) |
Rules are configured via appsettings.json or have sensible defaults.
Minimal config (appsettings.json):
{
"BlockchainEvents": {
"Network": "preprod",
"StateStoreName": "statestore",
"PubSubName": "pubsub",
"TopicName": "blockchain-events",
"CheckpointKey": "sync-checkpoint"
},
"Ogmios": {
"Connection": {
"Host": "localhost",
"Port": 1337
}
}
}- .NET 10 SDK
- Docker (for full stack)
- Ogmios running locally (port 1337)
./setup.sh # adds blockchain.local to /etc/hosts — use instead of localhostdocker compose up --build
# View traces at http://localhost:4004# Terminal 1: Start infrastructure
docker compose up redis placement zipkin
# Terminal 2: Run with Dapr CLI
dapr run --app-id blockchain-events \
--app-port 4000 \
--resources-path ./dapr/components \
--config ./dapr/config/config.yaml \
-- dotnet run --project src/BlockchainEvents.Workerdotnet test# Install grpcurl
brew install grpcurl
# Subscribe to all events (streams until Ctrl+C)
grpcurl -plaintext -import-path . -proto protos/blockchain_events.proto \
-d '{}' localhost:4010 blockchain_events.BlockchainEventService/Subscribe
# Subscribe to address-match events only
grpcurl -plaintext -import-path . -proto protos/blockchain_events.proto \
-d '{"rule_filter": "address-match"}' localhost:4010 blockchain_events.BlockchainEventService/Subscribe# Runs five live demonstrations covering AC-1 through AC-3
./tools/milestone-2-demo.shpublic class HighFeeRule(IOptions<HighFeeOptions> options) : TransactionRuleBase
{
public override string Id => "high-fee";
public override string Name => "High Fee Rule";
public override string Description => "Matches high-fee transactions";
public override bool IsEnabled => options.Value.Enabled;
public override bool IsMatch(TransactionData tx, RuleContext ctx)
=> tx.Fee > options.Value.ThresholdLovelace;
public override RuleMatchResult Evaluate(TransactionData tx, RuleContext ctx)
=> new(Id, Name, new Dictionary<string, object> { ["fee"] = tx.Fee });
}
// Register: services.AddSingleton<ITransactionRule, HighFeeRule>();See docs/integration-guide.md for detailed examples.
Events are emitted as CloudEvents 1.0 with Cardano extensions:
{
"specversion": "1.0",
"id": "tx-abc123-address-match-1705312200000",
"source": "cardano://mainnet/slot/115545883/block/4e58bb36...",
"type": "io.cardano.transaction.address-match",
"cardanoslot": 115545883,
"cardanonetwork": "mainnet",
"data": {
"transactionId": "abc123...",
"ruleId": "address-match",
"matchedCriteria": { ... }
}
}See docs/event-schema.md for complete specification.
See CONTRIBUTING.md for development setup and guidelines.