Back to Blog 

SolidityWeb3 SecurityAudit
Phishing Attack in Web3: Why You Should Never Use tx.origin
Understand the difference between tx.origin and msg.sender, when each should be used, and why tx.origin opens the door to phishing attacks in Solidity.
What This Guide Covers
This article explains the role of
tx.origin and msg.sender in Solidity, how they differ, and why using tx.origin for authorization makes your smart contract vulnerable to phishing attacks. We walk through a concrete exploit scenario and show the simple fix.tx.origin
tx.origin returns the address of the externally owned account (EOA) that originally initiated the transaction. It traces all the way back through the call chain to the very first sender.One valid use case: if you want to block a specific address from interacting with your contract,
tx.origin is more appropriate because the owner of that address cannot use an intermediary contract to circumvent the block. Note that this only blocks a single address.msg.sender
msg.sender is the address that directly called the current function. This address could be an EOA or a smart contract. To identify the immediate caller of a function, you use the globally available msg object.How tx.origin and msg.sender Differ
Let's use an illustration to see how they differ in a multi-contract call chain:

msg.sender is always the EOA or smart contract that directly calls the function, while tx.origin traces back to the EOA or smart contract that initiated the entire transaction.Phishing Attack in Solidity
Let's look at a contract that uses
tx.origin for authorization in its transfer function:1contract Wallet {2 address public owner;34 constructor() payable {5 owner = msg.sender;6 }78 function transfer(address payable _to, uint _amount) public {9 require(tx.origin == owner, "Not owner");1011 (bool sent, ) = _to.call{value: _amount}("");12 require(sent, "Failed to send Ether");13 }14}
The
transfer function checks authorization with:1require(tx.origin == owner, "Not owner");
As we saw in the illustration above,
tx.origin looks at who started the entire transaction — not who directly called transfer. This means a malicious contract in the middle of the call chain can pass this check if the real owner initiated the transaction.The Attack Contract
Here is how an attacker would exploit this:
1contract Attack {2 address payable public owner;3 Wallet wallet;45 constructor(Wallet _wallet) {6 wallet = Wallet(_wallet);7 owner = payable(msg.sender);8 }910 function attack() public {11 wallet.transfer(owner, address(wallet).balance);12 }13}
The attacker deploys this contract pointing at the victim's
Wallet. Then the attacker tricks the wallet owner into calling attack() — through a phishing email, a malicious dApp link, or a deceptive transaction. When the owner calls attack(), it triggers wallet.transfer(). Since tx.origin is the wallet owner (they started the transaction), the require check passes and all funds are drained to the attacker.How to Prevent a Phishing Attack
1function transfer(address payable _to, uint _amount) public {2 require(msg.sender == owner, "Not owner");34 (bool sent, ) = _to.call{value: _amount}("");5 require(sent, "Failed to send Ether");6}
Are you audit-ready?
Download the free Pre-Audit Readiness Checklist used by 30+ protocols preparing for their first audit.
No spam. Unsubscribe anytime.
With
msg.sender, the contract checks who directly called the function. If the attacker's contract calls transfer, msg.sender will be the attacker's contract address — not the wallet owner — and the require will revert.Key Takeaways
- Never use
tx.originfor authorization. It is vulnerable to phishing because it looks at the original transaction sender, not the direct caller. - Always use
msg.senderfor access control. It correctly identifies the immediate caller of a function. tx.originhas limited valid uses — primarily for blocking specific addresses where intermediary contracts shouldn't bypass the block.- Phishing in Web3 is real. Attackers can trick users into interacting with malicious contracts that exploit
tx.originchecks to drain funds.
About Zealynx Security
Authorization vulnerabilities like
tx.origin misuse are avoidable, but only if you know where to look. At Zealynx Security, we help teams strengthen smart contracts through expert audits, fuzzing, and targeted tests that catch the kinds of exploits covered here. If you're building on Ethereum or need peace of mind before launch, reach out to us or explore how we can help.Building an EVM contract? Get your access-control logic reviewed before attackers find it first. Request an audit scope →
Connect with us:
FAQ: tx.origin phishing attacks
1. Is tx.origin ever safe to use?
The only valid use case is blocking a specific address from interacting with your contract, because the address owner cannot use an intermediary contract to bypass the block. For any form of authorization or access control, always use
msg.sender instead.2. How does the attacker trick the owner into calling the malicious contract?
Common vectors include phishing emails with links to malicious dApps, fake token approval requests, deceptive transaction prompts disguised as legitimate interactions, or compromised frontend interfaces that route transactions through the attacker's contract.
3. Can this attack drain a multisig wallet?
If the multisig uses
tx.origin for authorization (which would be unusual), yes. However, most multisig wallets use msg.sender checks and require multiple signers, making them inherently resistant to this attack vector.4. Does this vulnerability exist in other blockchain languages?
The specific
tx.origin vs msg.sender distinction is unique to Solidity and the EVM. However, the underlying principle applies everywhere: always verify the immediate caller, not the original transaction initiator. Solana and Move have different authorization models that avoid this specific pattern.5. How can auditors detect tx.origin misuse?
Static analysis tools like Slither flag any use of
tx.origin in authorization checks. During manual review, auditors look for require(tx.origin == ...) patterns and verify that msg.sender is used consistently for all access control decisions.Glossary
| Term | Definition |
|---|---|
| Phishing Attack | A social engineering technique where attackers trick victims into performing unintended actions, such as calling malicious smart contract functions. |
| EOA | Externally Owned Account — a blockchain account controlled by a private key, as opposed to a contract account. |
| tx.origin | A Solidity global variable that returns the address of the account that originally initiated the transaction. |
| msg.sender | A Solidity global variable that returns the address of the immediate caller of the current function. |
| Access Control | Security mechanisms that restrict which addresses can call specific functions in a smart contract, preventing unauthorized actions. |
| Solidity | The primary programming language for writing smart contracts on Ethereum and EVM-compatible blockchains. |
Are you audit-ready?
Download the free Pre-Audit Readiness Checklist used by 30+ protocols preparing for their first audit.
No spam. Unsubscribe anytime.


