msg.sender and msg.value

The two most important global variables in Solidity — understanding them is understanding who's calling and how much they're sending.

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.

solidity
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.

solidity
// 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.

solidity
// 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

solidity
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 transaction

block.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).

solidity
// 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
←   Sending and receiving EtherThe ERC-20 standard   →