Protocol

Scopes, payers & phases

After authentication returns an actorId, authorization is a scope check against the action. Sender and payer sign different domains. Calls run in phases with independent commit boundaries.

Actor scopes

The scope byte in actor_config is a bitmask of grants. 0x00 means unrestricted (admin): every context is allowed, and config-change / lock / delegation authority is exactly that predicate. Any non-zero value grants only the bits that are set. Reads are fail-closed: unknown bits grant nothing.

ConceptMeaning
SENDER · 0x01Ungated initiation via sender_auth. May originate calls to any call.to. Operational unless combined with POLICY (also satisfies ERC-1271).
POLICY · 0x02Gated initiation via sender_auth. Calls must target the actor’s policy_manager. Never operational.
NONCE · 0x04Restricted actors may use sequenced nonce_keys (sender-context). Without it, only NONCE_KEY_MAX (nonceless).
SELF_PAYER · 0x08Pay the account’s own gas when payer == sender (self-pay).
SPONSOR_PAYER · 0x10Act as payer_auth for a different sender (payer != sender).

Operational authority (no SIGNER bit)

An actor is operational when admin || (SENDER && !POLICY). ERC-1271 verifySignature() is authorized for any operational actor. Signing is not a separate grant: an operational key can already call approve / transfer, so a raw hash signature grants nothing new. A POLICY actor is never operational and must not sign raw hashes (that would escape its gate). Scoped signing lives at the account layer (approved-hash / approve-typed-data) driven by a policy key.

What each action requires

  • sender_auth: admin, or SENDER, or POLICY. When POLICY is set, every call is gated to policy_manager regardless of SENDER (wallets SHOULD NOT set SENDER | POLICY).
  • Self-pay: admin or SELF_PAYER on the actor that authorizes payment.
  • Sponsored payer_auth: admin or SPONSOR_PAYER on the payer-resolved actor.
  • Config / lock / delegation: admin only (scope == 0x00).
  • verifySignature(): operational (admin, or SENDER without POLICY).

POLICY | SELF_PAYER, POLICY | NONCE, and POLICY | SPONSOR_PAYER compose (SPONSOR_PAYER is not gated by the policy manager). Combinations are stored verbatim; semantics are checked at use. See Policies and session keys.

Signing hashes

Sender and payer use different type bytes so a signature from one role cannot be replayed as the other. Both hashes cover the same field list through payer, and both exclude sender_auth and payer_auth (auth blobs are malleable / re-signable on fee bumps).

Sender signature hashtext
keccak256(AA_TX_TYPE || rlp([
  chain_id, sender, nonce_key, nonce_sequence, expiry,
  max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
  account_changes, calls, metadata,
  payer
]))
Payer signature hashtext
keccak256(AA_PAYER_TYPE || rlp([
  chain_id, sender, nonce_key, nonce_sequence, expiry,
  max_priority_fee_per_gas, max_fee_per_gas, gas_limit,
  account_changes, calls, metadata,
  payer
]))
  • AA_TX_TYPE = 0x79, AA_PAYER_TYPE = 0x7A.
  • In the payer hash, sender MUST be the resolved sender. On the implicit EOA path (empty wire sender), substitute the recovered address before hashing. That blocks cross-sender replay of payer signatures.
  • Including payer binds the signature to the account being charged (important with delegate authenticators).

Payer modes

Gas payment is controlled by two independent fields. payer is in the sender’s signed hash (the sender commits to who may pay). payer_auth is the payer’s own authorization blob, same authenticator || data shape as sender_auth.

ConceptMeaning
payer empty · payer_auth emptySelf-pay. Charged account is the sender. The resolved sender actor must have SELF_PAYER (or be admin). No separate payer_auth.
payer = sender · payer_auth setSelf-pay via a dedicated gas key. Payer account == sender; the payer_auth-resolved actor on that account must have SELF_PAYER. Lets a SELF_PAYER-only key fund another key’s transactions on the same account.
payer = other address · payer_auth setSponsored (payer != sender). Authenticate against the payer account; require SPONSOR_PAYER. Fee bumps need a fresh payer_auth (fees are in the payer hash).

Payer authentication is metered separately and does not draw from gas_limit, so the payer’s authenticator choice cannot starve calls. Sender-intrinsic gas and execution share gas_limit. Sponsorship patterns, ERC-8168, and session-key gas paths are expanded in Gas & payers.

Call phases

calls is a two-level structure: an ordered array of phases, each an ordered array of calls ([[call, …], [call, …]]). All phases share one gas_limit pool.

Call phases: batch 0 (approve + swap) commits when all calls succeed; batch 1 reverts and undoes its own calls when a later call fails, without rolling back batch 0.
Within a phase, calls are atomic. Completed phases persist: a later failure does not roll them back.
  • Within a phase, calls are atomic: any revert discards that phase’s state and skips remaining phases.
  • Completed phases persist: later phase failures do not roll them back.
  • Common pattern: sponsor settlement in phase 0, user actions in phase 1 so the sponsor payment commits even if the user batch reverts.

Policy-gated actors snapshot policy_manager once at the start of calls; every call in every phase must target that address or the protocol fails the call with ActorPolicyViolation without dispatching.

ConceptMeaning
expiryOptional tx-level deadline. Required non-zero for nonceless (NONCE_KEY_MAX) txs.
metadataOptional opaque bytes (attribution, memo). Signed by sender and payer; ignored by validation/execution.
account_changesCreate / config change / delegation entries applied before calls. Config changes carry their own admin auth.

Field-level jobs on the wire envelope are also covered in the Overview interactive explainer and the Validation pipeline.