Event-Driven Architecture
Build reactive systems using Guardian events and WebSockets.
Guardian Events
Guardian emits events for all on-chain activities:
interface GuardianEvent {
type: 'transaction.executed' | 'transaction.blocked' | 'override.created' | 'vault.paused';
timestamp: string;
data: any;
}WebSocket Connection
Connect to Guardian's WebSocket for real-time events:
import WebSocket from 'ws';
const ws = new WebSocket('wss://aegis-guardian-production.up.railway.app/ws');
ws.on('open', () => {
// Subscribe to vault events
ws.send(JSON.stringify({
action: 'subscribe',
vault: vaultAddress,
}));
});
ws.on('message', (data) => {
const event = JSON.parse(data.toString());
handleEvent(event);
});Event Handlers
Transaction Executed
async function handleTransactionExecuted(event: GuardianEvent) {
const { signature, vault, destination, amount } = event.data;
// Update internal state
await db.transactions.create({
data: {
signature,
vault,
destination,
amount,
status: 'executed',
timestamp: new Date(event.timestamp),
},
});
// Trigger downstream actions
await notifySlack({
message: `Transaction executed: ${amount / 1e9} SOL to ${destination}`,
signature,
});
// Update analytics
await updateSpendingMetrics(vault, amount);
}Transaction Blocked
async function handleTransactionBlocked(event: GuardianEvent) {
const { vault, destination, amount, blockReason, blinkUrl } = event.data;
// Log blocked transaction
await db.blockedTransactions.create({
data: {
vault,
destination,
amount,
blockReason,
blinkUrl,
timestamp: new Date(event.timestamp),
},
});
// Notify owner
await sendEmail({
to: vaultOwnerEmail,
subject: 'Transaction Blocked - Approval Required',
body: `
Amount: ${amount / 1e9} SOL
Destination: ${destination}
Reason: ${blockReason}
Approve: ${blinkUrl}
`,
});
// Alert security team for high-value blocks
if (amount > 10 * LAMPORTS_PER_SOL) {
await alertSecurityTeam(event);
}
}Override Created
async function handleOverrideCreated(event: GuardianEvent) {
const { vault, nonce, destination, amount } = event.data;
// Track override request
await db.overrides.create({
data: {
vault,
nonce,
destination,
amount,
status: 'pending',
createdAt: new Date(event.timestamp),
},
});
// Set expiration reminder
setTimeout(async () => {
const override = await db.overrides.findUnique({
where: { vault, nonce },
});
if (override.status === 'pending') {
await notifyOwner('Override expiring soon', override);
}
}, 55 * 60 * 1000); // 55 minutes (expires in 60)
}Event Aggregation
Aggregate events for analytics:
class EventAggregator {
private events: GuardianEvent[] = [];
async processEvents() {
setInterval(() => {
this.aggregateAndStore();
}, 60000); // Every minute
}
private async aggregateAndStore() {
const metrics = {
timestamp: new Date(),
totalTransactions: this.events.filter(e => e.type === 'transaction.executed').length,
blockedTransactions: this.events.filter(e => e.type === 'transaction.blocked').length,
totalVolume: this.events
.filter(e => e.type === 'transaction.executed')
.reduce((sum, e) => sum + e.data.amount, 0),
};
await db.metrics.create({ data: metrics });
this.events = []; // Clear processed events
}
}Complex Event Processing
React to patterns across multiple events:
class PatternDetector {
private recentEvents: GuardianEvent[] = [];
detectPatterns() {
// Detect rapid blocked transactions
const recentBlocked = this.recentEvents
.filter(e => e.type === 'transaction.blocked')
.filter(e => Date.now() - new Date(e.timestamp).getTime() < 300000); // Last 5 min
if (recentBlocked.length >= 5) {
this.alertSuspiciousActivity('Multiple blocked transactions');
}
// Detect unusual spending patterns
const recentTxs = this.recentEvents
.filter(e => e.type === 'transaction.executed')
.map(e => e.data.amount);
const avgAmount = recentTxs.reduce((a, b) => a + b, 0) / recentTxs.length;
const maxAmount = Math.max(...recentTxs);
if (maxAmount > avgAmount * 5) {
this.alertAnomalousTransaction(maxAmount, avgAmount);
}
}
}State Machines
Use events to drive state machines:
enum PaymentState {
PENDING = 'pending',
APPROVED = 'approved',
EXECUTED = 'executed',
FAILED = 'failed',
}
class PaymentStateMachine {
private state = PaymentState.PENDING;
handleEvent(event: GuardianEvent) {
switch (this.state) {
case PaymentState.PENDING:
if (event.type === 'override.approved') {
this.state = PaymentState.APPROVED;
} else if (event.type === 'transaction.blocked') {
// Stay pending, waiting for approval
}
break;
case PaymentState.APPROVED:
if (event.type === 'transaction.executed') {
this.state = PaymentState.EXECUTED;
}
break;
}
this.onStateChange(this.state);
}
}Next Steps
- Performance Optimization - Optimize event processing
- Custom Policies - Add custom logic