Skip to main content
EOA Code Delegation lets an Externally Owned Account (EOA) point at a contract whose code will execute in the EOA’s own context whenever the EOA is called, similar to how DELEGATECALL runs Contract B’s code inside Contract A’s storage frame. It is Hedera’s adoption of Ethereum’s EIP-7702, defined for Hedera by HIP-1340 and rolled out as part of the Pectra hard fork. Delegation unlocks smart-account use cases (transaction batching, sponsored gas, session keys, privilege de-escalation) without migrating funds to a new contract wallet; the EOA’s address, balance, NFTs, and tokens stay put.

How delegation is stored

The Account entity gains a new optional 20-byte field, delegation_address. When set, calls to the EOA’s address resolve to the code at delegation_address. For Ethereum-tool compatibility, the Smart Contract Service exposes the delegation as a Delegation Indicator in the EOA’s code field:
  • Before Pectra, eth_getCode(eoa) always returned 0x.
  • After Pectra, an EOA with delegation set returns the 23-byte 0xef0100... indicator. An EOA with no delegation still returns 0x.
  • Regular contract bytecode cannot start with 0xef (per EIP-3541), so the prefix is unambiguous.
A delegation_address of 0x0000000000000000000000000000000000000000 clears the delegation (the field becomes empty).

Two paths to configure delegation

You can set, change, or clear an EOA’s delegation through either an Ethereum-compatible Type 4 transaction or a native HAPI CryptoCreate / CryptoUpdate. Both routes converge on the same delegation_address field on the account.

Path A: EVM (Type 4 EthereumTransaction)

A Type 4 transaction extends the standard RLP-encoded fields with an authorization_list:
Each entry is an independent intent, signed by the EOA that wants delegation set. The signature is recovered with ecrecover, and the recovered account’s delegation_address is set to the entry’s address. A single Type 4 transaction can authorize delegations for many unrelated EOAs in one shot, and the transaction sender may be a third party (i.e., sponsored delegation setup, useful for onboarding flows). Validity rules (per EIP-7702, enforced by Hedera):
  • Signature must be valid ecrecover over (MAGIC ‖ rlp([chain_id, address, nonce])).
  • chain_id must be 0 (any chain) or match the network’s chain ID (296 testnet, 295 mainnet).
  • nonce must match the authorizing account’s current nonce.
  • If the authorization list is empty, the transaction is invalid and rejected.
  • Invalid entries are silently skipped (no revert).
  • If multiple valid entries target the same EOA, only the last one is applied.
Each authorization spawns a child CryptoCreate (for a brand-new hollow account) or CryptoUpdate (for an existing account). These child records appear in the transaction’s record stream regardless of whether the parent EVM call reverts; the delegation persists even if the subsequent code execution fails.

JavaScript example (viem)

There are two common flows: self-submitted (the EOA pays for and sends its own delegation transaction) and sponsored (a third party pays; the EOA only signs the authorization). Self-submitted (the EOA owns the gas):
JavaScript
Sponsored (a third party pays; the EOA only signs the authorization):
JavaScript
After the transaction is mined, calls to eoa.address will execute the smart-account implementation’s code in the EOA’s storage context. To revoke, the EOA signs a new authorization with contractAddress: "0x0000000000000000000000000000000000000000".

Submitting via EthereumTransaction (Hedera SDK)

If you have a fully-signed Type 4 RLP payload, you submit it the same way as any other Ethereum transaction:
JavaScript
The SDK also models the Type 4 payload directly via EthereumTransactionDataEip7702, which carries the standard EIP-1559-style fields plus accessList and an authorizationList of Authorization entries (chainId, address, nonce, yParity, r, s). Calling toBytes() on it produces the raw payload to pass to setEthereumData().

Path B: Native HAPI (CryptoCreate / CryptoUpdate)

HIP-1340 extends both CryptoCreateTransactionBody and CryptoUpdateTransactionBody with a delegation_address field (bytes, 20 raw EVM address bytes, not the 0xef0100-prefixed indicator). The SDK lifts this onto AccountCreateTransaction and AccountUpdateTransaction as setDelegationAddress(), which accepts raw bytes, a hex string (with or without the 0x prefix), or an EvmAddress.
JavaScript
To clear the delegation, set it to the zero address (0x0000000000000000000000000000000000000000) or pass null on an AccountUpdateTransaction.
Native HAPI is the only path available to ED25519 accounts and ECDSA accounts with a long-zero alias, since those account types cannot sign EIP-7702 authorizations. See Account-type applicability.

What happens when a delegated EOA is called

When the Smart Contract Service processes a call whose target is an EOA address:
The call applies uniformly whether it originated from:
  • A top-level transaction (transaction.to == eoa).
  • An inner CALL / STATICCALL from another contract (address(eoa).call(...)).
  • Any of the legacy transaction types (0, 1, 2), not just Type 4.

Code queries

EXTCODESIZE, EXTCODECOPY, EXTCODEHASH, eth_getCode, and ContractGetBytecodeQuery against a delegated EOA all return the Delegation Indicator (0xef0100 ‖ delegation_address). For an undelegated EOA they return empty bytecode, as before.

Storage & logs

A delegated EOA can now hold mutable storage slots and emit EVM logs, because the delegated code runs in its context. Tooling implications:
  • eth_getStorageAt(eoa, slot) returns the EOA’s storage.
  • eth_getLogs returns logs emitted from the EOA’s address.
  • Mirror Node /api/v1/contracts/{eoa}/state, /results/logs, /call, and friends now work on EOA addresses too.

Chains & loops

If delegation_address points to another EOA that itself has a delegation, only the first hop is resolved. The bytecode at the second hop is interpreted literally, and since 0xef is a banned opcode (EIP-3541), the call reverts. In practice this means don’t chain delegations: always delegate directly to a real contract.

No-op targets

Setting delegation_address to any of the following is allowed, but the resulting call to the EOA is a no-op (value transfer only, no code executes):
  • A non-existent account.
  • A precompile address (0x010x11).
  • A system contract (HTS at 0x167, HAS at 0x16a, HSS at 0x16b, Exchange Rate at 0x168, PRNG at 0x169).
  • A Hedera system account.
  • Another EOA whose own code is empty.
Direct delegation from a regular EOA to a system contract is therefore intentionally inert; the token-proxy / schedule-proxy accounts are the only entities permitted to “delegate” to a system contract address.

HAS facade precedence

Calls that match a Hedera Account Service facade selector are always routed to HAS, even if the EOA has a delegation set. This preserves the user-facing behavior of pre-Pectra HAS calls. Processing order (per HIP-1340):
  1. If the call selector matches a HAS facade function → HAS handles it. The user’s delegation is ignored for this call.
  2. Else if the EOA has a delegation → run the delegate contract’s code.
  3. Else → no-op (with any attached value transfer).
⚠️ Selector-collision warning. If your smart-account delegate defines a function whose selector happens to equal one of the HAS selectors above (e.g. you happen to write a hbarApprove(address,int256) method), that function will be unreachable; HAS will intercept the call first. Pick distinct signatures.

Account-type applicability

Whether you can use the EVM path depends on the account’s key type and alias scheme. This isn’t new behavior; it reflects the existing rule that only ECDSA-with-public-key-derived-alias accounts can submit Ethereum transactions on Hedera. Once delegation is configured, execution semantics are uniform across account types: a call to an ED25519 account with delegation_address set runs the delegate code just like for any other account. CryptoTransfer signature validation is unchanged. Hedera-native authorization (e.g., multi-sig threshold keys) continues to govern transfers initiated through HAPI.

Gas costs

Per EIP-7702, every entry in an authorization_list costs gas, whether valid, invalid, or duplicate: The cap on hollow accounts created in one transaction is governed by the transaction’s gas_limit and the network’s operations-per-second throttle. For the calldata and execution sides of the gas calculation, see Gas and Fees.
Gas shortage on a valid authorization. If an otherwise-valid authorization can’t be processed because the transaction ran out of gas (for example, it would require creating a hollow account but the gas_limit is exhausted), Hedera still emits a child CryptoCreate / CryptoUpdate record with a failed status so the error surfaces in the record stream rather than being silently dropped.

Smart-account example (Solidity)

A minimal smart-account delegate that supports batched calls and an owner-only check:
Solidity
Deploy once with a regular ContractCreate. Then any number of EOAs can delegate to that deployed address, and they each get smart-account behavior without re-deploying.
Use namespaced storage (ERC-7201) in delegate contracts so the EOA can later switch delegates without colliding on storage slots written by the previous implementation.
This is the flagship EIP-7702 use case: a new user with zero HBAR gets smart-account behavior and executes a multi-step action in one transaction, while a sponsor pays the gas. The single Type 4 transaction does two things at once: it sets the EOA’s delegation and runs a batched call in the EOA’s context.
JavaScript
The EOA never needed HBAR or a prior on-chain footprint. After this transaction, eth_getCode(eoa.address) returns the Delegation Indicator, and the approve + swap have executed from the EOA’s own address.
The delegation set by the authorization persists even if the batched call reverts (authorizations are committed before EVM execution). If you want all-or-nothing onboarding, have the delegate contract revert on any sub-call failure (as executeBatch does via its require), and treat the delegation itself as a separate, durable state change.

Mirror Node surface

HIP-1340 extends the Mirror Node REST API:
  • Accounts endpoints (GET /api/v1/accounts, GET /api/v1/accounts/{idOrAliasOrEvmAddress}) include a new delegation_address field per account. The value is 0x when no delegation is set.
  • Contract-result endpoints (GET /api/v1/contracts/{id}/results/{timestamp}, GET /api/v1/contracts/results, GET /api/v1/contracts/results/{txOrHash}) include a new authorization_list field for Type 4 transactions:
  • EOA contract APIs. /api/v1/contracts/{eoa}/state, /results/logs, /results, /results/{tx}/actions, /results/{tx}/opcodes, and /api/v1/contracts/call now accept EOA addresses and return contract-style data when the EOA has a delegation set.
JSON-RPC Relay updates: eth_getCode, eth_getStorageAt, and eth_getLogs work for delegated EOAs; the relay handles Type 4 transaction parsing and submission.

HTS / HSS proxy accounts

HIP-1340 also re-implements the long-standing HTS-token and HSS-schedule facade calls (HIP-218 / HIP-719 / HIP-755 / HIP-906) using the new Delegation Indicator format. Calls like IERC20(tokenAddress).transfer(...) continue to work exactly as before, but ContractGetBytecode(tokenAddress) now returns a stable 0xef0100 ‖ <HTS-system-contract-address> indicator instead of a runtime-synthesized DELEGATECALL stub. This is invisible to most users. Tools that introspect the bytecode of token-proxy or schedule-proxy accounts (e.g. block explorers, custom indexers) may need to update their parsing.

Caveats and edge cases

  • An empty authorization_list makes a Type 4 transaction invalid. Don’t construct one and then drop entries client-side; either include at least one authorization or send a different type.
  • Authorizations persist even if the EVM execution reverts. They are processed before EVM code runs and committed independently; this is a feature (so onboarding flows still succeed even if the follow-up call fails), but it does mean a “rolled back” transaction can still leave delegations in place.
  • Selector collisions with HAS make those functions unreachable on a delegated EOA. Pick non-colliding signatures.
  • Storage migration. Switching delegation targets does not migrate storage. Use ERC-7201 namespaces in your delegates to avoid collisions.
  • msg.sender == tx.origin invariant no longer holds in deeper frames. Code historically used msg.sender == tx.origin as a “no contracts” check. With delegation, the topmost frame on a Type 4 transaction can be a delegated EOA, meaning tx.origin and msg.sender align in places they previously wouldn’t. Treat this check as advisory, not a security boundary.
  • No impact on hooks. A call to a delegated EOA inside a hook behaves the same as outside one. Hooks use a dedicated storage namespace, so delegate code writing to the EOA’s storage slots cannot collide with hook storage.

References