ποΈ Writing Solidity Functions
Your Blueprint for Building Smart Contracts
The Big Picture: Functions are Like Doors
Imagine a smart contract as a magical vending machine. π°
- The machine has different buttons (functions) you can press
- Some buttons anyone can press (public)
- Some buttons only the owner has a key for (private)
- Some buttons need you to put in coins first (payable)
- And some buttons just show you information without changing anything (view)
Thatβs exactly how Solidity functions work!
π― What is a Solidity Function?
A function is a set of instructions that does something when you call it.
function sayHello() public pure
returns (string memory) {
return "Hello, World!";
}
Think of it like a recipe:
- Name:
sayHello(what we call it) - Who can use it:
public(everyone!) - What it gives back: a message
π Function Visibility: Who Can Press the Button?
There are 4 types of visibility β like 4 different locks on doors:
graph TD A["Function Visibility"] --> B["π public"] A --> C["π private"] A --> D["π internal"] A --> E["π external"] B --> B1["Anyone can call"] C --> C1["Only this contract"] D --> D1["This + child contracts"] E --> E1["Only from outside"]
The 4 Locks Explained:
| Visibility | Who Can Use It? | Real-Life Example |
|---|---|---|
public |
Everyone | Front door of a shop |
private |
Only this contract | Your secret diary |
internal |
This + inherited contracts | Family recipe |
external |
Only outsiders | Drive-thru window |
Quick Examples:
// Anyone can call this
function getBalance() public view
returns (uint) {
return balance;
}
// Only this contract can use
function _secretCalc() private pure
returns (uint) {
return 42;
}
// This contract + children
function _familySecret() internal view
returns (uint) {
return treasureLocation;
}
// Only call from outside
function deposit() external payable {
balance += msg.value;
}
π Constructors: The Birth Certificate
A constructor runs once β the moment your contract is born.
Itβs like setting up a new phone:
- Choose your name
- Set your password
- Pick your settings
contract MyToken {
string public name;
address public owner;
// Runs ONCE when deployed
constructor(string memory _name) {
name = _name;
owner = msg.sender;
}
}
Key Points:
- β No function keyword needed
- β Can take parameters
- β Sets up initial state
- β Cannot be called again ever
π‘οΈ Function Modifiers: The Security Guards
Modifiers are checkpoints before your function runs.
Think of a nightclub bouncer:
- βAre you on the list?β β
- βAre you old enough?β β
- βOK, you can enter!β π
// The bouncer definition
modifier onlyOwner() {
require(
msg.sender == owner,
"Not the owner!"
);
_; // Continue to function
}
// Using the bouncer
function withdraw() public onlyOwner {
// Only owner gets here!
payable(owner).transfer(balance);
}
The Magic _; Symbol
The underscore _; means: βNow run the actual functionβ
graph LR A["Call Function"] --> B["Check Modifier"] B --> C{Pass Check?} C -->|Yes| D["Run Function Code"] C -->|No| E["Revert with Error"]
Stacking Multiple Modifiers:
modifier notPaused() {
require(!paused, "Contract paused");
_;
}
// Both guards must pass!
function transfer()
public
onlyOwner
notPaused
{
// Safe to run
}
π Read-Only Functions: Just Looking, Not Touching
Two types of functions that promise not to change anything:
view Functions β Reading State
uint public balance = 100;
// Can READ state variables
function getBalance() public view
returns (uint) {
return balance;
}
pure Functions β No State Access
// Cannot read OR write state
function add(uint a, uint b)
public pure
returns (uint) {
return a + b;
}
The Difference:
| Type | Can Read State? | Can Modify State? | Gas Cost |
|---|---|---|---|
view |
β Yes | β No | Free* |
pure |
β No | β No | Free* |
*Free when called externally, not from a transaction
π° Payable Functions: Show Me The Money!
Payable functions can receive ETH (Ether).
Without payable, the function rejects any money sent to it.
contract Donation {
// Can receive ETH
function donate() public payable {
// msg.value = amount sent
emit Received(msg.sender, msg.value);
}
// Cannot receive ETH
function normalFunc() public {
// Reverts if ETH sent!
}
}
Real Example β Crowdfunding:
contract Crowdfund {
uint public goal = 10 ether;
uint public raised;
function contribute() public payable {
require(msg.value > 0, "Send ETH!");
raised += msg.value;
}
function goalReached() public view
returns (bool) {
return raised >= goal;
}
}
π¨ Fallback and Receive: The Safety Nets
What happens when someone sends ETH with no function call?
Or calls a function that doesnβt exist?
receive() β Plain ETH Transfers
// Triggered when ETH sent with no data
receive() external payable {
emit Received(msg.sender, msg.value);
}
fallback() β Everything Else
// Triggered when:
// 1. Function doesn't exist
// 2. ETH sent without receive()
fallback() external payable {
emit FallbackCalled(msg.data);
}
The Decision Flow:
graph TD A["ETH Sent to Contract"] --> B{msg.data empty?} B -->|Yes| C{receive exists?} B -->|No| D{Function exists?} C -->|Yes| E["Call receive"] C -->|No| F["Call fallback"] D -->|Yes| G["Call that function"] D -->|No| F F --> H{fallback payable?} H -->|Yes| I["Accept ETH"] H -->|No| J["Revert!"]
Complete Example:
contract Wallet {
event Deposit(address, uint);
event Fallback(bytes);
// Plain ETH transfers
receive() external payable {
emit Deposit(msg.sender, msg.value);
}
// Unknown calls or ETH with data
fallback() external payable {
emit Fallback(msg.data);
}
}
π Putting It All Together
Hereβs a complete contract using everything we learned:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract VendingMachine {
address public owner;
uint public itemPrice = 0.01 ether;
uint public stock = 100;
// CONSTRUCTOR - runs once
constructor() {
owner = msg.sender;
}
// MODIFIER - security check
modifier onlyOwner() {
require(msg.sender == owner);
_;
}
// PAYABLE - accepts ETH
function buyItem() public payable {
require(msg.value >= itemPrice);
require(stock > 0);
stock--;
}
// VIEW - read only
function getStock() public view
returns (uint) {
return stock;
}
// PURE - no state access
function calculate(uint qty)
public pure returns (uint) {
return qty * 0.01 ether;
}
// PRIVATE - internal only
function _resetStock() private {
stock = 100;
}
// EXTERNAL + MODIFIER
function withdraw() external onlyOwner {
payable(owner).transfer(
address(this).balance
);
}
// RECEIVE - plain ETH
receive() external payable {}
// FALLBACK - unknown calls
fallback() external payable {}
}
π Quick Reference
| Concept | Purpose | Keyword |
|---|---|---|
| Visibility | Who can call | public private internal external |
| Constructor | Initial setup | constructor() |
| Modifier | Add checks | modifier name() { _; } |
| Read-only | No changes | view or pure |
| Accept ETH | Receive money | payable |
| Safety net | Handle unknown | receive() fallback() |
π‘ Remember This!
π° Functions = Buttons on your smart contract vending machine
public= Everyoneβs buttonprivate= Secret buttonpayable= Coin slot buttonview/pure= Display screen (just shows info)receive/fallback= βInsert coins hereβ slot
You now understand Solidity functions! π
Each function type has its purpose. Mix them together to build powerful, secure smart contracts.
