# Token Transfer Events Post

**URL:** <https://forum.aztec.network/t/token-transfer-events-post/8491>\
**Category:** Aztec\
**Created:** [February 23, 2026, 1:59pm UTC](https://forum.aztec.network/t/token-transfer-events-post/8491 "2026-02-23T13:59:20Z")\
**Posts on this page:** 1\
**Page:** 1

<div class="post-metadata">

**Author:** ![wonderland](https://dub1.discourse-cdn.com/flex013/user_avatar/forum.aztec.network/wonderland/32/2437_2.png) [@wonderland](https://forum.aztec.network/u/wonderland)\
**Post date:** [February 23, 2026, 1:59pm UTC](https://forum.aztec.network/t/token-transfer-events-post/8491/1 "2026-02-23T13:59:20Z")

</div>

# Transfer Events on Aztec Token & Tokenized Vault

This post explains how `Transfer` events are emitted across the Token and Tokenized Vault contracts, where they fire, and why the design enables indexers to reconcile supply from public event data.

* * *

## The Transfer Event

```auto
struct Transfer {
    from: AztecAddress,
    to: AztecAddress,
    amount: u128,
}

```

Every public balance-changing operation emits a public `Transfer(from, to, amount)` event. Two sentinel addresses are used:

| Sentinel | Value | Meaning |
| --- | --- | --- |
| `0x0` (AztecAddress::zero) | Mint origin or burn destination | Standard ERC-20 convention for mints (`from = 0`) and burns (`to = 0`) |
| `PRIVATE_ADDRESS_MAGIC_VALUE` | `sha224("PRIVATE_ADDRESS")` → `0x1ea7e...3264` | The counterpart of the balance change is a private note |

* * *

## Why the Magic Value?

**Invariants:**

- **Supply:** The sum of all amounts where `from == 0x0` minus the sum where `to == 0x0` equals the current `total_supply`.
- **Per-address balance:** For any address `addr`, the sum of amounts where `to == addr` minus the sum where `from == addr` equals that address’s public balance. (Private balances are shielded; `PRIV` acts as a sink/source for the private aggregate.)

Without a sentinel for the private side, we would need to expose an address when moving tokens between public and private. That would break privacy. Using a single, fixed magic value instead:

- Keeps the event log **privacy-preserving** for private counterparties
- Preserves the **accountability invariants** — indexers can compute `total_supply` and per-address public balances from public logs even when private counterparties remain shielded
- Keeps `0x0` exclusively for mints and burns, so `total_supply` calculations stay unambiguous

* * *

## Token Contract: Where Events Are Emitted

### Direct (User-Callable) Methods

| Method | Event | Rationale |
| --- | --- | --- |
| `transfer_public_to_public(from, to, amount)` | `Transfer(from, to, amount)` | Direct public→public move; both addresses are public |
| `transfer_public_to_commitment(from, commitment, amount)` | `Transfer(from, PRIV, amount)` | Public balance decreases; recipient is a commitment (private) |
| `mint_to_public(to, amount)` | Via `_mint_to_public` | Mint creates new supply and credits a public address |
| `mint_to_commitment(commitment, amount)` | `Transfer(0x0, PRIV, amount)` | Mint to private; supply increases, recipient is private |
| `burn_public(from, amount)` | Via `_burn_public` | Burn from public; supply decreases |

### Internal Methods (Called by Self or Private Enqueues)

| Method | Event | Called By |
| --- | --- | --- |
| `increase_public_balance_internal(to, amount)` | `Transfer(PRIV, to, amount)` | `transfer_private_to_public`, `transfer_private_to_public_with_commitment` — private balance → public |
| `decrease_public_balance_internal(from, amount)` | `Transfer(from, PRIV, amount)` | `transfer_public_to_private` — public balance → private |
| `increase_total_supply_internal(amount)` | `Transfer(0x0, PRIV, amount)` | `mint_to_private` — mint to private |
| `decrease_total_supply_internal(amount)` | `Transfer(PRIV, 0x0, amount)` | `burn_private` — burn from private |
| `_mint_to_public(to, amount)` | `Transfer(0x0, to, amount)` | `mint_to_public`, `deposit_public_to_public`, `issue_public_to_public`, and vault settle methods |
| `_burn_public(from, amount)` | `Transfer(from, 0x0, amount)` | `burn_public`, `withdraw_public_to_public`, `redeem_public_to_public`, and vault settle methods |

### No Event

- **`transfer_private_to_private`** — Pure private-to-private; no public balance change, no public event.
- `transfer_private_to_commitment` — Pure private-to-private; no public balance change, no public event.

* * *

## Tokenized Vault: Where Events Are Emitted

The vault is a Token (shares). Its events follow the same rules. Asset transfers are on the **asset** contract; share mint/burn/transfer events are on the **vault** contract.

### Asset vs Shares

Each vault operation usually involves:

1. **Asset contract** — `Transfer` for the underlying token
2. **Vault (shares) contract** — `Transfer` for share mint/burn

### Vault Public Methods (Direct Events)

| Method | Asset Events | Vault (Shares) Events |
| --- | --- | --- |
| `deposit_public_to_public` | `transfer_public_to_public(from, vault, assets)` | `_mint_to_public` → `Transfer(0x0, to, shares)` |
| `issue_public_to_public` | `transfer_public_to_public(from, vault, max_assets)` [+ optional refund] | `_mint_to_public` → `Transfer(0x0, to, shares)` |
| `withdraw_public_to_public` | `transfer_public_to_public(vault, to, assets)` | `_burn_public` → `Transfer(from, 0x0, shares)` |
| `redeem_public_to_public` | `transfer_public_to_public(vault, to, assets)` | `_burn_public` → `Transfer(from, 0x0, shares)` |

### Vault Settle Methods (Internal — Emit Share Events)

These are enqueued from private flows and emit share `Transfer` events when they run in public:

| Settle Method | Event | Reason |
| --- | --- | --- |
| `settle_deposit_public_to_private_internal` | `Transfer(0x0, PRIV, shares)` | Mint shares to private |
| `settle_deposit_private_to_private_internal` | `Transfer(0x0, PRIV, shares)` | Mint shares to private |
| `settle_deposit_public_to_private_exact_internal` | `Transfer(0x0, PRIV, max_shares)` | Mint shares to private |
| `settle_deposit_private_to_private_exact_internal` | `Transfer(0x0, PRIV, max_shares)` | Mint shares to private |
| `settle_issue_public_to_private_internal` | `Transfer(0x0, PRIV, shares)` | Mint shares to private |
| `settle_issue_private_to_private_exact_internal` | `Transfer(0x0, PRIV, shares)` | Mint shares to private |
| `settle_withdraw_public_to_private_internal` | Via `_burn_public` | Burn shares from public |
| `settle_withdraw_private_to_private_internal` | `Transfer(PRIV, 0x0, shares)` | Burn shares from private |
| `settle_withdraw_private_to_public_exact_internal` | `Transfer(PRIV, 0x0, shares)` | Burn shares from private |
| `settle_withdraw_private_to_private_exact_internal` | `Transfer(PRIV, 0x0, shares)` | Burn shares from private |
| `settle_redeem_private_to_public_internal` | `Transfer(PRIV, 0x0, shares)` | Burn shares from private |
| `settle_redeem_private_to_private_exact_internal` | `Transfer(PRIV, 0x0, shares)` | Burn shares from private |
| `settle_redeem_public_to_private_exact_internal` | Via `_burn_public` | Burn shares from public |
| `settle_deposit_private_to_public_internal` | Via `_mint_to_public` | Mint shares to public |
| `settle_issue_private_to_public_exact_internal` | Via `_mint_to_public` | Mint shares to public |

Asset events for private flows come from the underlying Token’s internal methods (`transfer_private_to_public`, `transfer_public_to_private`, etc.), which emit `PRIV` when the counterpart is private.

* * *

## Quick Reference: Event Patterns

| Operation | from | to |
| --- | --- | --- |
| Mint to public | `0x0` | recipient |
| Mint to private | `0x0` | `PRIV` |
| Public → public | sender | recipient |
| Public → private | sender | `PRIV` |
| Private → public | `PRIV` | recipient |
| Private → private | _(no event)_ | |
| Burn from public | sender | `0x0` |
| Burn from private | `PRIV` | `0x0` |

* * *

## Summary

- **Token:** Events are emitted at every public balance change (direct or via internal helpers). Private-only moves (`transfer_private_to_private`) emit nothing.
- **Vault:** Same logic for shares; asset events are on the asset contract, share events on the vault.
- **Magic value:** `PRIVATE_ADDRESS_MAGIC_VALUE` represents the private side so both invariants hold (supply and per-address balance) while keeping private counterparties hidden.
