Wallets
This page covers the main responsibilities of a wallet in the Aztec network.
Wallets are the applications through which users manage their accounts. Users rely on wallets to browse through their accounts, monitor their balances, and create new accounts. Wallets also store seed phrases and private keys, or interact with external keystores such as hardware wallets.
Wallets also provide an interface for dapps. Dapps may request access to see the user accounts, in order to show the state of those accounts in the context of the application, and request to send transactions from those accounts as the user interacts with the dapp.
In addition to these usual responsibilities, wallets in Aztec also need to track private state. This implies keeping a local database of all private notes encrypted for any of the user's accounts, so dapps and contracts can query the user's private state. Aztec wallets are also responsible for producing local proofs of execution for private functions.
Account setup
The first step for any wallet is to let the user set up their accounts. An account in Aztec is represented onchain by its corresponding account contract that the user must deploy to begin interacting with the network. This account contract dictates how transactions are authenticated and executed.
A wallet must support at least one specific account contract implementation, which means being able to deploy such a contract, as well as interacting with it when sending transactions. Code-wise, this requires implementing the AccountContract interface.
Note that users must be able to receive funds in Aztec before deploying their account. A wallet should let a user generate a deterministic complete address without having to interact with the network, so they can share it with others to receive funds. This requires that the wallet pins a specific contract implementation, its initialization arguments, a deployment salt, and the user's keys. These values yield a deterministic address, so when the account contract is actually deployed, it is available at the precalculated address. Once the account contract is deployed, the user can start sending transactions using it as the transaction origin.
Transaction lifecycle
Every transaction in Aztec is broadcast to the network as a zero-knowledge proof of correct execution, in order to preserve privacy. This means that transaction proofs are generated on the wallet and not on a remote node. This is one of the biggest differences with regard to EVM chain wallets.
A wallet is responsible for creating an execution request out of one or more function calls requested by a dapp. For example, a dapp may request a wallet to "invoke the transfer function on the contract at 0x1234 with the following arguments", in response to a user action. The wallet turns that into an execution request with the signed instructions to execute that function call from the user's account contract. In an ECDSA-based account, for instance, this is an execution request that encodes the function call in the entrypoint payload, and includes its ECDSA signature with the account's signing private key.
Once the execution request is created, the wallet is responsible for simulating and proving the execution of its private functions. The simulation yields an execution trace, which can be used to provide the user with a list of side effects of the private execution of the transaction. During this simulation, the wallet is responsible for providing data to the virtual machine, such as private notes, encryption keys, or nullifier secrets. This execution trace is fed into the prover, which returns a zero-knowledge proof that guarantees correct execution and hides all private information. The output of this process is a transaction object.
Private functions use a UTXO model, so their execution trace is determined entirely by the input notes. Since notes are immutable, simulation results match mined results exactly. However, a transaction may be dropped if it tries to consume a note that was nullified by another transaction first.
Public functions use an account model (like Ethereum), so their execution trace depends on chain state at inclusion time, which may differ from simulation.
Finally, the wallet sends the resulting transaction object, which includes the proof of execution, to an Aztec Node. The transaction is then broadcasted through the peer-to-peer network, to be eventually picked up by a sequencer and included in a block.
Authorizing actions
Account contracts in Aztec expose an interface for other contracts to validate whether an action is authorized by the account or not. For example, an application contract may want to transfer tokens on behalf of a user, in which case the token contract will check with the account contract whether the application is authorized to do so. These actions may be carried out in private or in public functions, and in transactions originated by the user or by someone else.
Wallets should manage these authorizations, prompting the user when they are requested by an application. Authorizations in private executions come in the form of auth witnesses, which are usually signatures over an identifier for an action. Applications can request the wallet to produce an auth witness via the createAuthWit call. In public functions, authorizations are pre-stored in the account contract storage, which is handled by a call to an internal function in the account contract implementation.
Key management
As in EVM-based chains, wallets are expected to manage user keys, or provide an interface to hardware wallets or alternative key stores. Keep in mind that in Aztec each account requires multiple key pairs: protocol keys (nullifier and incoming viewing keys) are mandated by the protocol and used for spending notes and decryption, whereas signing keys are dependent on the account contract implementation rolled out by the wallet. Should the account contract support it, wallets must provide the user with the means to rotate or recover their signing keys.
Due to limitations in the current architecture, protocol keys need to be available in the wallet software itself and cannot be delegated to an external keystore. This restriction may be lifted in a future release.
Recipient address management
Wallets are also expected to manage the public encryption keys of any recipients of local transactions. When creating an encrypted note for a recipient given their address, the wallet needs to provide their complete address. Recipients broadcast their complete addresses when deploying their account contracts, and wallets collect this information and save it in a local registry for easy access when needed.
Note that, in order to interact with a recipient who has not yet deployed their account contract (and thus not broadcasted their complete address), it must also be possible to manually add an entry to a wallet's local registry of complete addresses.
Private state
Wallets also store the user's private state. Aztec uses a note tagging system that allows users to efficiently discover notes that belong to them. When a note is created, the sender tags it with a value derived from a shared secret, allowing the recipient's wallet to query for relevant notes without attempting to decrypt every note on the network. Once discovered, notes are decrypted and added to the corresponding account's private state.
To discover notes from a sender, the wallet must first register that sender's address with the PXE. This allows the wallet to compute the shared secrets needed for note tagging. Wallets typically register senders when users add contacts or interact with new counterparties. Wallets must also scan for private state in blocks prior to the deployment of a user's account contract, since users may have received notes before deployment.
Private state can be encrypted and broadcast through the network, then committed to L1. While tags allow wallets to query the network for relevant notes, the tags themselves don't reveal the recipient - only the sender and recipient can compute and recognize them. This means wallets need to maintain a local database of their accounts' private state to answer queries efficiently.
Dapps may require access to the user's private state, in order to show information relevant to the current application. For instance, a dapp for a token may require access to the user's private notes in the token contract in order to display the user's balance. It is the responsibility of the wallet to require authorization from the user before disclosing private state to a dapp.
Account interface
The account interface is used for creating an execution request out of one or more function calls requested by a dapp, as well as creating an auth witness for a given message hash. Account contracts are expected to handle multiple function calls per transaction, since dapps may choose to batch multiple actions into a single request to the wallet.
/**
* Handler for interfacing with an account. Knows how to create transaction execution
* requests and authorize actions for its corresponding account.
*/
export interface AccountInterface extends EntrypointInterface, AuthWitnessProvider {
/** Returns the complete address for this account. */
getCompleteAddress(): CompleteAddress;
/** Returns the address for this account. */
getAddress(): AztecAddress;
/** Returns the chain id for this account */
getChainId(): Fr;
/** Returns the rollup version for this account */
getVersion(): Fr;
}
Source code: yarn-project/aztec.js/src/account/interface.ts#L6-L25