🏠 Home
Beginner
01 — Introduction to Solidity 02 — Setting Up Your Environment 03 — Your First Smart Contract 04 — Data Types & Variables 05 — Functions & Visibility 06 — Control Flow 07 — Arrays & Mappings 08 — Structs & Enums
Intermediate
09 — Events & Logging 10 — Modifiers 11 — Inheritance 12 — Interfaces & Abstract Contracts 13 — Error Handling 14 — Ether & Wei 15 — Payable Functions 16 — msg.sender & msg.value 17 — Storage vs Memory vs Stack
Advanced
18 — Gas Optimization 19 — ERC-20 Tokens 20 — ERC-721 NFT Standard 21 — Contract Security 22 — Reentrancy Attacks 23 — Oracles & Chainlink 24 — Upgradeable Contracts 25 — Deploying to Mainnet
SolidityMaster / Lesson 16
Lesson 16 of 25

msg.sender & msg.value

Global variables — caller context, ETH value, and block information.

Intermediate

Transaction Context Globals

Solidity provides built-in global variables that give you context about the current transaction and block. These are the most commonly used.
Solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract ContextDemo {
    function getContext() public payable returns (
        address sender,
        uint256 value,
        uint256 gasLeft,
        uint256 blockNum,
        uint256 timestamp
    ) {
        sender    = msg.sender;          // who called this function
        value     = msg.value;           // how much ETH was sent
        gasLeft   = gasleft();           // remaining gas
        blockNum  = block.number;        // current block number
        timestamp = block.timestamp;     // current block timestamp (Unix)
    }
}

msg.sender in Access Control

msg.sender changes depending on who calls the function. When contracts call other contracts, msg.sender is the calling contract's address — not the original EOA.
Solidity
contract AccessControl {
    address public owner;
    mapping(address => bool) public admins;

    constructor() { owner = msg.sender; }

    modifier onlyOwner() {
        require(msg.sender == owner, "Not owner");
        _;
    }

    modifier onlyAdmin() {
        require(admins[msg.sender] || msg.sender == owner, "Not admin");
        _;
    }

    function addAdmin(address admin) public onlyOwner {
        admins[admin] = true;
    }

    function adminAction() public onlyAdmin {
        // accessible to owner AND admins
    }
}

tx.origin vs msg.sender

tx.origin is the original EOA that started the transaction chain. NEVER use tx.origin for authorization — it is vulnerable to phishing attacks where a malicious contract tricks a user into calling it.
Solidity
// ❌ DANGEROUS — vulnerable to phishing
contract Unsafe {
    address owner;
    function withdraw() public {
        require(tx.origin == owner); // WRONG!
        // A malicious contract can trick the owner into calling it,
        // which then calls this withdraw() — tx.origin is still owner!
    }
}

// ✅ SAFE — use msg.sender
contract Safe {
    address owner;
    function withdraw() public {
        require(msg.sender == owner); // msg.sender = direct caller only
    }
}