Skip to main content

How to use authentication witnesses (authwit)

This page assumes you have authwit set up correctly in your contract. To learn how to do that, go here.

For an introduction to authentication witnesses on Aztec, read this explainer.

Import libraries

These are all the libraries you might need for using authwits in Aztec.js:

import {
computeAuthWitMessageHash,
computeInnerAuthWitHash,
computeOuterAuthWitHash,
} from "@aztec/aztec.js";

You may not need all of these.

Publicly deploy accounts

note

This is only required if you are using authwits in public

If you are using public authwit (ie using assert_current_call_valid_authwit_public in your contract), you will need to deploy the following accounts publicly:

  1. The account that is giving permission to an account to act on behalf of it (authwit giver)
  2. The account that does the action (authwit receiver)

Here is an example implementation:

public_deploy_accounts
export async function publicDeployAccounts(sender: Wallet, accountsToDeploy: Wallet[]) {
const accountAddressesToDeploy = accountsToDeploy.map(a => a.getAddress());
const instances = await Promise.all(accountAddressesToDeploy.map(account => sender.getContractInstance(account)));
const batch = new BatchCall(sender, [
(await registerContractClass(sender, SchnorrAccountContractArtifact)).request(),
...instances.map(instance => deployInstance(sender, instance!).request()),
]);
await batch.send().wait();
}
Source code: yarn-project/end-to-end/src/fixtures/utils.ts#L474-L484

You would then call this like so:

public_deploy_accounts
await publicDeployAccounts(wallets[0], wallets.slice(0, 2));
Source code: yarn-project/end-to-end/src/e2e_authwit.test.ts#L23-L25

Define the action

When creating an authwit, you will need to pass the authwit giver, the authwit receiver (who will perform the action), and the action that is being authorized. The action can be a smart contract function call, or alternatively, arbitrary data.

When the action is a function call

You can define the action like this:

authwit_computeAuthWitMessageHash
const action = asset
.withWallet(wallets[1])
.methods.transfer(wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce);
Source code: yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts#L52-L56

In this example,

  • asset refers to a token contract
  • withWallet(wallets[1]) is specifying the authwit receiver (wallets[1]) will do this action
  • .methods.transfer() is specifying that the action is calling the transfer method on the token contract
  • (wallets[0].getAddress(), wallets[1].getAddress(), amount, nonce); are the args of this method - it will send the amount from wallets[0] to wallets[1]

Arbitrary message

You can hash your own authwit message by creating an inner hash with the data, like this:

compute_inner_authwit_hash
const innerHash = computeInnerAuthWitHash([Fr.fromString('0xdead')]);
Source code: yarn-project/end-to-end/src/e2e_authwit.test.ts#L45-L47

Then create the outer hash by hashing the inner hash with the authwit receiver address, chainId, and version:

compute_outer_authwit_hash

const intent = { consumer: auth.address, innerHash };
Source code: yarn-project/end-to-end/src/e2e_authwit.test.ts#L48-L51

Create the authwit

There are slightly different interfaces depending on whether your contract is checking the authwit in private or public.

Public authwits are stored in the account contract and batched with the authwit action call, so a user must send a transaction to update their account contract, authorizing an action before the authorized contract's public call will succeed.

Private execution uses oracles and are executed locally by the PXE, so the authwit needs to be created by the authwit giver and then added to the authwit receiver's PXE.

Private

This is expected to be used alongside private authwits in Aztec.nr contract.

Create a private authwit like this:

create_authwit
const witness = await wallets[0].createAuthWit({ caller: wallets[1].getAddress(), action });
Source code: yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts#L57-L59

In this example,

  • wallets[0] is the authwit giver
  • wallets[1] is the authwit reciever and caller of the function
  • action was defined previously

If you created an artbitrary message, you can create the authwit by replacing these params with the outer hash:

compute_outer_authwit_hash

const intent = { consumer: auth.address, innerHash };
Source code: yarn-project/end-to-end/src/e2e_authwit.test.ts#L48-L51

Then add it to the wallet of the authwit receiver (the caller of the function):

add_authwit
await wallets[1].addAuthWitness(witness);
Source code: yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_private.test.ts#L60-L62

Public

This is expected to be used alongside public authwits in Aztec.nr contract.

Set a public authwit like this:

set_public_authwit
await wallets[0].setPublicAuthWit({ caller: wallets[1].getAddress(), action }, true).send().wait();
Source code: yarn-project/end-to-end/src/e2e_blacklist_token_contract/transfer_public.test.ts#L115-L117

Remember it is a transaction and calls a method in the account contract. In this example,

  • wallets[0] is the authwit giver
  • wallets[1] is the authwit reciever and caller of the function
  • action was defined previously
  • true sets the authorized boolean (false would revoke this authwit)

If you created an arbitrary message, you would replace the first param struct with the outer hash:

set_public_authwit
await wallets[0].setPublicAuthWit(intent, true).send().wait();
Source code: yarn-project/end-to-end/src/e2e_authwit.test.ts#L163-L165

Further reading