# Multi-Wallet Event & Audit Matrix

Reference artifacts:
- docs/adr/0007-observability-and-audit-traceability-standard.md
- docs/multi-wallet/multi-wallet-phase-tracker.md
- docs/multi-wallet/deliverables/command-query-event-contracts.md

## 1. Purpose

This matrix provides a complete, traceable record of every domain event and audit event
emitted by the multi-wallet subsystem. It cross-references:
- The command or code path that emits each event
- The telemetry event name(s) that carry it
- The typed metric event re-emitted by `MultiWalletTelemetry` (where applicable)
- The compliance controls that depend on the audit trail

## 2. Domain Events (PubSub — `wallet_*:events` topics)

Domain events are broadcast via `Phoenix.PubSub` under the `{:domain_event, event}` message
on the `"wallet_accounts:events"` or `"wallet_transfers:events"` topic.

### 2.1 WalletProduct Domain Events

| Event Module | Action Field | Emitted By | Topic |
|---|---|---|---|
| `WalletProductCreated` | `wallet_product_created` | `CreateWalletProduct.execute/3` | `wallet_accounts:events` |
| `WalletProductFrozen` | `wallet_product_frozen` | `FreezeWalletProduct.execute/2` | `wallet_accounts:events` |
| `WalletProductUnfrozen` | `wallet_product_unfrozen` | `UnfreezeWalletProduct.execute/2` | `wallet_accounts:events` |
| `WalletProductClosed` | `wallet_product_closed` | `CloseWalletProduct.execute/2` | `wallet_accounts:events` |
| `WalletProductUpdated` | `wallet_product_updated` | `UpdateWalletProduct.execute/2` | `wallet_accounts:events` |

### 2.2 Sub-Wallet Domain Events

| Event Module | Action Field | Emitted By | Topic |
|---|---|---|---|
| `SubWalletCreated` | `sub_wallet_created` | `CreateSubWallet.execute/3` | `wallet_accounts:events` |
| `SubWalletFrozen` | `sub_wallet_frozen` | `FreezeSubWallet.execute/2` | `wallet_accounts:events` |
| `SubWalletUnfrozen` | `sub_wallet_unfrozen` | `UnfreezeSubWallet.execute/2` | `wallet_accounts:events` |
| `SubWalletClosed` | `sub_wallet_closed` | `CloseSubWallet.execute/2` | `wallet_accounts:events` |
| `SubWalletUpdated` | `sub_wallet_updated` | `UpdateSubWallet.execute/2` | `wallet_accounts:events` |

### 2.3 Currency Config Domain Events

| Event Module | Action Field | Emitted By | Topic |
|---|---|---|---|
| `CurrencyConfigAdded` | `currency_config_added` | `AddCurrencyConfig.execute/3` | `wallet_accounts:events` |

### 2.4 Transfer Domain Events

| Event Module | Action Field | Emitted By | Topic |
|---|---|---|---|
| `SubWalletTransferInitiated` | `sub_wallet_transfer_initiated` | `TransferBetweenSubWallets — do_execute/2` | `wallet_transfers:events` |
| `SubWalletTransferCompleted` | `sub_wallet_transfer_completed` | `TransferBetweenSubWallets — do_execute/2` | `wallet_transfers:events` |
| `SubWalletTransferFailed` | `sub_wallet_transfer_failed` | `TransferBetweenSubWallets — do_execute/2` | `wallet_transfers:events` |

## 3. Audit Events (Telemetry — `[:app, :audit]`)

Audit events are emitted via `:telemetry.execute([:app, :audit], %{}, audit_struct)` where
`audit_struct` is produced by `WalletObservability.AuditEvent.build/6`.

### 3.1 Accounts Audit Events (`[:wallet_accounts, :audit]`)

| Action | Resource Type | Outcome | Emitted By | Compliance Control |
|---|---|---|---|---|
| `wallet_product_created` | `wallet_product` | `:success` | `CreateWalletProduct` | — |
| `wallet_product_frozen` | `wallet_product` | `:success` | `FreezeWalletProduct` | CTRL-MWF-001 |
| `wallet_product_unfrozen` | `wallet_product` | `:success` | `UnfreezeWalletProduct` | CTRL-MWF-001 |
| `wallet_product_closed` | `wallet_product` | `:success` | `CloseWalletProduct` | CTRL-MWF-001, CTRL-MWB-001 |
| `wallet_product_updated` | `wallet_product` | `:success` | `UpdateWalletProduct` | — |
| `sub_wallet_created` | `sub_wallet` | `:success` | `CreateSubWallet` | — |
| `sub_wallet_frozen` | `sub_wallet` | `:success` | `FreezeSubWallet` | — |
| `sub_wallet_unfrozen` | `sub_wallet` | `:success` | `UnfreezeSubWallet` | — |
| `sub_wallet_closed` | `sub_wallet` | `:success` | `CloseSubWallet` | — |
| `sub_wallet_updated` | `sub_wallet` | `:success` | `UpdateSubWallet` | — |
| `currency_config_added` | `currency_config` | `:success` | `AddCurrencyConfig` | — |

### 3.2 Transfers Audit Events (`[:wallet_transfers, :audit]`)

| Action | Resource Type | Outcome | Emitted By | Compliance Control |
|---|---|---|---|---|
| `sub_wallet_transfer_completed` | `sub_wallet_transfer` | `:success` | `TransferBetweenSubWallets` | CTRL-MWT-001 |
| `sub_wallet_transfer_failed` | `sub_wallet_transfer` | `:failure` | `TransferBetweenSubWallets` | CTRL-MWT-001, CTRL-MWT-002 |

**Note:** `sub_wallet_transfer_initiated` is a domain event only (PubSub) — no separate audit event;
the `completed` or `failed` audit event provides the terminal trace.

## 4. Typed Metric Events (via `MultiWalletTelemetry`)

`WalletAccounts.Telemetry.MultiWalletTelemetry` subscribes to `[:wallet_accounts, :audit]` and
`[:wallet_transfers, :audit]`, filters by `@multi_wallet_actions`, and re-emits typed metric events
for each operation. These are the canonical events for dashboards, SLO counters, and alerting.

### 4.1 Wallet Accounts Metric Events (`[:wallet_accounts, :multi_wallet, :<action>]`)

| Typed Metric Event | Triggered By Audit Action |
|---|---|
| `[:wallet_accounts, :multi_wallet, :wallet_product_created]` | `wallet_product_created` |
| `[:wallet_accounts, :multi_wallet, :wallet_product_frozen]` | `wallet_product_frozen` |
| `[:wallet_accounts, :multi_wallet, :wallet_product_unfrozen]` | `wallet_product_unfrozen` |
| `[:wallet_accounts, :multi_wallet, :wallet_product_closed]` | `wallet_product_closed` |
| `[:wallet_accounts, :multi_wallet, :wallet_product_updated]` | `wallet_product_updated` |
| `[:wallet_accounts, :multi_wallet, :sub_wallet_created]` | `sub_wallet_created` |
| `[:wallet_accounts, :multi_wallet, :sub_wallet_frozen]` | `sub_wallet_frozen` |
| `[:wallet_accounts, :multi_wallet, :sub_wallet_unfrozen]` | `sub_wallet_unfrozen` |
| `[:wallet_accounts, :multi_wallet, :sub_wallet_closed]` | `sub_wallet_closed` |
| `[:wallet_accounts, :multi_wallet, :sub_wallet_updated]` | `sub_wallet_updated` |
| `[:wallet_accounts, :multi_wallet, :currency_config_added]` | `currency_config_added` |

### 4.2 Wallet Transfers Metric Events (`[:wallet_transfers, :multi_wallet, :<action>]`)

| Typed Metric Event | Triggered By Audit Action |
|---|---|
| `[:wallet_transfers, :multi_wallet, :sub_wallet_transfer_initiated]` | `sub_wallet_transfer_initiated` |
| `[:wallet_transfers, :multi_wallet, :sub_wallet_transfer_completed]` | `sub_wallet_transfer_completed` |
| `[:wallet_transfers, :multi_wallet, :sub_wallet_transfer_failed]` | `sub_wallet_transfer_failed` |

## 5. Compliance Control Cross-Reference

| Control ID | Domain | Enforced By | Evidence |
|---|---|---|---|
| CTRL-MWF-001 | multi_wallet | `FreezeWalletProduct` / `CloseWalletProduct` emit audit with `actor_id` + `reason` | `[:wallet_accounts, :audit]` AuditEvent.metadata |
| CTRL-MWT-001 | multi_wallet | `TransferBetweenSubWallets.execute/2` calls `Map.fetch!(:idempotency_key)` + `IdempotencyStore.register/2` | `IdempotencyStore`, command rejection on missing key |
| CTRL-MWT-002 | multi_wallet | `TransferBetweenSubWallets` failure path emits `AuditEvent.build(:wallet_transfers, "sub_wallet_transfer_failed", :failure, ...)` | `[:wallet_transfers, :audit]` AuditEvent.outcome == :failure |
| CTRL-MWB-001 | multi_wallet | `CloseWalletProduct` command enforces zero-balance guard before closing | `CloseWalletProduct` returns `{:error, :non_zero_balance}` when guard fails |

## 6. Event Payload Fields

### 6.1 AuditEvent common fields (all multi-wallet audits)

```
category:       atom — :wallet_accounts or :wallet_transfers
action:         string — e.g. "wallet_product_frozen"
resource_type:  string — e.g. "wallet_product"
resource_id:    TypedId — e.g. "wp_abc123"
outcome:        atom — :success | :failure
correlation_id: string — UUID v4
occurred_at:    DateTime (UTC)
metadata:       map — operation-specific fields (see below)
```

### 6.2 Metadata fields by action

| Action | Key Metadata Fields |
|---|---|
| `wallet_product_created` | `user_id`, `product_type_id`, `primary_currency`, `label` |
| `wallet_product_frozen` | `actor_id`, `reason`, `wallet_product_id` |
| `wallet_product_unfrozen` | `actor_id`, `wallet_product_id` |
| `wallet_product_closed` | `actor_id`, `reason`, `wallet_product_id` |
| `sub_wallet_created` | `wallet_product_id`, `label`, `currency`, `is_default` |
| `sub_wallet_frozen` | `actor_id`, `reason`, `sub_wallet_id` |
| `sub_wallet_unfrozen` | `actor_id`, `sub_wallet_id` |
| `sub_wallet_closed` | `actor_id`, `reason`, `sub_wallet_id` |
| `currency_config_added` | `wallet_product_id`, `currency_code`, `classification` |
| `sub_wallet_transfer_completed` | `from_sub_wallet_id`, `to_sub_wallet_id`, `amount`, `currency`, `journal_id` |
| `sub_wallet_transfer_failed` | `from_sub_wallet_id`, `to_sub_wallet_id`, `amount`, `currency`, `failure_reason` |

## 7. Coverage Assessment

| Coverage Area | Status | Notes |
|---|---|---|
| WalletProduct lifecycle audit trail | Complete | All 5 commands emit `[:wallet_accounts, :audit]` |
| SubWallet lifecycle audit trail | Complete | All 5 commands emit `[:wallet_accounts, :audit]` |
| Transfer success audit | Complete | `sub_wallet_transfer_completed` audit present |
| Transfer failure audit | Complete | `sub_wallet_transfer_failed` audit added in Phase 7 (was missing in Phase 5) |
| Typed metric events | Complete | 12 actions → 12 typed events via `MultiWalletTelemetry` |
| Compliance control coverage | Complete | 4 multi-wallet controls in `ControlCatalog` (CTRL-MWF-001/MWT-001/MWT-002/MWB-001) |
| Idempotency audit | Partial | Transfer idempotency replay does not re-emit audit (expected: replay returns cached result) |
