The global message context
Every Solidity function call comes with an implicit msg object containing context about the transaction. The two most important properties are msg.sender and msg.value.
msg.sender
msg.sender is the address of the entity that called the current function. It could be an externally owned account (a wallet) or another contract.
contract AccessControlled {
address public owner;
constructor() {
owner = msg.sender; // whoever deployed this contract
}
function adminAction() external {
require(msg.sender == owner, 'Only owner');
// ...
}
}msg.sender in call chains
When Contract A calls Contract B, inside Contract B, msg.sender is Contract A's address — not the original wallet. This is a critical distinction for security.
// Wallet → Contract A → Contract B
// Inside Contract B:
// msg.sender == address(Contract A)
// tx.origin == address of the original wallet
// tx.origin is the original sender — but NEVER use it for authentication
// It's exploitable: a malicious contract can trick a user into calling it,
// and then tx.origin will still be the user's wallet.msg.value
msg.value is the amount of ETH (in wei) sent with the function call. It's only accessible in payable functions.
// 1 ether = 1e18 wei
// msg.value is always in wei
function buy() external payable {
require(msg.value >= 0.05 ether, 'Minimum 0.05 ETH');
uint256 tokens = (msg.value * 1000) / 1 ether; // 1000 tokens per ETH
_mint(msg.sender, tokens);
}Other useful msg and block globals
msg.data // full calldata as bytes
msg.sig // first 4 bytes of calldata (function selector)
block.timestamp // current block timestamp (Unix seconds)
block.number // current block number
block.basefee // current block base fee in wei (EIP-1559)
block.chainid // chain ID (1 for mainnet, 11155111 for Sepolia)
tx.gasprice // gas price of the transactionblock.timestamp caution
block.timestamp is set by the block validator and can be manipulated slightly (within ~15 seconds). Never use it as a source of randomness or for precise timing — use it only for broad time ranges (hours, days, weeks).
// Fine: broad time checks
require(block.timestamp >= vestingStart + 30 days, 'Still vesting');
// Dangerous: using for randomness
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp)));
// Validators can influence this — don't use it for anything with value at stake