5/5
## Securing Withdrawals with `msg.sender` in Vyper When building smart contracts that handle funds, such as a "Buy Me a Coffee" contract where users can send cryptocurrency, a critical security concern arises: how do you ensure only the intended recipient can withdraw the collected funds? If anyone could call the `withdraw` function, the funds wouldn't be safe. This lesson demonstrates a fundamental pattern for implementing access control in Vyper, specifically ensuring only the contract owner can withdraw funds using the `msg.sender` global variable. The core solution involves two key steps: 1. Storing the contract owner's address permanently within the contract's storage. 2. Verifying the caller's identity against this stored address whenever a restricted action, like withdrawing funds, is attempted. We achieve this using several core Vyper features: **1. State Variables:** Variables declared at the contract level persist on the blockchain between transactions. We'll use a state variable to store the owner's address. To access state variables within functions, we use the `self.` prefix. **2. The `__init__` Function (Constructor):** Decorated with `@deploy`, this special function executes only *once* when the contract is first deployed to the blockchain. It's the ideal place for initial setup, including defining the contract's owner. **3. `msg.sender` Global Variable:** Vyper provides a built-in global variable `msg.sender` of type `address`. This variable always holds the address that *directly called* the current function. * During contract deployment (inside `__init__`), `msg.sender` is the address of the account deploying the contract. * During a regular function call (like `withdraw`), `msg.sender` is the address of the account or contract initiating that specific transaction. **4. The `assert` Statement:** `assert` is used to check for conditions that *must* be true for the contract to function correctly. If the condition provided to `assert` evaluates to `False`, the entire transaction is reverted. This means any state changes are undone, and the remaining gas is consumed. It's commonly used for enforcing access control rules and validating internal states. The syntax is `assert <condition>, <error_message>`. **Implementation Steps:** Let's see how these components work together to secure a `withdraw` function. **Step 1: Define the Owner State Variable** First, declare an `address` state variable at the contract level to store the owner's address. We'll also make it `public`, which automatically creates a getter function allowing anyone to query the owner's address (useful for verification). ```vyper # State variable declaration owner: public(address) ``` **Step 2: Set the Owner During Deployment** Inside the `__init__` function, assign the address of the deployer (`msg.sender`) to the `owner` state variable. This permanently sets the account deploying the contract as its owner. ```vyper @deploy def __init__(): # Other initialization logic (e.g., setting price feeds, minimum amounts) might go here # ... # Set the contract deployer as the owner self.owner = msg.sender ``` Because `__init__` runs only once at deployment, `self.owner` is assigned the deployer's address immutably at that moment. **Step 3: Implement Access Control in `withdraw`** Now, in the `withdraw` function, use an `assert` statement to check if the current caller (`msg.sender`) is the same as the stored owner (`self.owner`). ```vyper @external def withdraw(): """Allows the owner to withdraw the contract's balance.""" # Check if the caller is the stored owner assert msg.sender == self.owner, "Only the contract owner can withdraw!" # If the assert passes, proceed with the withdrawal logic # (Actual withdrawal code would go here, e.g., transferring self.balance) pass ``` **How It Works in Practice** When any account calls the `withdraw` function: 1. The Vyper runtime evaluates the condition `msg.sender == self.owner`. 2. It compares the address of the account making the current call (`msg.sender`) against the address stored in the `self.owner` state variable (which was set during deployment). 3. **If the addresses match:** The condition is `True`. The `assert` passes, and the rest of the function code executes, allowing the owner to withdraw funds. 4. **If the addresses do *not* match:** The condition is `False`. The `assert` statement triggers a revert. The transaction stops immediately, any state changes within this transaction are rolled back, and the specified error message ("Only the contract owner can withdraw!") is returned. The non-owner caller is prevented from withdrawing funds. **Conclusion** Using `msg.sender` within the `__init__` function to set an `owner` state variable, and then checking `msg.sender == self.owner` with an `assert` statement in privileged functions like `withdraw`, is a standard, secure, and fundamental pattern for implementing owner-based access control in Vyper smart contracts. This ensures that critical actions can only be performed by the designated owner, significantly enhancing the security and integrity of your decentralized application.
A fundamental guide to Securing Withdrawals with msg.sender in Vyper - Learn how to implement owner-only access control by storing the deployer's address using `__init__` and `msg.sender`. Discover how `assert` statements verify the caller, preventing unauthorized function execution like withdrawals.
Previous lesson
Previous
Next lesson
Next
Give us feedback
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025
Duration: 2h 08min
Duration: 2h 32min
Duration: 24min
Course Overview
About the course
The basics of blockchain transactions, how to send and receive money on a blockchain network.
How to write Python based smart contracts using Vyper.
How to read and understand Vyper smart contracts.
Vyper data structures, arrays, structs, hash maps.
How to build a smart contract application and deploy on ZKsync with Moccasin.
Smart Contract Auditor
$100,000 - $200,000 (avg. salary)
On-chain Data Analyst
$59,000 - $139,000 (avg. salary)
DeFi Developer
$75,000 - $200,000 (avg. salary)
Smart Contract Engineer
$100,000 - $150,000 (avg. salary)
Web3 developer
$60,000 - $150,000 (avg. salary)
Web3 Developer Relations
$85,000 - $125,000 (avg. salary)
Last updated on June 11, 2025