A custom Aztec account contract that uses password-based authentication instead of traditional signature-based authentication. This example demonstrates how to implement a custom account contract with the Aztec protocol.
This project implements a password-protected account contract for Aztec, showcasing how to create custom authentication logic for account contracts. Instead of using cryptographic signatures, transactions are authorized using a password hashed with Poseidon2.
- Password-based Authentication: Uses Poseidon2 hash for secure password verification
- Custom Account Entrypoint: Implements a custom entrypoint interface for transaction execution
- Fee Payment Support: Supports multiple fee payment methods (external, pre-existing FeeJuice, FeeJuice with claim)
- Authorization Witnesses: Implements authwit verification for cross-contract calls
- Cancellable Transactions: Optional transaction cancellation through nullifiers
- TypeScript Integration: Complete TypeScript SDK for deployment and interaction
The main contract implements:
- constructor(password: Field): Initializes the account with a hashed password
- entrypoint(...): Main entrypoint for executing transactions with password authentication
- verify_private_authwit(...): Verifies authorization witnesses for cross-contract calls
- lookup_validity(...): Unconstrained function to check authwit validity
struct Storage<Context> {
hashed_password: PublicImmutable<Field, Context>,
}The contract stores only the Poseidon2 hash of the password in public state.
The AccountActions module provides:
- Transaction entrypoint logic with fee payment handling
- Authorization witness verification
- Support for cancellable transactions via nullifiers
Implements the AccountContract interface for easy deployment:
const passwordAccountContract = new PasswordAccountContract(
new Fr(your_password)
);Provides the account interface for creating transactions:
const accountInterface = new PasswordAccountInterface(
authWitnessProvider,
address,
chainInfo,
password
);Handles transaction construction with custom entrypoint parameters:
const entrypoint = new PasswordAccountEntrypoint(
address,
auth,
password,
chainId,
version
);Compile the Noir contract:
aztec compileInstall TypeScript dependencies:
yarn installStart the local network:
aztec start --local-networkDeploy the account contract to the local network:
npx tsx deploy-account-contract.ts- The password is hashed using Poseidon2 before storage
- Password is required for every transaction (no caching)
- Password is included in transaction data (encrypted in private state)
- This is a demonstration contract - production use should consider additional security measures
- Consider using signature-based accounts for most production use cases
When implementing custom account contracts in Aztec, be aware of these critical points:
This is the most important gotcha: In Aztec, all transaction execution begins in the private context, even if your contract only has public functions. The account contract's entrypoint function always executes in private first.
- Your
entrypointfunction must be aprivateorunconstrained privatefunction - Even when calling public functions on other contracts, the call originates from private execution
- Authentication logic in the entrypoint runs in the private context
- If you need to validate anything on-chain, you must enqueue public calls and handle them accordingly
- Never store passwords in plain text: Always hash sensitive data before storage (like we do with Poseidon2)
- The
hashed_passwordis stored inPublicImmutablestorage, meaning it's visible on-chain but cannot be changed - Consider whether your authentication secret should be changeable (would require mutable storage)
- The entrypoint must match the expected signature for account contracts
- It receives the payload (functions to call) and fee payment options
- Private state is encrypted and only visible to those with the viewing key
- Public state is visible to everyone on-chain
- Choose storage types carefully:
PublicImmutable,PublicMutable,PrivateImmutable,PrivateMutable,PrivateSet, etc. - Changing storage types after deployment requires a new contract deployment
- Account contracts need TypeScript integration for proper transaction construction
- You must implement the
AccountContract,AccountInterface, and custom entrypoint classes - The entrypoint class handles encoding your authentication mechanism into the transaction payload
- Mismatches between Noir and TypeScript implementations will cause authentication failures
- Private execution errors can be harder to debug since execution details aren't always visible
- Test thoroughly with different fee payment methods
- Ensure your authentication mechanism works for both direct calls and authwit flows
- Account contracts are responsible for paying transaction fees
- You must handle the fee payment method selection properly
- Failed fee payments will cause the entire transaction to fail
- Consider how users will fund their account contracts with Fee Asset
This example is compatible with Aztec v4.1.0-rc.2.
To set this version:
aztec-up 4.1.0-rc.2- aztec: v4.1.0-rc.2
- @aztec/aztec.js: 4.1.0-rc.2
- @aztec/accounts: 4.1.0-rc.2
- @aztec/stdlib: 4.1.0-rc.2
- @aztec/entrypoints: Included in aztec.js
account-contract/
├── Nargo.toml # Noir project configuration
├── package.json # Node.js dependencies and scripts
├── src/
│ ├── main.nr # Main contract implementation
│ └── account_actions.nr # Account action handlers
└── ts/
├── deploy-account-contract.ts # Deployment script
├── password-account-entrypoint.ts # TypeScript entrypoint implementation
└── password-account-contract-artifact.ts # Contract artifact loader
- Aztec Account Contracts
- Account Abstraction in Aztec
- Aztec Documentation
- Noir Language Documentation
- This is an educational example demonstrating custom account contract patterns
- For production use, consider using the standard ECDSA or Schnorr signature-based accounts
- The password-based approach trades cryptographic security guarantees for simplicity