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:
-
The Victim Contract: You have a
MyWallet
contract with a function liketransferAllFunds(address target)
. For security, you add the checkrequire(tx.origin == owner);
thinking only you can authorise transfers. -
The Attacker’s Contract: An attacker deploys a malicious contract, let’s call it
FreeKittensGame
. -
The Trap: The attacker convinces you to interact with their
FreeKittensGame
by calling one of its functions (e.g.,getFreeKitten()
). -
The Exploit: Hidden inside the
getFreeKitten()
function is a call to yourMyWallet.transferAllFunds()
function, with the attacker’s address as the target.
When your wallet’s function runs:
-
The
msg.sender
is theFreeKittensGame
contract’s address. A check againstowner
would fail here. -
The
tx.origin
is your wallet address, because you are the one who started this entire chain of calls. The checkrequire(tx.origin == owner);
passes, and the attacker’s contract successfully drains your funds.
Rule of Thumb: Always use msg.sender
for authorisation checks.