---
# System prepended metadata

title: 'AZRC-1: Token Ramblings'

---

# AZRC-1: Token Ramblings

:::info
**Disclaimer**: 

Please note that any example token contract set out herein is provided solely for informational/academic purposes only and does not constitute any inducement to use, deploy and/or any confirmation of any token related to Aztec or otherwise. 

Any implementation and/or use of any such example token contract with an interface or any other infrastructure should be used in accordance with applicable laws and regulations, including without limitation any applicable sanctions laws, export control laws, securities laws, anti-money laundering laws and privacy laws. 

You expressly acknowledge that any example token contract herein is provided to you on an “as is basis” and “as available” basis without any warranties or conditions of merchantability, fitness for a particular purpose, satisfactory quality, or confirmation that any such example token contract has been audited.  

Please also refer to the terms of service available at: https://aztec.network/termsofservice 






:::


:::info
**Az**tec **R**equest for **C**omments 
:::

Make heavy use of `AuthWit`, see [Authentication Witness](https://docs.aztec.network/concepts/foundation/accounts/authwit) in the docs for more information on this.

:::info
In Aztec all accounts will be contract, e.g., smart wallets are the only wallets.
:::

## Simple Summary
A standard interface for tokens that have both a private and public part.

## Abstract

## Introduction
We propose this standard interface to have an easy to integrate API available for wallets and contracts to interact with tokens. 

It is loosly based on [ERC-20](https://eips.ethereum.org/EIPS/eip-20https://eips.ethereum.org/EIPS/eip-20) for the skeleton, with a focus on avoiding the requirement of an explicit on-contract approval by using `AuthWit` wallets, see appendix. 

There are two main reasons for avoiding approvals: 
1. They have led to a lot of pain
2. It is not intuitive how they would work in the private domain, as knowledge of pre-images is also required (simple approval is insufficient).

### Goal 

We want to support transfer flows similar to the ones outline below. Solid lines are calls, dashed lines static calls and dotted lines off-chain communication. 

1. Alice performs a transfer of funds, `from == msg_sender`.
2. Alice want to deposit into a DeFi contract. She will make a call to it, which will call the transfer function of the token contract, which calls back to her account contract using a static call and the auth witness is provided by Alice herself (on Alice's device).
3. Alice want her friend Bob to be make a transfer on her behalf. She prepares auth witness up front and sends it to Bob (along with pre-images for private transfers), all of which happens off-chain. Bob can then execute the action (transfer money to Bob) on behalf of Alice.

![](https://hackmd.io/_uploads/SyeiHcdA2.png)


## Specification

### AuthWit messages
Whenever `AuthWit` is used, a nullifier MUST be emitted. By including a nonce in the arguments we can simply emit the `message_hash` to ensure it cannot be replayed.

Messages for AuthWit MUST be computed as:
$$
msg = H\left(caller, address(token), selector, args\_hash \right)
$$

Where $caller$ should be `msg_sender()` to work as designated caller, ensuring that only $caller$ can "consume" the AuthWit. This is similar to the designated caller in [l1-l2-messages](https://docs.aztec.network/aztec/how-it-works/l1-l2-messaging#designated-caller).

Any `is_valid` call must be done as a static call to ensure that control flow is not passed along.

### Methods 

#### name (public, private, unconstrained)
Returns the name of the asset

#### symbol (public, private, unconstrained)
Returns the symbol for the asset (Dai as an example).

#### decimals (public, private, unconstrained)
Returns the number of decimals 

#### total_supply (public, unconstrained)
Returns the total supply of the asset. 
```rust
fn total_supply() -> u120;
```

#### balance_of_public (public, unconstrained)
Returns the public balance of an account with address `owner`.

```rust
fn balance_of_public(owner: AztecAddress) -> u120;
```

#### balance_of_private (private, unconstrained)
Returns the private balance of an account with address `owner`. Will only produce a meaningful result for someone who holds the privacy keys of `owner`. 

```rust
fn balance_of_private(owner: AztecAddress) -> u120;
```

#### transfer_public (public)
Transfers `amount` of tokens from the address `from` to address `to`, using `nonce` for `AuthWit`. 

- MUST throw if not `from == msg_sender()` *AND* no valid `AuthWit` by `from` is provied.
- MUST throw if `from` has insufficient balance to cover `amount`
- MUST emit nullifier when consuming `AuthWit`
- SHOULD throw if `nonce = 0` and `from == msg_sender()`

```rust
#[aztec(public)]
fn transfer_public(
    from: AztecAddress,
    to: AztecAddress,
    amount: Field,
    nonce: Field, // used if done onbehalf of other account, otherwise 0
) -> bool;
```

#### transfer (private)
Transfers `amount` of tokens from the address `from` to address `to`, using `nonce` for `AuthWit`. 

Since done in private and consuming commitments, the caller is required to know the preimages for those commitments.


- MUST throw if not `from == msg_sender()` *AND* no valid `AuthWit` by `from` is provied.
- MUST throw if `from` has insufficient balance to cover `amount`
- MUST emit nullifier when consuming `AuthWit`
- SHOULD throw if `nonce = 0` and `from == msg_sender()`

```rust
#[aztec(private)]
fn transfer(
    from: AztecAddress,
    to: AztecAddress,
    amount: Field,
    nonce: Field, // used if done onbehalf of other account, otherwise 0
) -> bool;
```

#### shield (public)
Reduce the public balance of `from` by `amount` of tokens, and creates a note that can be claimed through `redeem_shield()` to mint tokens in private. Claim can only be executed with knowledge of `secret` for `secret_hash = H(secret)`. 

- MUST throw if not `from == msg_sender()` *AND* no valid `AuthWit` by `from` is provied.
- MUST throw if `from` has insufficient balance to cover `amount`
- MUST emit nullifier when consuming `AuthWit`
- MUST create a note for token minting
- SHOULD throw if `nonce = 0` and `from == msg_sender()`

```rust
#[aztec(public)]
fn shield(
    from: AztecAddress,
    amount: Field,
    secret_hash: Field,
    nonce: Field,
) -> bool
```

#### redeem_shield (private)
Redeems a note `(amount, secret)` created from `shield` and mints notes of equal value to the `to` address.

- MUST throw if no note to consume
- MUST create new note(s) for `to` of `amount` tokens.

```rust
#[aztec(private)]
fn transfer(
    to: AztecAddress,
    amount: Field,
    secret: Field,
) -> bool;
```

#### unshield (private)
Destroy notes of value `amount` from the `from` address and increase the `to` address' public balance by `amount`. 

- MUST throw if not `from == msg_sender()` *AND* no valid `AuthWit` by `from` is provied.
- MUST throw if `from` has insufficient balance to cover `amount`
- MUST emit nullifier when consuming `AuthWit`
- MUST create a note for token minting
- SHOULD throw if `nonce = 0` and `from == msg_sender()`

```rust
#[aztec(private)]
fn unshield(
    from: AztecAddress,
    to: AztecAddress,
    amount: Field,
    nonce: Field,
) -> bool;
```


## Implementation

An initial implementation can be seen at [aztec-packages/pull/2069](https://github.com/AztecProtocol/aztec-packages/pull/2069/files#diff-cf7e7354bd056d8f8e2eae98fd7d3672faf1b63f563eead38426f797ac735d19).

## How is this different to approvals?
Technically, this is quite similar to approvals if approvals were for a specific action instead of an amount. 

Also, the action taken, is a `transfer` it is not something different to that, so it should be simpler for non-sophisticated users to realise what is happening, they are "approving" a transfer, not signing an approval in the current sense. 

For private functions especially, a lot more data is required than just the `AuthWit` due to pre-images etc.

## Rationale

We propose to use the same functions for handling transfers on behalf of self and others for the simple reason that it makes our [ACIR](https://docs.aztec.network/concepts/advanced/acir_simulator) much smaller and make all transfers follow the same structure so users 


## Appendix 

### Application Access Control
For private transactions, an application DO NOT have unrestricted access to the notes owned by the user (e.g., unless user have given permission only public data is leaked to dapp). This means that phising or scam sites that intends to steal funds will need to be given explicit access from the user to read his notes before they are able to steal private funds, even if provided with a valid authenticaion witness.

For other access control, such as minters and blacklists, see blacklist implementation a bit further down.

### Early implementation
For building tests and figuring out what makes sense, we have a partial implementation at https://github.com/AztecProtocol/aztec-packages/blob/master/yarn-project/noir-contracts/src/contracts/token_contract/src/main.nr, which includes the most parts, but not all the getters atm.

### Early implementation with blacklist
Since many assets such as centralized stable coins use a blacklist, an example implementation of such is available in https://github.com/AztecProtocol/aztec-packages/blob/20259393b9af9d2c8de6e7cf3d517911063e1bf0/yarn-project/noir-contracts/src/contracts/token_blacklist_contract/src/main.nr

## Parts to be designed/discussed:

### Transient Escrowing
When we have flows such as user A sends funds into contract B which forwards it into C after some accounting (example could be some Yearn vault etc), contract B is temporarily escrowing the assets. 

When doing such, we will run into the issue that contract B is owning the asset briefly.

Generally, we have that a note have:
- An owner in the *protocol domain*, specifying provides the nullifier secret for spending the note. This information is part of the privacy keys; 
- An owner in the *application domain*, specifying application specific rules controlling when the note can be nullified. This will often be only allowing you to spend notes in a struct like `mapping(spender => amount)` for `spender == msg_sender`.

:::info
Currently the nullifier secret is a private key matching a public key. So you gotta know the private key as part of nullifying.
:::

To consume the note, you need to know the secrets of the *protocol domain* owner, and **be** the *application domain* owner or approved by them (depending on application logic).

In most cases, these two owners are the same, since it provides an easy flow. Alice sends money to Bob, so Bob is both the application and protocol domain owner. When Bobs want to spend the notes it is very easy for him.

However, for the case where we are sending funds to be owned briefly by a contract, it creates some issues! Contracts don't really control private/public keys like users does, so what to specify as the *protocol domain* owner? 

- If the contract is to have only one actor do this, we could just let that actor decide it, or have the user specify a key when deploying the contract :shrug: 
- If it is a shared contract (such as Uniswap etc) that many people uses, it gets more tricky. 
    - If we are specifying one key that everyone will know, it could break some privacy assumptions since everyone can see the amount deposited. In some cases this is not an issue since because it might move into the public domain anyway and they it is leak nonetheless.
    - In other cases, we might want to let the sender specify the *protocol domain* owner as themselves. Such a case could be when you want to use a "split" contract to do a donation where some of the funds go to a shared pool. 

By allowing callers to specify we provide much larger flexibility, as contract can now be used even when passing along assets. But it also upon up for more cases of users loosing access to their funds. 

Say that Alice transfer funds to Bob, but by mistake is setting Charlie as the *protocol domain* owner. Bob now cannot spend the funds without help from Charlie. We have seen plenty users having issue with doing transfers `to` someone on Ethereum, so adding another argument that can be messed up might not be desired. Nevertheless, much of this should be handled at the wallet layer.

Similarly, contract that wish to use the features could end up practically burning your funds if they specify the *protocol domain* owner to be an entity without a matching private key.

:::info
I think adding the extra argument have enough value for it to be worth it. But it is up for discussion. Example:
```rust
#[aztec(private)]
fn transfer(
    from: AztecAddress, 
    to: AztecAddress,
    to_secret_provider: AztecAddress, 
    amount: Field,
    nonce: Field, // used if done onbehalf of other account, otherwise 0
) -> bool;
```
:::