| Signing scheme Flexible auth |
ECDSA (secp256k1) |
Anything (ECDSA, P256, WebAuthn, multisig, MPC, ZK proof) |
ECDSA or P256 natively; smart accounts define freely |
secp256k1, P256, WebAuthn, External/ISigner (EVM-verified) |
ECDSA, WebAuthn, P256 |
secp256k1 (EIP-712 message_version) |
| Signing cardinality Multisig / threshold |
1-of-1 only |
N-of-M, threshold, social recovery — account defines |
N-of-M — account defines |
1-of-1 per key (External → m-of-n via MultiSigSigner) |
1-of-1 (or account-defined?) |
1-of-1 |
| Who authorized execution? |
signer = sender = payer (all one) |
Smart account validateUserOp |
VERIFY frame → APPROVE(0x0) |
keyHash (super admin or session) |
app-defined |
signer = sender = payer |
| Who authorized payment? Gas sponsorship |
same as sender (implicit) |
Paymaster validatePaymasterUserOp (or self-pay) |
VERIFY frame → APPROVE(0x1) |
Account pays tokens → relayer; relayer pays ETH |
fee_payer_signature (separate signer) |
Same as sender |
| Can sender ≠ payer? |
No |
Yes — Paymaster is separate entity |
Yes — APPROVE(0x2) opts into collapse; otherwise distinct |
Yes |
Yes — separate signers |
No |
| Gas pricing EIP-1559 gas |
maxFee, maxPriority, gasLimit |
maxFee, maxPriority, preVerificationGas, per-op gasLimits |
maxFee, maxPriority, per-frame gas_limit |
EIP-1559 + combinedGas in Intent |
EIP-1559 fees |
gas_price (legacy) |
| Gas token ERC-20 gas payment |
ETH only |
ETH (Paymaster accepts ERC-20 off-protocol) |
ETH (sponsor accepts ERC-20 via frames) |
ETH (relayer); tokens (account → relayer) |
fee_token (any TIP-20 stablecoin) |
ETH only |
| Sequence (don't execute twice) Nonce |
nonce (sequential, protocol-enforced) |
nonce (2D: key ‖ sequence in EntryPoint) |
nonce (protocol-enforced, incremented by APPROVE) |
Sequential per 192-bit key (on account) |
nonce (sequential) |
Sequential (legacy) |
| Validity window Time/block-based validity |
None (valid until included or nonce consumed) |
validUntil, validAfter (paymaster can enforce) |
None (app-defined in frame logic) |
Intent.expiry |
valid_before + valid_after |
recent_block_hash + expires_at_block |
| Cross-chain protection |
chainId (in RLP, protocol-enforced) |
chainId (in UserOp hash) |
chainId (in RLP) |
EIP-712 domain separator |
domainSeparator |
chainId |
| Cross-type protection Domain separation |
RLP structure (not valid EIP-191) |
Wrapped inside regular tx (bundler submits 0x00–0x04) |
0x06 ‖ RLP(...) |
EIP-712 type hashes |
tx type / EIP-712? |
0x4A ‖ RLP(…) |
| Nonce lane selection 2D nonces |
— |
Yes (nonce_key) |
— |
Yes (192-bit key prefix) |
Yes (nonce_key) |
— |
| Payload structure |
single: to, value, data |
single: to, value, calldata |
frames[] — ordered (mode, target, gas_limit, data) |
calls[] — ERC-7821 Call{target,value,data} |
calls[] (batched) |
Single to, value, data |
| Multicall Call batching |
— |
— (account contracts often implement it) |
Yes — multi-frame |
Yes |
Yes — native calls[] |
— |
| Account deployment |
Separate tx |
initCode in first UserOp |
Frame 0 as DEFAULT calling deployer |
7702 delegation tx |
aa_authorization_list |
— |
| Pre-warm storage / parallel execution Access lists |
access_list (declares touched slots upfront) |
— |
Warm/cold journal shared across frames |
Inherits from carrier tx |
— |
None |
msg.sender at depth 0 |
from (= signer) |
account address (via EntryPoint) |
SENDER: tx.sender. DEFAULT/VERIFY: ENTRY_POINT |
Orchestrator (or self if direct) |
? |
from (= signer) |
tx.origin |
from (= signer) |
bundler EOA (!) |
frame's caller (varies per frame!) |
Relayer EOA |
? |
from (= signer) |
| Calldata privacy Calldata encryption |
— |
— |
— |
— |
— |
encrypted (ECDH+AEAD) |
| Response privacy Response encryption |
— |
— |
— |
— |
— |
encrypted |
| Authenticated reads Signed reads |
— |
— |
— |
— |
— |
signed_read |