Vyper
Security scanning for Vyper smart contracts. Vyper is a Python-like smart contract language designed for security and simplicity. Its limited feature set...
Last updated: January 14, 2026
Vyper Guide
Security scanning for Vyper smart contracts.
Overview
Vyper is a Python-like smart contract language designed for security and simplicity. Its limited feature set intentionally reduces attack surface.
Supported Versions
| Version | Support | Notes |
|---|---|---|
| 0.3.x | Full | Recommended |
| 0.2.x | Full | Legacy support |
| 0.1.x | Limited | Basic analysis |
Available Scanners
| Scanner | Focus | Description |
|---|---|---|
| Vyper Analyzer | Core security | Vyper-specific vulnerability detection |
| Moccasin | Framework-aware | Modern Vyper tooling integration |
Vyper Security Features
Vyper has built-in security features:
Bounds Checking
All array accesses are bounds-checked:
@external
def get_item(index: uint256) -> uint256:
# Automatic bounds check - reverts if out of range
return self.items[index]
Integer Overflow Protection
Arithmetic operations revert on overflow:
@external
def add(a: uint256, b: uint256) -> uint256:
# Reverts on overflow automatically
return a + b
No Recursive Calls
Reentrancy is prevented by design:
# Vyper doesn't allow recursive external calls
# This pattern is not possible in Vyper
No Modifiers
Explicit function logic prevents hidden complexity:
@external
def restricted_function():
# Access control must be explicit
assert msg.sender == self.owner, "Not owner"
# ... rest of function
Project Setup
Directory Structure
project/
├── contracts/ # Vyper contracts
│ ├── Token.vy
│ └── Vault.vy
├── interfaces/ # Interface definitions
│ └── IERC20.vy
└── ape-config.yaml # Ape framework config
What to Upload
Include:
- All
.vyfiles - Interface files
- Configuration files
Exclude:
- Test files (optional)
- Build artifacts
Common Vulnerability Patterns
Even with Vyper's safety features, vulnerabilities can occur:
Access Control Issues
# BAD: No access control
@external
def set_owner(new_owner: address):
self.owner = new_owner
# GOOD: Proper access control
@external
def set_owner(new_owner: address):
assert msg.sender == self.owner, "Not owner"
self.owner = new_owner
Incorrect Comparisons
# BAD: Using == for address comparison
@external
def is_owner(addr: address) -> bool:
return addr == empty(address) # Always check explicitly
# GOOD: Explicit comparison
@external
def is_owner(addr: address) -> bool:
return addr == self.owner
Unprotected Initialization
# BAD: Can be called multiple times
@external
def initialize(owner: address):
self.owner = owner
# GOOD: One-time initialization
@external
def initialize(owner: address):
assert self.owner == empty(address), "Already initialized"
self.owner = owner
External Call Issues
# BAD: No return value check
@external
def transfer_out(to: address, amount: uint256):
ERC20(self.token).transfer(to, amount)
# GOOD: Check return value
@external
def transfer_out(to: address, amount: uint256):
success: bool = ERC20(self.token).transfer(to, amount)
assert success, "Transfer failed"
Scanner Detection
Vyper Analyzer Detects
| Issue | Description |
|---|---|
| Missing access control | Functions without ownership checks |
| Uninitialized storage | Storage not set in constructor |
| External call issues | Unchecked return values |
| Logic errors | Incorrect comparisons |
| State consistency | Invariant violations |
Moccasin Detects
| Issue | Description |
|---|---|
| Framework misuse | Incorrect Ape/Brownie patterns |
| Configuration issues | Invalid settings |
| Dependency problems | Version conflicts |
| Test coverage gaps | Untested code paths |
Best Practices
Contract Structure
# @version 0.3.9
"""
@title Secure Token
@author Your Name
@notice ERC20 token with security features
"""
from vyper.interfaces import ERC20
implements: ERC20
# State variables (explicit types)
name: public(String[32])
symbol: public(String[8])
decimals: public(uint8)
totalSupply: public(uint256)
balanceOf: public(HashMap[address, uint256])
allowance: public(HashMap[address, HashMap[address, uint256]])
owner: address
initialized: bool
# Events
event Transfer:
sender: indexed(address)
receiver: indexed(address)
amount: uint256
event Approval:
owner: indexed(address)
spender: indexed(address)
amount: uint256
@external
def __init__():
# Initialize in constructor
self.owner = msg.sender
self.initialized = True
@external
def transfer(to: address, amount: uint256) -> bool:
# Input validation
assert to != empty(address), "Invalid recipient"
assert self.balanceOf[msg.sender] >= amount, "Insufficient balance"
# State changes
self.balanceOf[msg.sender] -= amount
self.balanceOf[to] += amount
# Event emission
log Transfer(msg.sender, to, amount)
return True
Security Patterns
- Explicit Access Control: Always check
msg.sender - Input Validation: Validate all parameters
- Event Emission: Log all state changes
- Clear Logic: Avoid complex nested conditions
- Interface Compliance: Implement standard interfaces fully
Vyper vs Solidity Security
| Feature | Vyper | Solidity |
|---|---|---|
| Reentrancy | Prevented by design | Requires guards |
| Overflow | Built-in protection | 0.8.0+ protected |
| Modifiers | Not available | Can hide logic |
| Inheritance | Single only | Multiple (diamond problem) |
| Inline Assembly | Not available | Available (risky) |
| Recursive Calls | Not allowed | Allowed |
Framework Integration
Ape Framework
# ape-config.yaml
name: my-vyper-project
plugins:
- name: vyper
- name: solidity # For Solidity interfaces
dependencies:
- name: snekmate
github: pcaversaccio/snekmate
compiler:
vyper:
version: 0.3.9
Brownie
# brownie-config.yaml
compiler:
vyper:
version: "0.3.9"
Example Results
Vyper Analyzer Finding
Vault.vy:45 - Missing Access Control
Function set_fee() can be called by anyone
Recommendation: Add owner check
Severity: High
Confidence: High
Moccasin Finding
Token.vy:12 - Unchecked External Call
ERC20.transfer() return value not checked
Recommendation: Assert success
Severity: Medium
Confidence: High
Troubleshooting
Compilation Issues
"Invalid version pragma"
- Check
# @versioncomment at top of file - Ensure version is supported
"Interface not found"
- Include interface files in upload
- Check import paths
Scanner Issues
Low detection rate
- Vyper's design prevents many issues
- Fewer findings often indicates secure code
- Focus on logic and access control
Next Steps
- Language Guides Overview - Other languages
- Framework Guides - Ape setup
- Security Best Practices - Secure coding