Skip to content

5.3 Observability and Auditing Framework

Effective institutional operation requires a comprehensive observability framework that bridges the private and public execution contexts.

Unified Dashboard

A comprehensive dashboard combining Canton ledger state with public chain execution status.

Key Metrics

┌────────────────────────────────────────────────────┐
│            Canton DeFi Vault Dashboard             │
├────────────────────────────────────────────────────┤
│ Intents Generated: 1,234  │  Success Rate: 99.2%   │
│ Total AUM: $10.5M         │  Active Vaults: 12     │
│ Avg. Latency: 2.3s        │  Failed TX: 10         │
└────────────────────────────────────────────────────┘

Implementation

typescript
interface DashboardMetrics {
  // Canton metrics
  canton: {
    activePolicies: number;
    pendingApprovals: number;
    intentsGenerated24h: number;
    averageApprovalTime: number;
  };

  // Relayer metrics
  relayer: {
    queueDepth: number;
    processingRate: number;
    verificationFailures: number;
    currentLeader: string;
  };

  // Execution metrics
  execution: {
    successRate: number;
    averageGas: BigNumber;
    averageLatency: number;
    totalVolume24h: BigNumber;
  };

  // Per-chain metrics
  chains: Map<string, ChainMetrics>;
}

class ObservabilityService {
  async getMetrics(): Promise<DashboardMetrics> {
    const [canton, relayer, execution] = await Promise.all([
      this.getCantonMetrics(),
      this.getRelayerMetrics(),
      this.getExecutionMetrics()
    ]);

    return { canton, relayer, execution, chains: await this.getChainMetrics() };
  }
}

Audit Trail Correlation

Tracing a Transaction

typescript
async function traceTransaction(txHash: string): Promise<AuditTrail> {
  // 1. Find on-chain transaction
  const tx = await ethProvider.getTransaction(txHash);
  const receipt = await ethProvider.getTransactionReceipt(txHash);

  // 2. Extract intent data from transaction calldata
  const intentData = decodeExecuteIntent(tx.data);

  // 3. Query Canton for the ExecutionIntent
  const cantonIntent = await canton.query({
    templateId: 'Vault.Policy:ExecutionIntent',
    filter: { nonce: intentData.nonce }
  });

  // 4. Find the source proposal (if Flow B)
  let proposal = null;
  if (cantonIntent.sourceProposalId) {
    proposal = await canton.query({
      templateId: 'Vault.Policy:RebalanceProposal',
      contractId: cantonIntent.sourceProposalId
    });
  }

  // 5. Get the policy in effect
  const policy = await canton.query({
    templateId: 'Vault.Policy:VaultPolicy',
    contractId: cantonIntent.policyId
  });

  return {
    publicChain: {
      txHash,
      block: receipt.blockNumber,
      timestamp: (await ethProvider.getBlock(receipt.blockNumber)).timestamp,
      gasUsed: receipt.gasUsed,
      success: receipt.status === 1
    },
    canton: {
      intentId: cantonIntent.contractId,
      issuer: cantonIntent.issuer,
      createdAt: cantonIntent.createdAt,
      policy: policy,
      proposal: proposal
    }
  };
}

Example Audit Query

typescript
// Find all high-value transactions in the last month
async function auditHighValueTransactions(): Promise<AuditReport> {
  const oneMonthAgo = Date.now() - 30 * 24 * 60 * 60 * 1000;

  const intents = await canton.query({
    templateId: 'Vault.Policy:ExecutionIntent',
    filter: {
      createdAt: { $gte: oneMonthAgo },
      estimatedValue: { $gte: 100000 } // > $100k
    }
  });

  const report = await Promise.all(
    intents.map(async (intent) => {
      const execution = await findExecution(intent.id);

      return {
        intentId: intent.id,
        createdAt: intent.createdAt,
        estimatedValue: intent.estimatedValue,
        executed: execution !== null,
        success: execution?.success,
        actualValue: execution?.actualValue,
        approvals: await getApprovals(intent.sourceProposalId)
      };
    })
  );

  return { period: '30d', highValueTx: report };
}

Real-Time Alerts

Alert Configuration

yaml
# alerts.yml
alerts:
  - name: policy_violation_attempted
    severity: high
    condition: canton.policy_check_failed > 0
    action:
      - notify: risk-officer
      - create_ticket: jira

  - name: execution_failure
    severity: medium
    condition: execution.failed_tx_rate > 0.05
    action:
      - notify: ops-team
      - page_on_call: true

  - name: high_slippage
    severity: medium
    condition: execution.slippage > 2.0  # > 2%
    action:
      - notify: trader
      - pause_vault: true  # Auto-pause

  - name: intent_expiry
    severity: low
    condition: relayer.expired_intents > 5
    action:
      - notify: ops-team

  - name: relayer_equivocation
    severity: critical
    condition: relayer.duplicate_submissions > 0
    action:
      - emergency_pause: all
      - page_on_call: true
      - notify: security-team

Alert Implementation

typescript
class AlertManager {
  private rules: AlertRule[];

  async evaluateAlerts(): Promise<void> {
    const metrics = await this.getMetrics();

    for (const rule of this.rules) {
      if (this.evaluateCondition(rule.condition, metrics)) {
        await this.triggerAlert(rule);
      }
    }
  }

  private async triggerAlert(rule: AlertRule): Promise<void> {
    // Record alert
    await db.alerts.create({
      name: rule.name,
      severity: rule.severity,
      timestamp: Date.now(),
      metrics: this.currentMetrics
    });

    // Execute actions
    for (const action of rule.actions) {
      switch (action.type) {
        case 'notify':
          await this.notify(action.recipient, rule);
          break;

        case 'pause_vault':
          await this.pauseVault(action.vaultId);
          break;

        case 'page_on_call':
          await this.pageOnCall(rule);
          break;

        case 'emergency_pause':
          await this.emergencyPauseAll();
          break;
      }
    }
  }
}

Off-Chain Canton State Snapshots

Snapshot Service

typescript
class CantonSnapshotService {
  async createSnapshot(): Promise<Snapshot> {
    // 1. Query all active contracts
    const [policies, proposals, intents] = await Promise.all([
      canton.queryAll('Vault.Policy:VaultPolicy'),
      canton.queryAll('Vault.Policy:RebalanceProposal'),
      canton.queryAll('Vault.Policy:ExecutionIntent')
    ]);

    // 2. Serialize state
    const state = {
      timestamp: Date.now(),
      ledgerHeight: await canton.getLedgerHeight(),
      policies,
      proposals,
      intents
    };

    // 3. Sign snapshot with Canton key
    const stateHash = hash(JSON.stringify(state));
    const signature = await canton.sign(stateHash);

    // 4. Store snapshot
    const snapshot = {
      ...state,
      hash: stateHash,
      signature
    };

    await storage.put(`snapshots/${state.timestamp}.json`, snapshot);

    return snapshot;
  }

  async verifySnapshot(snapshot: Snapshot): Promise<boolean> {
    // 1. Recompute hash
    const { signature, hash, ...state } = snapshot;
    const computedHash = hash(JSON.stringify(state));

    if (computedHash !== hash) {
      return false;
    }

    // 2. Verify signature
    return canton.verify(hash, signature);
  }
}

// Schedule regular snapshots
setInterval(async () => {
  await snapshotService.createSnapshot();
}, 1000 * 60 * 60); // Every hour

Forensic Analysis Tools

Transaction Investigation

typescript
async function investigateIncident(
  incidentId: string
): Promise<Investigation> {
  const incident = await db.incidents.findOne(incidentId);

  // Collect all related data
  const investigation = {
    incident,
    timeline: await buildTimeline(incident),
    cantonState: await getCantonStateAtTime(incident.timestamp),
    relayerLogs: await getRelayerLogs(incident.timestamp),
    chainTransactions: await getChainTransactions(incident),
    policyHistory: await getPolicyHistory(incident),
    approvals: await getApprovals(incident)
  };

  // Analyze
  investigation.analysis = {
    rootCause: await analyzeRootCause(investigation),
    impact: await assessImpact(investigation),
    recommendations: await generateRecommendations(investigation)
  };

  return investigation;
}

Compliance Report Generation

typescript
async function generateComplianceReport(
  startDate: Date,
  endDate: Date
): Promise<ComplianceReport> {
  // Query all intents in period
  const intents = await canton.query({
    templateId: 'Vault.Policy:ExecutionIntent',
    filter: {
      createdAt: { $gte: startDate, $lte: endDate }
    }
  });

  // Analyze compliance
  const report = {
    period: { start: startDate, end: endDate },
    totalIntents: intents.length,
    policyCompliance: {
      total: intents.length,
      compliant: intents.filter(i => i.policyValidated).length,
      violations: intents.filter(i => !i.policyValidated).length
    },
    approvalWorkflow: {
      requiresApproval: intents.filter(i => i.sourceProposalId).length,
      averageApprovalTime: calculateAverageApprovalTime(intents),
      quorumBreaches: findQuorumBreaches(intents)
    },
    execution: {
      successful: await countSuccessful(intents),
      failed: await countFailed(intents),
      averageGas: await calculateAverageGas(intents)
    },
    securityEvents: await getSecurityEvents(startDate, endDate)
  };

  // Generate PDF
  const pdf = await generatePDF(report);

  // Sign report
  const signature = await canton.sign(hash(pdf));

  return { report, pdf, signature };
}

Performance Monitoring

Key Performance Indicators

typescript
interface KPIs {
  // Latency metrics
  cantonToRelayer: number;  // Intent generation → Relayer receipt
  relayerToChain: number;   // Relayer → Chain submission
  chainConfirmation: number; // Submission → Confirmation
  endToEnd: number;         // Intent generation → Confirmed

  // Throughput metrics
  intentsPerHour: number;
  transactionsPerChain: Map<string, number>;

  // Reliability metrics
  intentSuccessRate: number;
  relayerUptime: number;
  cantonUptime: number;

  // Cost metrics
  averageGasPerIntent: BigNumber;
  totalGasCost24h: BigNumber;
}

async function calculateKPIs(): Promise<KPIs> {
  const metrics = await metricsService.query({
    range: '24h',
    aggregation: 'avg'
  });

  return {
    endToEnd: metrics.latency.p95,
    intentSuccessRate: metrics.success.rate,
    averageGasPerIntent: metrics.gas.avg,
    // ... other KPIs
  };
}

Best Practices

1. Structured Logging

typescript
// Good: Structured, queryable logs
logger.info('intent_executed', {
  intentId: intent.id,
  vaultId: intent.vaultId,
  nonce: intent.nonce,
  chain: 'ethereum',
  txHash: receipt.transactionHash,
  gasUsed: receipt.gasUsed.toString(),
  duration_ms: executionTime
});

// Bad: Unstructured logs
console.log(`Executed intent ${intent.id} with tx ${receipt.transactionHash}`);

2. Correlation IDs

typescript
// Track requests across components
const correlationId = uuid();

logger.info('intent_received', { correlationId, intentId });
// → Relayer
logger.info('transaction_prepared', { correlationId, txHash });
// → Chain
logger.info('transaction_confirmed', { correlationId, blockNumber });
// → Canton
logger.info('execution_reported', { correlationId, success: true });

3. Regular Audits

Schedule regular audit reviews:

typescript
// Weekly audit review
cron.schedule('0 9 * * 1', async () => {  // Every Monday 9 AM
  const report = await generateComplianceReport(
    new Date(Date.now() - 7 * 24 * 60 * 60 * 1000),
    new Date()
  );

  await notifyAuditTeam(report);
});

Canton DeFi - Multichain DeFi Technical Reference