Expanding on tx.origin vs. msg.sender

Both variables identify an address, but they refer to different actors in a transaction chain.

  • msg.sender: The immediate account that called the current function. It can be a user’s wallet or another smart contract.

  • tx.origin: The original wallet account (Externally Owned Account) that started the entire chain of transactions. This value never changes, even through multiple contract-to-contract calls.

Using tx.origin for authorisation is extremely dangerous. Here is a classic phishing attack scenario:

  1. The Victim Contract: You have a MyWallet contract with a function like transferAllFunds(address target). For security, you add the check require(tx.origin == owner); thinking only you can authorise transfers.

  2. The Attacker’s Contract: An attacker deploys a malicious contract, let’s call it FreeKittensGame.

  3. The Trap: The attacker convinces you to interact with their FreeKittensGame by calling one of its functions (e.g., getFreeKitten()).

  4. The Exploit: Hidden inside the getFreeKitten() function is a call to your MyWallet.transferAllFunds() function, with the attacker’s address as the target.

When your wallet’s function runs:

  • The msg.sender is the FreeKittensGame contract’s address. A check against owner would fail here.

  • The tx.origin is your wallet address, because you are the one who started this entire chain of calls. The check require(tx.origin == owner); passes, and the attacker’s contract successfully drains your funds.

Rule of Thumb: Always use msg.sender for authorisation checks.