Accounts
Tempo uses a "Default Delegation" (DD) model for accounts on the Tempo blockchain. DD extends the Ethereum account model by allowing any externally owned account (EOA) to be seamlessly upgraded into a smart contract wallet, without requiring user intervention or setup.
The core mechanism is that, on first use (when an EOA sends its first transaction and has never been used before), the protocol automatically delegates the account to a canonical smart contract implementation by setting its code to a special format. This enables all EOAs to immediately benefit from smart wallet features, while preserving full backward compatibility with legacy ECDSA transactions and not affecting contract accounts.
Additionally, a registrar precompile is introduced to allow anyone to permissionlessly delegate an EOA to the default implementation by proving control of the address via a signature. The default implementation contract is treated as always warm for gas purposes.
Tempo's DD model is fully compatible with EIP-7702. For a detailed understanding of the underlying delegation format and semantics, see the EIP-7702 specification.
Features
Default Delegation (DD) allows any EOA to be used as a smart contract wallet.
It does so through two new behaviors in-protocol:
-
Auto-delegation on first use. When a top-level transaction originates from an address
A,nonce(A) == 0, andcode(A)is empty, the protocol setscode(A) = 0xEF0100 || DEFAULT_7702_IMPLduring execution, thereby delegatingAto the default implementation per 7702 semantics. Legacy ECDSA tx validation is unchanged. -
Registrar precompile. A new precompile, DefaultAccountRegistrar, takes
(hash, v, r, s), derives anauthorityaddress viaecrecover(hash, v, r, s), and delegatesauthorityto DEFAULT_7702_IMPL. It reverts if that address is already delegated or is a contract.
Additionally, DEFAULT_7702_IMPL is treated as always warm for gas, like a precompile.
Motivation
- Make EOAs immediately usable with a canonical smart-wallet implementation without user setup.
- Preserve full backward compatibility with legacy ECDSA transactions.
- Avoid changing any behavior of non-EOA addresses.
- Provide a permissionless path to prove that an address is an EOA (via any prior ECDSA signature) and delegate an address to DEFAULT.
Specification
Notation & constants
EMPTY— empty byte arrayEF_PREFIX—0xEF0100(EIP-7702 delegation prefix)DEFAULT_7702_IMPL—0x7702c00000000000…(20-byte, fixed in genesis)DEFAULT_ACCOUNT_REGISTRAR—0x7702ac00000000000…(20-byte precompile address)- “Delegated to X” ⇔
code(account) == EF_PREFIX || X(exact 7702 format) - “Plain EOA” ⇔
code(account) == EMPTY
Out of scope: The runtime behavior/ABI of
DEFAULT_7702_IMPLitself (separate spec). DD only defines delegation mechanics.
1) Auto-delegation on first use
Trigger: When executing a top-level transaction with tx.from = A.
code(A) == EMPTYnonce_pre(A) == 0(the nonce value read before nonce increment)
- Set
code(A) := EF_PREFIX || DEFAULT_7702_IMPL.
- Legacy ECDSA tx admission and verification are unchanged.
- The auto-delegation check uses
nonce_pre(A)(the state prior to the normal nonce increment). This makes behavior deterministic across implementations.
- If
code(A) != EMPTY(e.g., a contract), do nothing. - If
nonce_pre(A) != 0, do nothing. - This rule does not prohibit later re-delegation or undelegation; those are governed by standard 7702 semantics (see Redelegation & undelegation below).
- The gas accounting for installing
EF_PREFIX || DEFAULT_7702_IMPLmatches the cost model for 7702 delegation on Tempo (same schedule as 7702 delegation).
2) DefaultAccountRegistrar precompile
Address: DEFAULT_ACCOUNT_REGISTRAR = 0x7702ac00000000000…
delegateToDefault(bytes32 hash, uint8 v, bytes32 r, bytes32 s)- Inputs:
hash: any 32-byte string, typically a hash of a message(v, r, s): ECDSA signature parameters
- Internal derivation:
pubkey = ecrecover(hash, v, r, s)with standard Ethereum constraints:- Accept
v ∈ {27,28}or{0,1}(treated as 27/28, respectively) - Enforce EIP-2 style “low-s” (reject high-
s) - Reject invalid points or failed recovery
- Accept
authority = address(pubkey)
- Require
code(authority) == EMPTY. If not, revert. - Require
nonce(authority) == 0. If not, revert. - Set
code(authority) := EF_PREFIX || DEFAULT_7702_IMPL.
- Returns authority on success.
- No event is emitted.
- Charge identical total gas as a 7702 delegation on Tempo (i.e.,
ecrecovercost + code-installation cost consistent with 7702). Exact numeric schedule is inherited from 7702 on Tempo.
Always-warm rule
DEFAULT_7702_IMPLis always warm (like a precompile). For gas, treat it as if it appears in the access list at the start of every transaction. This applies chain-wide and unconditionally.
Redelegation & undelegation
- After auto-delegation or registrar delegation, an account may:
- Re-delegate to another 7702 target, or
- Delegate to nothing (undelegate),
- …using the exact same mechanisms and semantics as EIP-7702.
Rationale
- First-use default: Ensures EOAs are smart-wallet capable immediately without wallet migration UX, while preserving legacy tx signing.
- Registrar “any message”: The design intentionally accepts any valid ECDSA signature to “prove EOA control,” enabling use of existing public signatures (tweets, GitHub sigs, old onchain proofs). Replay across chains or contexts is explicitly allowed by design (no domain binding).
- Revert if nonce is not 0: Allows addresses to opt out (by delegating to some other 7702 contract or to empty code) and not be forcibly redelegated.
- Revert if code is not empty: Prevents edge cases (including repeated auto-delegation of the same account).
- Always-warm DEFAULT_7702_IMPL: Smooths gas, based on the assumption that many transactions will use the default contract.
Backwards Compatibility
- Legacy ECDSA transactions: Unchanged validation. The only new effect is auto-delegation on the first tx for plain EOAs with
nonce == 0. - Contracts / codeful accounts: Never auto-delegated; registrar reverts.
- 7702 tooling: Fully compatible; DD uses the exact 7702 delegation bytecode format and override semantics.
Security Considerations
- Forced delegation by third parties: Anyone can delegate an EOA via the registrar if they can produce any valid signature by that key (by design). This does not grant fund control if
DEFAULT_7702_IMPLrespects the original key, but it does change account semantics and may surprise tooling. Accepted as a trade-off. - Signature replay & no domain binding: Signatures from other chains or contexts can be reused. This is deliberate; downstream apps MUST NOT treat registrar delegation as consent for anything beyond delegation.
- Malleability constraints: Enforce low-
sand canonicalvmapping to avoid malleability and recovery edge cases. - CREATE/CREATE2 & contracts at EOA addresses: DD never writes code to an account that already has code; changing a contract account’s code via registrar is disallowed (revert). Accounts created as contracts at genesis are unaffected.
Test Cases (illustrative)
-
First legacy tx auto-delegates
- Pre:
code(A)=EMPTY,nonce(A)=0 - Execute: valid legacy ECDSA tx
from=A - Post:
code(A)=EF_PREFIX||DEFAULT_7702_IMPL; tx body executes normally.
- Pre:
-
First tx when
nonce(A)=1(e.g., state pre-set)- Pre:
code(A)=EMPTY,nonce(A)=1 - Execute: tx
from=A - Post: No delegation performed.
- Pre:
-
Address has code (contract)
- Pre:
code(A)=<non-empty>,nonce(A)=0 - Execute: tx
from=A - Post: No delegation performed.
- Pre:
-
Registrar happy path
- Input:
(hash, v, r, s)whereecrecover(hash, v,r,s)=A - Pre:
code(A)=EMPTY - Call:
DEFAULT_ACCOUNT_REGISTRAR.delegateToDefault(...) - Post:
code(A)=EF_PREFIX||DEFAULT_7702_IMPL; success (empty returndata).
- Input:
-
Registrar with reused public signature
- As (4), but
hashis a keccak hash of a known public message (e.g., a tweet contents). Same success outcome.
- As (4), but
-
Registrar when already delegated (to default or another impl)
- Pre:
code(A)=EF_PREFIX||Xfor anyX - Call: registrar
- Post: Revert.
- Pre:
-
Registrar for contract address
- Pre:
code(A) != EMPTY - Call: registrar with signature yielding
A - Post: Revert.
- Pre:
-
Invalid signature
ecrecoverfails orsis high- Post: Revert.
-
Re-delegation via 7702
- Pre:
code(A)=EF_PREFIX||DEFAULT_7702_IMPL - Action: perform a standard 7702 redelegation of
AtoI2(or to none) - Post:
code(A)=EF_PREFIX||I2(orEMPTYif undelegated)
- Pre:
-
Gas warmness
- Any tx executing a
CALL/DELEGATECALLtoDEFAULT_7702_IMPLtreats it as warm without prior access-list touch.
- Any tx executing a
Reference Pseudocode (consensus-level)
Auto-delegation on tx start/// Executed at the beginning of a top-level tx, during execution:
fn maybe_auto_delegate_on_first_use(sender: Address):
let code = state.get_code(sender)
let nonce_pre = state.get_nonce(sender) // value before nonce increment
if code.length == 0 && nonce_pre == 0:
state.set_code(sender, EF_PREFIX ++ DEFAULT_7702_IMPL)/// At address DEFAULT_ACCOUNT_REGISTRAR
/// abi: delegateToDefault(bytes hash, uint8 v, bytes32 r, bytes32 s)
fn delegateToDefault(message: bytes, v: u8, r: bytes32, s: bytes32):
let v_norm = if v in {27,28} then v else if v in {0,1} then (v + 27) else revert()
require(is_low_s(s)) // EIP-2 style
let authority = ecrecover(hash, v_norm, r, s) or revert()
require(state.get_code(authority).length == 0) // must be plain EOA
require(!is_7702_delegated(state.get_code(authority))) // no EF_PREFIX
state.set_code(authority, EF_PREFIX ++ DEFAULT_7702_IMPL)
return authorityconst EF_PREFIX = 0xEF0100
const DEFAULT_7702_IMPL = 0x7702c00000000000... // 20 bytes
fn is_7702_delegated(code: bytes) -> bool:
return code.length == 1+1+1+20 // 0xEF 0x01 0x00 || 20 bytes
&& code[0..3] == EF_PREFIXDeployment / Activation
- Genesis: Insert
DEFAULT_7702_IMPLas an immutable, predeployed contract at0x7702c00000000000…with its code defined by the separate implementation spec. - Fork rules: DD is active from genesis on Tempo. Clients must include:
- the auto-delegation state transition hook,
- the DefaultAccountRegistrar precompile at
0x7702ac00000000000…, - the always-warm treatment for
DEFAULT_7702_IMPL.