Skip to main content

Advanced contract account patterns

Use these patterns to extend a basic contract account with guardrails. Put the logic in __check_auth or a helper it calls, and test both the allow and deny paths. Treat signer rules as “who can act” and policy rules as “under what conditions.”

Spend limits

  • Store a limit and a running total in instance storage (for example, outflows over the last 24 hours).
  • Derive a window key from the ledger timestamp (for example, day = timestamp / 86_400) and reset the total when the window changes.
  • On each request, check the remaining allowance; if the amount would exceed it, reject and emit an event with the attempted amount and remaining allowance.

See the Complex Account example for a reference implementation with weighted signers and limits.

Allow lists

  • Keep allowed destination addresses or contract IDs in storage.
  • Inspect both the root invocation and any subinvocations in auth_context before approving so nested calls cannot bypass the list.

Policy signers

  • Define one or more policy signer roles in storage. A policy signer exists to approve or deny specific actions (for example, an auditor key, a guardian key, or a service that enforces spending rules).
  • For high-risk contract calls (e.g. large transfers, changing signers), require the end user plus a policy signer; reject if either is missing.
  • Separate roles (admin vs. standard) so daily use can be delegated while upgrades or recovery stay with admins.

Time rules

  • Block execution until a specific ledger timestamp, or add a cooldown after actions like key rotation or large transfers.
  • Store the earliest allowed timestamp in instance storage; compare it against the current ledger timestamp in __check_auth.

Session keys

  • Generate a short-lived session key (for example, a p256 key created client-side and kept in secure browser storage) and limit it to one function plus a maximum amount.
  • Store a record for the session key with its expiry, allowed scope, and remaining allowance; reduce the allowance on each authorized call.
  • Reject if the session key is unknown, expired, out of allowance, or used outside its allowed scope.

External policy contracts

  • Offload specific checks (deny lists, time windows, device posture) to a dedicated policy contract; pass context into __check_auth and require the policy contract to approve before returning success.
  • Keep the policy interface minimal (for example, fn approve(auth_context) -> bool) so you can swap policies without changing your account.

Where to go next