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_contextbefore 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_authand 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
- See these patterns applied in the contract account examples.
- Explore reference implementations and libraries:
Guides in this category:
📄️ Smart wallets
Smart wallets are contract accounts that act as user wallets. They hold assets and enforce authorization in check_auth instead of a single secret key. Passkeys (WebAuthn) are common, but you can also use Ed25519 keys, policy signers, session keys or anything the contract can verify.
📄️ Advanced contract account patterns
Layer spend limits, allow lists, time rules, and other guardrails onto contract accounts.
📄️ Contract account examples
Real projects that showcase policy signers, passkeys, and contract-account UX on Stellar.