A construction project manager stares at a spreadsheet showing 47 subcontractors working on different phases. When contractor A finishes their phase, contractor B can't start until someone manually verifies A's completion, approves the payment, and notifies B. This "waterfall with lag" is built into every construction workflow.
The lag isn't caused by incompetence. It's caused by disconnected systems. The milestone proof lives in one system. The payment rules live in another. The notification system is a third. Payment can't be released until a human manually confirms the proof matches the rules, then triggers the notification.
DUAL's Event Bus inverts this: events flow through a single shared channel. Every token subscribes to events it cares about. When an event fires, all dependent systems react atomically, instantly, and in order.
A Concrete Cascade: From Milestone to Payment to Notification
Let's trace a BuildFinance milestone from submission to downstream contractor notification:
The entire cascade completes in < 2 seconds. Every step is logged immutably. If any step fails, the cascade stops and emits a diagnostic event. No human intervention needed.
Why Event Bus Beats Webhooks
Traditional webhook systems are fragile:
| Property | REST Webhooks | DUAL Event Bus |
|---|---|---|
| Delivery Guarantee | No — if receiver is down, event is lost | Yes — queues until receiver ready |
| Order Preservation | No — concurrent requests may reorder | Yes — strict ordering per token |
| Replay | No — events disappear | Yes — full history queryable |
| Idempotency | No — duplicates possible | Yes — de-duplicated by token state |
Real consequence: With REST webhooks, if the lender's system briefly goes offline, the PAYMENT_EXECUTED event is lost. Subcontractors don't get paid notifications. The next phase contractor incorrectly starts work. Disputes ensue.
With DUAL Event Bus, the event queues. When lender comes back online, PAYMENT_EXECUTED processes normally. All downstream notifications fire. The state machine stays consistent with reality.
Event Message Format: Self-Describing, Cryptographically Signed
Every event follows this canonical structure:
{
// Unique event ID for replay protection
"event_id": "evt_042801_1712689301_001",
// What happened
"event_type": "MILESTONE_VERIFIED",
// Which token generated this event
"source_token_id": "build_phase_042801",
// Event-specific data
"payload": {
"verified_at": 1712689302,
"checks_passed": [
"photo_metadata_valid",
"inspector_certified",
"insurance_coverage_active"
],
"milestone_value_cents": 500000
},
// Who authorized this event
"actor_did": "subcontractor_001_did",
// Cryptographic proof of authenticity
"signature": "0x...",
// Timestamp from clock oracle (prevents replay)
"timestamp": 1712689302,
"network": "dual_mainnet_v1"
}
This ensures every event is:
- Authentic: Signature proves actor authorization
- Tamper-proof: Any change invalidates the signature
- Timestamped: Clock oracle prevents replay attacks
- Queryable: Event ID allows historical lookup
- Traceable: Links back to source token
Real Example: Auction to Delivery Event Flow
An e-commerce auction completes. This single event triggers a cascade across multiple token types:
| Event | Token Affected | Result |
|---|---|---|
| AUCTION_WON | Product token | state → SOLD, owner → buyer |
| INVOICE_CREATED | Invoice token | amount = final_bid + tax + shipping, state → ISSUED |
| PAYMENT_EXECUTED | Escrow account | Funds held, seller notified payment pending |
| SHIPMENT_CREATED | Shipment token, inventory | Inventory decremented, shipment → IN_TRANSIT |
| DELIVERY_CONFIRMED | Product, seller reputation | Escrow → seller, buyer review enabled, seller rating +1 |
Each event is independent, but causally connected. The Event Bus ensures:
- Events process in order (auction won before payment)
- If a step fails (insufficient buyer funds), downstream events halt
- All state changes are atomic from the distributed system's perspective
Token Subscriptions: Fine-Grained Event Filtering
Not every token cares about every event. DUAL provides fine-grained filtering:
// "Next phase" contractor token
token "phase_b_contractor" {
subscribe_to: [
{
"event_type": "MILESTONE_VERIFIED",
"source_token": "phase_a_token",
"on_fire": transition_to("READY_TO_START")
}
]
}
This token only reacts to one event: MILESTONE_VERIFIED from phase A. When it fires, this token automatically transitions to READY_TO_START. No polling, no manual checks.
Failure Recovery and Compensation Events
If payment execution fails (insufficient funds), the Event Bus emits a compensation event:
{
"event_type": "PAYMENT_FAILED",
"source_token_id": "build_phase_042801",
"failure_reason": "insufficient_lender_funds",
"timestamp": 1712689305
}
Tokens subscribed to PAYMENT_EXECUTED don't receive it. They receive PAYMENT_FAILED instead:
if (event.type == "PAYMENT_FAILED") {
token.state = "PAYMENT_PENDING"
emit "ALERT_LENDER_INSUFFICIENT_FUNDS"
}
Next phase contractor token stays BLOCKED (not READY_TO_START). Lender is alerted. The system is correct: it reflects reality.
Scaling: Why Event Bus is Fast
Concern: With hundreds of milestones submitting simultaneously, won't the Event Bus become a bottleneck?
Answer: No, because events process in parallel.
- 50 milestone submissions → 50 parallel event streams
- Each stream: photo check (parallel), inspector cert (parallel), insurance check (parallel)
- Total time: ~1 second for all 50 milestones to complete compliance
Compare to manual: 50 milestones × 1 hour coordination per milestone = 50 hours. DUAL: 1 second. That's a 180,000× speedup.
Comparison: Manual Coordination vs. Event-Driven
Manual Coordination
Process: Human reviews proof, checks rules, sends notifications
Time: 1-2 hours per milestone
Reliability: Depends on human attention
Auditability: Implicit; hard to reconstruct decisions
Event Bus Automation
Process: Tokens react to events automatically
Time: < 2 seconds per milestone
Reliability: Guaranteed delivery, ordered execution
Auditability: Full event history, immutable
What's Next
Events are asynchronous signals. But some events carry special information: location data. In Part 4, we'll explore how DUAL tokens embed geo-positioning, enabling location-aware economies where tokens behave differently based on where they are in the physical world.