doug

Posted on Feb 21, 2022Read on Mirror.xyz

0x0 vs. msg.sender

UPDATE: It turns out msg.sender can be 0x0 in read operations. I didn’t try that in my tests below, since the mystery check that drove me bananas (below) was in a mint operation and you cannot execute that by Read. But it’s food for thought, anyway.

Is it Nerd Sniping if you snipe yourself? It happened to me. I saw a weird thing in a Solidity contract and I couldn’t get it out of my mind: The Pepsi Mic Drop contract has this check in its mint method:

require(msg.sender != address(0x0), "Public address is not correct");

I can’t understand what it’s doing, or how msg.sender could ever be 0x0. It doesn’t make any sense. The author of this Solidity tutorial notices it and doesn’t understand, too. There’s a bunch of comments on an old Stack Overflow question, and the general consensus is that it’s not possible. But some Googling (and Discord chat, shout out Def Discord) suggest that a contract’s msg.sender might be 0x0 if an outbound request is made a contract’s constructor. Could that be? Let’s find out!

I set up a contract (let’s call it Target) that has two methods: one to return the caller’s msg.sender, the other to return its tx.origin. (BTW, skip way down for some additional details on these two values!)

I then deployed another contract that itself will deploy an intermediary contract, CallerByConstructor. Its job is to call Target’s methods to see what that Target sees for CallerByConstructor’s tx.origin and msg.sender. CallerByConstructor will emit events to log those responses.

Sure enough, Target sees CallerByConstructor’s address for the msg.sender, and my wallet’s address for the tx.origin.

So I guess that means the Pepsi check is just cargo cult copy and pasted in? Maybe Solidity used to allow for 0x0 msg.senders long ago, the code has been passed down to us today? Do you know? Let me know on Twitter, it’s driving me crazy.


Quick reminder about msg.sender and tx.origin:

msg.sender will be the address of the immediate caller to a contract. That could be you and your wallet, or it could be a contract calling another contract.

tx.origin remains consistent even across multiple contract calls. It will generally always be the wallet address that initially triggered a transaction. For example, say I call Contract A, and A calls B, and B calls C. C is going to report the tx.origin as my wallet address (since it originated the whole thing), and msg.sender as B’s address.

Additionally, this is why a common anti-bot trick is to require(msg.sender == tx.origin): that’ll generally catch any funny business, like contracts deploying other contracts to interact with your contract, as happened to Adidas. Montana Wong touches on it here:

https://twitter.com/Montana_Wong/status/1472302221026942977