Use of tx.origin¶
Smart contracts routinely need to authorize certain parties for privileged operations such as changing an admin address, withdrawing funds, or triggering protocol-critical mechanisms. Solidity offers two mechanisms to identify the calling party, msg.sender and tx.origin. They appear interchangeable at first glance, and their behavior diverges depending on how the contract is invoked.
Three entities illustrate the difference: an admin address initiating a transaction, a target contract that enforces access control, and an optional proxy contract sitting between the two.
flowchart LR
Admin -->|msg.sender| Target
Admin --> Proxy
Proxy -->|tx.origin| Target
When the admin calls the target contract directly, both msg.sender and tx.origin yield the admin's address. Routed through a proxy, msg.sender becomes the proxy contract while tx.origin remains the original admin address. The Solidity documentation describes the two values as:
- msg.sender (address): sender of the message (current call)
- tx.origin (address): sender of the transaction (full call chain)
A contract authorizing callers by tx.origin therefore lets malicious proxy contracts spoof the identity of the original sender, because a smart contract invokes other contracts without the user approving each step in the call chain. Relying on tx.origin trusts the entire call path rather than the direct caller, which opens a surface for phishing-style attacks.
Attack Scenario¶
A lending protocol supporting user liquidations makes the vulnerability concrete. An early version of the project uses a LiquidationManager contract to restrict liquidations to an authorized bot or actor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | |
The contract intends to restrict liquidations to one specific address. Because the check reads tx.origin, an attacker bypasses it with the following contract:
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
An authorized liquidator who calls bait(), perhaps taking it for a legitimate peripheral contract of the protocol, causes the manager to observe the correct liquidator address in tx.origin and approve the call. The liquidation is in fact triggered by a malicious proxy against an unintended user, which hands the attacker the ability to liquidate at will.
Fragile EOA Detection¶
Beyond authorization logic, many smart contracts, especially early DeFi projects, restrict interaction to externally owned accounts (EOAs) with a simple check:
1 2 | |
The check assumes that matching tx.origin and msg.sender prove the call originated from an EOA. The Ethereum Pectra upgrade broke that assumption by introducing EIP-7702, which lets an EOA delegate to smart contract code and revert the delegation later.
Through the resulting self-sponsoring mechanism, a smart contract constructs a transaction in which msg.sender and tx.origin are equal even though the originator is no longer a traditional EOA. The EIP's security considerations warn about it directly:
Allowing the sender of an EIP-7702 to also set code has the possibility to:
- Break atomic sandwich protections which rely on tx.origin;
- Break reentrancy guards of the style
require(tx.origin == msg.sender).
The EIP authors accept these risks in light of broader design goals, which leaves developers and auditors responsible for recognizing, flagging, and avoiding any anti-pattern built on tx.origin. Few legitimate use cases remain, and fewer still survive EIP-7702.