Pairs with: Per-Request Flows · Updates: Deployment Ceremony
The per-request architecture puts each AP settlement behind a multisig approval. If the multisig is pure-human, routine MM flows (dozens of subscriptions and redemptions per day, all within a few bps of NAV) pile up waiting on coordinating 3 humans to sign. SLAs slip. APs see friction. Secondary-market peg tracking degrades — which is the whole thing the per-request model was meant to solve.
The auto-signer pattern compresses this: one slot in the multisig is held by an automated service that signs iff pre-defined NAV-adjacency rules are satisfied. Normal flow drops from 3 human signatures to 2. Emergency/override path stays unchanged (pure human quorum).
This doc specifies the rules, the infrastructure, and the failure modes.
The contract does not know about the auto-signer. From the contract's perspective this is just a Gnosis Safe with N signers and a threshold. The auto-signer is off-chain infrastructure that happens to hold one of those signer keys. No contract changes required.
Recommended: 3-of-5 Gnosis Safe.
Signers:
Normal flow: Ops proposes tx → AutoSigner signs within seconds if in policy → 1 human countersigns → 3-of-5 met → execute.
Override flow: AutoSigner refused (price out of tolerance, source stale, or paused) → 3 humans sign → 3-of-5 met → execute. AutoSigner is bypassed, not blocking.
Emergency flow: AutoSigner key rotated out, or 4 humans reach quorum without it. System stays live.
AutoSigner only signs specific function selectors on specific contracts. Anything else, it refuses.
Allowed targets:
ARUSubscriptionManager.approveSubscription(bytes32, uint256)
ARUSubscriptionManager.approveRedemption(bytes32, uint256)
Refused:
setTreasuryAddress, setMinimumMintAmount, pauseSubscriptions, freeze, addAP, removeAP, overwriteRequest, etc.ARU directly (no minting or ownership ops)ARUPriceOracle (no price updates)changeThreshold, addOwner, removeOwner, swapOwner, setGuard)This is enforced at two layers:
AutoSigner evaluates each approval tx against three hard checks before signing:
Target contract: ARUSubscriptionManager
Function: approveSubscription | approveRedemption
Parameters: (id, pricePerARU) — both non-zero
Request state: not fulfilled, not expired, priceBound satisfied
Fetched from on-chain state at evaluation time. Stale data → refuse.
nav = median(Redstone, BlackRock IAU ref, internal model)
deviation_bps = |pricePerARU − nav| * 10000 / nav
allowed_bps = 100 // 1%, tunable by multisig-signed config tx
Tolerance defaults to 100 bps (1%). Wider for stablecoin-like assets, tighter for hard-commodity pegs where NAV is nearly deterministic.
Three NAV sources, median of three. All three must be:
If any source is stale or disagreeing, AutoSigner refuses. Falls to human quorum.
max_approvals_per_hour = 50
max_approvals_per_day = 500
max_total_usdc_per_day = $50M // sum of subscription USDC + redemption USDC
Beyond these, AutoSigner auto-pauses. Prevents a scenario where a subtle oracle bug routes many small-deviation-but-adversarial approvals through. Manual human unpause required.
Rules live in two places:
On-chain (immutable per deploy): the Safe signers, the Safe threshold, optionally a Safe Guard contract enforcing function-allowlist.
Off-chain (versioned, auditable): autosigner.yaml with:
yamlversion: 1
active: true
nav_sources:
- name: redstone
type: onchain_oracle
address: "0x..."
max_staleness_seconds: 43200
- name: blackrock_iau_ref
type: offchain_api
url: "https://..."
secret_env: BR_IAU_TOKEN
- name: internal_model
type: service
url: "https://nav.internal.aru/v1/price"
tolerance:
deviation_bps: 100
inter_source_max_divergence_bps: 500
rate_limits:
approvals_per_hour: 50
approvals_per_day: 500
total_usdc_per_day: 50000000
emergency:
paused: false
pause_reason: ""
Config changes require:
Stateless event-driven service, preferred deployment shape:
Cloud provider: AWS / GCP / Azure, OR OpenZeppelin Defender (see below)
Compute: Serverless (Lambda / Cloud Functions / Defender Autotasks)
Key storage: KMS HSM-backed; key never leaves HSM
Observability: Structured logs to SIEM + metrics to Prometheus + Slack alerts
Event source: Safe Transaction Service webhook
Evaluation: Pure function of (tx, nav_sources, config); no local state
Worth evaluating as a first-class option once the v2 contract ships. Defender provides the exact primitives AutoSigner needs, pre-audited by OZ:
Why Defender over self-hosted:
Why not Defender:
Recommendation: use Defender for v1 AutoSigner rollout, revisit self-host if we outgrow the service or need specific features it doesn't support. The stateless/pure-function design of AutoSigner means the Autotask can be lifted to self-hosted infra later with bounded migration cost.
Ops upgrade — schedule for after v2 contracts land and per-request flows stabilize. Not in the initial v2 launch path.
#multisig-ops, do nothing elseHardware-backed KMS with:
| Attack | Capability | Mitigation |
|---|---|---|
| AutoSigner key compromise | Attacker now holds 1-of-5 | Needs 2 more human signatures. Humans should apply extra scrutiny when AutoSigner signed first (cultural discipline) |
| NAV source poisoning | Attacker can't bypass multi-source median unless ≥2 sources are compromised | Multi-source + divergence check; admin rotates compromised source immediately |
| Replay / race | Same approval tx submitted twice | Safe's nonce + contract's fulfilled flag prevent effect; AutoSigner's rate limit also blocks |
| Slow oracle + NAV deviates | Stale source evaluates, AutoSigner signs at adversarial price | Strict staleness check + inter-source divergence; either triggers refusal |
| Admin-config tx disguised as approval | Calldata crafted to look like approval but target is unexpected | Function-selector allowlist at AutoSigner; optional Safe Guard as second layer |
| AutoSigner stuck / dead | New requests can't get automated sign | Fallback to 3 humans. AutoSigner is never a required dependency |
| Config drift (wrong tolerance) | AutoSigner signs too liberally | Config is git-committed, reviewed. Anomaly detection on signed-tx metadata. Humans notice bad patterns |
approve* functions, which AutoSigner can sign but not execute alone.priceBound. The contract rejects that regardless of AutoSigner's signature.MANAGER_ADMIN config change AutoSigner refuses.Commit publicly to APs:
| Condition | SLA |
|---|---|
| Request in policy (AutoSigner signs) + 1 human online | ≤ 30 minutes typical, ≤ 2 hours committed |
| Request requires human override | ≤ 4 business hours |
| Request during AutoSigner outage | ≤ 4 business hours (pure human quorum) |
| Emergency pause (any path) | Immediate on-chain via pause*, announced within 15 minutes |
Page on-call for any of:
autosigner.yaml config schema, version 1