Write a Custom Private Escrow Contract
Create or adapt Aztec private escrow contracts. Default to the scaffolded OTC atomic onchain settlement, then modify storage, roles, lifecycle, token primitives, and SDK helpers for the requested protocol.
Load References As Needed
references/noir-private-state-patterns.md- contract-owned private state and mutable lifecycle notes.references/role-restriction-patterns.md- caller-sampled role secrets,RoleAdded, and pseudonym checks.references/token-primitive-adapters.md- selected-token private transfer and partial-note primitives.references/sensitive-term-commitments.md- commit-onchain/deliver-offchain sensitive terms.../scaffold-escrow-project/references/design-intake.md- phase/timing/config-state confirmation.../scaffold-escrow-project/references/lifecycle-phases.md- phase graph and transition invariants.../scaffold-escrow-project/references/secret-contracts.md- deployment and registration model.../scaffold-escrow-project/references/manifest-schema.md- participant handoff fields.
Use Aztec and Noir companion guidance together before writing or reviewing contract behavior.
Intake Rule
Before writing a new escrow contract or changing lifecycle/config/state structure, load ../scaffold-escrow-project/references/design-intake.md. Confirm phase set, timing windows, ambiguous config/state fields, and OrderFilled payload. If the user declines structured planning, continue with explicit conservative assumptions.
Canonical Files
The real scaffold files live under ../scaffold-escrow-project/templates/project/packages/contracts/src/:
main.nr- default OTC escrow contract.types/config_note.nr- immutable terms.types/state_note.nr- mutable lifecycle phase.
Template notes:
templates/contract-template.md- adaptation guide for the real contract file.templates/config-note-template.md- config rules.templates/state-note-template.md- state rules.templates/order-filled-event-template.md- fill receipt event rules.
Documentation Style
Document generated Noir and TypeScript code aggressively. Above every generated function, entrypoint, helper, class method, and exported type helper, use full doc comments with a description, blank line, @param for each parameter, and @returns for non-void returns:
/**
* Description here.
*
* @param x - Parameter description.
* @returns Return value description.
*/
Inside longer functions, mark each logical phase with step comments, for example // Step 1: Verify caller role pseudonym and // Step 2: Move escrow state forward. Comments should explain protocol intent, privacy assumptions, and state/token flow, not restate obvious syntax.
Non-Negotiables
- Target Aztec
5.1.0and poseidonv0.3.0. - Use
SinglePrivateImmutablefor immutable config. - Use
u128for token amounts. ConfigNoteis immutable terms;StateNoteis all phase/cancel/fill/timer/runtime role state.- Do not add manual note
ownerfields or note randomness toConfigNote/StateNote. - Use caller-sampled role secrets: hash
[caller.to_field(), role_secret], store only the pseudonym, emitRoleAdded { secret }to the caller. - Atomic one-shot fills are open to any filler satisfying settlement terms; bind taker/filler only for
ACCEPTED, delayed settlement, allowlists, or explicit role-restricted phases. - Do not add custom order-level funding/fill nullifiers by default. Use constructor funding and
StateNoteterminal phases. - Emit
OrderFilled { filled: true }toself.addresswithMessageDelivery::onchain_constrained()on every successful fill; add extra payload fields only when intake explicitly confirms event-carried data. - Use
MessageDelivery::onchain_constrained()for contract-owned notes and escrow-addressed fill events; useMessageDelivery::onchain_unconstrained()forRoleAddedsent to the caller. - Token calls are adapter-specific. Inspect the selected binding/source before changing concrete constructor-funding/refund/payout/commitment calls.
- For atomic onchain settlement, complete maker receive-side partial notes from escrow-owned funds, with escrow as filler.
- Use anchor block timestamp for deadline checks.
- Generated tests use
EmbeddedWallet.create(node, { ephemeral: true, pxe: { proverEnabled } }); defaultproverEnabled = false.