tx.origin Authentication

tx.origin refers to the original externally-owned account (EOA) that initiated a transaction chain. msg.sender refers to the immediate caller. When a user calls contract A, which calls contract B, tx.origin is the user and msg.sender in contract B is contract A. Using tx.origin for auth means any contract the user calls can impersonate them.

How the attack works

A victim's wallet contract uses tx.origin == owner as its auth check. An attacker deploys a phishing contract and tricks the owner into calling it (e.g., through a fake airdrop). The phishing contract then calls the victim's wallet — tx.origin is still the owner — and drains it.

Vulnerable pattern

// VULNERABLE — uses tx.origin for ownership check
contract Wallet {
    address owner;
    
    function transfer(address payable to, uint amount) public {
        require(tx.origin == owner, "Not owner");  // WRONG!
        to.transfer(amount);
    }
}

// Attacker's phishing contract:
contract PhishingAttack {
    Wallet target;
    address attacker;
    
    receive() external payable {
        // When victim calls this contract, tx.origin == victim
        target.transfer(payable(attacker), address(target).balance);
    }
}

Safe pattern

// SAFE — use msg.sender, not tx.origin
function transfer(address payable to, uint amount) public {
    require(msg.sender == owner, "Not owner");  // CORRECT
    to.transfer(amount);
}

// tx.origin is only acceptable for one use case:
// ensuring the caller is an EOA (not a contract)
require(msg.sender == tx.origin, "No contracts allowed");

When can tx.origin be used?

The only safe use of tx.origin is checking that the immediate caller is an EOA (by requiring msg.sender == tx.origin). Never use it as an identity check for authorization.

How to prevent it

  • Always use msg.sender (not tx.origin) for authorization checks.
  • Solhint flags tx.origin usage — enable the rule in your CI pipeline.
  • Audit all require/if statements that reference tx.origin.
← Back to Glossary