|
1 | 1 | # MultiToken Contract |
2 | 2 |
|
3 | | -Initial scaffolding for the `MultiToken` contract. It currently exposes only an empty public initializer (`constructor`) and is intended as the starting point for further development. |
| 3 | +The `MultiToken` contract implements an ERC-1155-like multi-token with Aztec-specific privacy extensions. A single contract holds many fungible token ids, each with its own independent balance, and every balance can live in either a private or a public domain with seamless moves between the two. |
| 4 | + |
| 5 | +Compared to the single-asset [`Token`](../token_contract/README.md), every balance-changing function takes an extra `id: Field` selecting the token, there is no `decimals` and no `total_supply`, and the on-chain event is `TransferSingle` (ERC-1155 naming) instead of `Transfer`. |
| 6 | + |
| 7 | +## ARC-403: Authorization Hook |
| 8 | + |
| 9 | +Like `Token`, this contract implements the optional ARC-403 authorization hook: when an `auth_contract` is configured, every transfer and burn calls it before mutating balances, and the operation reverts if the hook reverts. If `auth_contract` is the zero address, the hook is disabled and the token behaves as a plain multi-token. The interface is **id-bearing** — the hook receives the token id so policies can differ per id: |
| 10 | + |
| 11 | +- `authorize_private(from, id, amount, selector)` — called by private-context functions. |
| 12 | +- `authorize_public(from, id, amount, selector)` — called by public-context functions. |
| 13 | + |
| 14 | +| Function | Hook called | |
| 15 | +|----------|-------------| |
| 16 | +| `transfer_private_to_private` | `authorize_private` | |
| 17 | +| `transfer_private_to_public` | `authorize_private` | |
| 18 | +| `transfer_private_to_commitment` | `authorize_private` | |
| 19 | +| `transfer_public_to_private` | `authorize_private` | |
| 20 | +| `transfer_public_to_public` | `authorize_public` | |
| 21 | +| `transfer_public_to_commitment` | `authorize_public` | |
| 22 | +| `burn_private` | `authorize_private` | |
| 23 | +| `burn_public` | `authorize_public` | |
| 24 | + |
| 25 | +Mints (`mint_to_private`, `mint_to_public`, `mint_to_commitment`) are **not** hooked — minting is already gated by the `minter` address set at construction. |
| 26 | + |
| 27 | +## TransferSingle Events |
| 28 | + |
| 29 | +A public `TransferSingle { from, to, id, amount }` event is emitted only on operations whose token id is already revealed on-chain (any public-balance write or commitment completion). Fully-private operations emit nothing, since an id-bearing event would leak the token id. |
| 30 | + |
| 31 | +| Operation | Event Pattern | |
| 32 | +|-----------|---------------| |
| 33 | +| Mint to public | `TransferSingle(0x0, recipient, id, amount)` | |
| 34 | +| Mint to commitment | `TransferSingle(0x0, PRIVATE_ADDRESS, id, amount)` | |
| 35 | +| Burn from public | `TransferSingle(from, 0x0, id, amount)` | |
| 36 | +| Public-to-public | `TransferSingle(from, to, id, amount)` | |
| 37 | +| Public-to-commitment | `TransferSingle(from, PRIVATE_ADDRESS, id, amount)` | |
| 38 | +| Public-to-private | `TransferSingle(from, PRIVATE_ADDRESS, id, amount)` | |
| 39 | +| Private-to-public | `TransferSingle(PRIVATE_ADDRESS, to, id, amount)` | |
| 40 | +| Mint to private / Burn from private | _(no public events)_ | |
| 41 | +| Private-to-private / Private-to-commitment | _(no public events)_ | |
| 42 | + |
| 43 | +**Sentinel values:** `0x0` denotes mint origin (`from`) or burn destination (`to`), following ERC-1155. `PRIVATE_ADDRESS` (sha224 of `"PRIVATE_ADDRESS"`) denotes the private side of a balance change when the counterpart cannot be revealed. |
| 44 | + |
| 45 | +## Storage Fields |
| 46 | + |
| 47 | +- `name: FieldCompressedString`: Token collection name (compressed). |
| 48 | +- `symbol: FieldCompressedString`: Token collection symbol (compressed). |
| 49 | +- `private_balances: Owned<MultiBalanceSet>`: A single private note set. Each `MultiTokenNote` self-describes its token id; per-owner scoping is `private_balances.at(owner)`, and per-id operations select notes where `token_id == id`. |
| 50 | +- `public_balances: Map<Field, Map<AztecAddress, u128>>`: Public balances keyed by token id, then owner. |
| 51 | +- `minter: AztecAddress`: Account permitted to mint any token id. |
| 52 | +- `auth_contract: AztecAddress`: ARC-403 authorization contract address (zero address disables the hook). |
| 53 | + |
| 54 | +## Function Reference |
| 55 | + |
| 56 | +All addresses are `AztecAddress`; `id` is a `Field`, `amount` is a `u128`, and `nonce` (used for authwit) is a `Field`. |
| 57 | + |
| 58 | +### Initializer |
| 59 | + |
| 60 | +- `constructor_with_minter(name: FieldCompressedString, symbol: FieldCompressedString, minter, auth_contract)` — Initializes the multi-token with a minter and an optional ARC-403 auth contract. |
| 61 | + |
| 62 | +### Private Functions |
| 63 | + |
| 64 | +- `transfer_private_to_private(from, to, id, amount, nonce)` — Moves `amount` of `id` between private balances. Fully private (no event, no public effect). |
| 65 | +- `transfer_private_to_public(from, to, id, amount, nonce)` — Spends private notes and enqueues a public credit to `to`. |
| 66 | +- `transfer_private_to_commitment(from, id, commitment, amount, nonce)` — Spends private notes and completes an already-initialized commitment with `(id, amount)`. |
| 67 | +- `transfer_public_to_private(from, to, id, amount, nonce)` — Enqueues a public debit of `from` and emits a private note to `to`. |
| 68 | +- `initialize_transfer_commitment(to, completer) -> Field` — Creates a partial note (privacy entrance) to be completed by later transfers/mints. Id-agnostic: the completer binds `id` and `amount`. |
| 69 | +- `mint_to_private(to, id, amount)` — Minter mints `id` into a private balance. Fully private. |
| 70 | +- `burn_private(from, id, amount, nonce)` — Burns `id` from a private balance. Fully private. |
| 71 | + |
| 72 | +### Public Functions |
| 73 | + |
| 74 | +- `transfer_public_to_public(from, to, id, amount, nonce)` — Moves `amount` of `id` between public balances. |
| 75 | +- `transfer_public_to_commitment(from, id, commitment, amount, nonce)` — Debits `from`'s public balance and completes a commitment prepared by `initialize_transfer_commitment`. |
| 76 | +- `mint_to_public(to, id, amount)` — Minter mints `id` into a public balance. |
| 77 | +- `mint_to_commitment(id, commitment, amount)` — Minter finalizes a mint into a commitment. |
| 78 | +- `burn_public(from, id, amount, nonce)` — Burns `id` from a public balance. |
| 79 | + |
| 80 | +### View Functions |
| 81 | + |
| 82 | +- `balance_of_public(owner, id) -> u128` — Public balance of `owner` for `id`. |
| 83 | +- `name() -> FieldCompressedString` — Token collection name. |
| 84 | +- `symbol() -> FieldCompressedString` — Token collection symbol. |
| 85 | +- `get_minter() -> AztecAddress` — Authorized minter address. |
| 86 | +- `get_auth_contract() -> AztecAddress` — ARC-403 auth contract address (zero if disabled). |
| 87 | + |
| 88 | +### Utility Functions |
| 89 | + |
| 90 | +- `balance_of_private(owner, id) -> u128` — Off-chain helper that pages through the owner's notes filtered by `id` and sums their values. No on-chain or proving cost, and no fixed cap. |
0 commit comments