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.
| Concept | Meaning |
|---|---|
| SENDER · 0x01 | Ungated initiation via sender_auth. May originate calls to any call.to. Operational unless combined with POLICY (also satisfies ERC-1271). |
| POLICY · 0x02 | Gated initiation via sender_auth. Calls must target the actor’s policy_manager. Never operational. |
| NONCE · 0x04 | Restricted actors may use sequenced nonce_keys (sender-context). Without it, only NONCE_KEY_MAX (nonceless). |
| SELF_PAYER · 0x08 | Pay the account’s own gas when payer == sender (self-pay). |
| SPONSOR_PAYER · 0x10 | Act 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, orPOLICY. WhenPOLICYis set, every call is gated topolicy_managerregardless ofSENDER(wallets SHOULD NOT setSENDER | POLICY). - Self-pay: admin or
SELF_PAYERon the actor that authorizes payment. - Sponsored payer_auth: admin or
SPONSOR_PAYERon the payer-resolved actor. - Config / lock / delegation: admin only (
scope == 0x00). - verifySignature(): operational (admin, or
SENDERwithoutPOLICY).
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).
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
]))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,
senderMUST be the resolved sender. On the implicit EOA path (empty wiresender), substitute the recovered address before hashing. That blocks cross-sender replay of payer signatures. - Including
payerbinds 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.
| Concept | Meaning |
|---|---|
| payer empty · payer_auth empty | Self-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 set | Self-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 set | Sponsored (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.

- 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.
Related envelope fields
| Concept | Meaning |
|---|---|
| expiry | Optional tx-level deadline. Required non-zero for nonceless (NONCE_KEY_MAX) txs. |
| metadata | Optional opaque bytes (attribution, memo). Signed by sender and payer; ignored by validation/execution. |
| account_changes | Create / 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.