Smart contracts are the building blocks of decentralized applications (dApps) on blockchain platforms like Ethereum. They enable developers to create self-executing logic that runs exactly as programmed—without downtime, censorship, or third-party interference. In this guide, you'll learn how to write and deploy a basic smart contract using Solidity, implement token creation, balance queries, transfers, and understand core concepts like gas and error handling.
Whether you're new to blockchain development or looking to deepen your understanding of smart contract mechanics, this hands-on tutorial will walk you through every step—from writing code in Remix IDE to deploying and interacting with your own token on the Ethereum Virtual Machine (EVM).
Understanding the Basics of Smart Contracts
A smart contract is essentially a program stored on a blockchain that executes automatically when predetermined conditions are met. These contracts allow for trusted transactions and agreements between anonymous parties without the need for a central authority.
In this article, we’ll focus on practical implementation:
- Storing data on-chain
- Creating a custom token
- Minting new tokens
- Transferring tokens between addresses
- Querying account balances
We’ll use Solidity, the most widely adopted language for Ethereum smart contracts, and the Remix IDE, a browser-based tool ideal for beginners.
👉 Start experimenting with smart contracts today using a secure development environment.
Setting Up Your Development Environment
Before diving into coding, ensure your tools are ready:
- Remix IDE: A web-based Solidity editor with built-in compiler and deployment tools.
- Solidity Documentation: Refer to the official English documentation for the latest syntax and best practices.
No installation is required—just open Remix in your browser and begin writing code.
Writing Your First Smart Contract: Storing Data On-Chain
Let’s start simple by creating a contract that stores and retrieves a number.
pragma solidity ^0.8.0;
contract SimpleStorage {
uint storedData;
function set(uint x) public {
storedData = x;
}
function get() public view returns (uint) {
return storedData;
}
}How It Works:
storedDatais a state variable that persists on the blockchain.set()updates the value (modifies state → costs gas).get()reads the value (view function → free to call).
Deployment Steps:
- Paste the code into Remix.
- Compile it using the "Compile" tab.
- Switch to the "Deploy & Run Transactions" tab.
- Deploy the contract.
- Use the
set()function to store a number (e.g., 77). - Call
get()to retrieve it.
🔹 Blue buttons indicate read-only functions (view,pure) — they don’t cost gas.
🔸 Yellow buttons modify blockchain state and require gas fees.
This simple example demonstrates the full lifecycle: write → compile → deploy → interact.
Creating a Custom Token
Now let’s build a functional ERC-like token with minting and transfer capabilities.
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.4;
contract Coin {
address public minter;
mapping (address => uint) public balances;
event Sent(address from, address to, uint amount);
constructor() {
minter = msg.sender;
}
function mint(address receiver, uint amount) public {
require(msg.sender == minter);
balances[receiver] += amount;
}
error InsufficientBalance(uint requested, uint available);
function send(address receiver, uint amount) public {
if (amount > balances[msg.sender])
revert InsufficientBalance({ requested: amount, available: balances[msg.sender] });
balances[msg.sender] -= amount;
balances[receiver] += amount;
emit Sent(msg.sender, receiver, amount);
}
}Key Features Explained:
minter: Only the deployer can mint new tokens.balances: Tracks token holdings per address.mint(): Allows controlled token issuance.send(): Enables peer-to-peer transfers with safety checks.event Sent: Logs all transfers for off-chain monitoring.
Error Handling in Solidity
Two main approaches:
require(condition, "message"): Validates inputs; reverts if false, refunds unused gas.revert(error)orrevert("message"): Immediately aborts execution with custom messages.
✅ Userequirefor input validation.
❌ Useassertonly for internal invariants—it consumes all gas if failed.
👉 Explore secure smart contract patterns used by leading dApps.
Deploying and Testing Your Token
- Deploy the contract via Remix using Account A (
0x5B38...). - Check initial balance: call
balances(AccountA)→ returns 0. Mint tokens:
- Call
mint(AccountA, 1000000000000000000)→ issues 1 full token (in wei). - Recheck balance → now shows 1e18.
- Call
Transfer tokens:
- Use Account B (
0xAb84...) as recipient. - Call
send(AccountB, 100).
- Use Account B (
Verify:
balances(AccountB)returns 100.- Event log shows
Sent(AccountA, AccountB, 100).
You’ve now successfully created and managed your own digital asset!
Understanding the Ethereum Virtual Machine (EVM)
All smart contracts run inside the EVM—an isolated runtime environment that ensures security and consistency across nodes.
Core Concepts:
- Gas: Every operation consumes gas to prevent spam and allocate resources fairly.
- Gas Price: Set by users; determines transaction priority (higher = faster).
- Transaction Cost =
gas used × gas price.
If gas runs out during execution:
- The transaction fails.
- State changes are reverted.
- Gas already spent is not refunded.
💡 Always estimate gas before sending transactions—especially when integrating with web3.js or wallets.
Frequently Asked Questions (FAQ)
Q1: What is a smart contract?
A smart contract is a self-executing program deployed on a blockchain that automatically enforces rules and facilitates trustless interactions between parties without intermediaries.
Q2: Can anyone edit a deployed smart contract?
No. Once deployed, a smart contract’s code is immutable unless specifically designed with upgradeability features (e.g., proxy patterns). This immutability ensures transparency and security.
Q3: Why does writing data cost gas but reading doesn’t?
Writing alters the global state of the blockchain, requiring consensus and storage across all nodes—hence the cost. Reading (view, pure) functions only query local node data and do not affect state.
Q4: What’s the difference between require and revert?
require checks conditions and reverts if false, often used for input validation. revert triggers an explicit exception with optional custom errors. Both refund unused gas.
Q5: How do I prevent unauthorized minting?
Restrict sensitive functions using access control modifiers like onlyOwner or role-based permissions. In our example, we used require(msg.sender == minter) for this purpose.
Q6: Is it safe to test contracts on Remix?
Yes—for learning and testing. However, always audit code before deploying to mainnet. Use testnets like Sepolia or Goerli for real-world simulations.
Final Thoughts
Building your first smart contract is an exciting milestone in blockchain development. You’ve learned how to:
- Store and retrieve data on Ethereum,
- Create a functional token with minting and transfer logic,
- Handle errors securely,
- Interact with contracts via Remix.
As you progress, consider exploring advanced topics like:
- Token standards (ERC-20, ERC-721),
- Upgradeable contracts,
- Security best practices (reentrancy guards, input sanitization),
- Integration with frontend dApps using Web3.js or Ethers.js.
The world of decentralized finance and Web3 awaits—with every line of code bringing you closer to innovation.
👉 Dive deeper into blockchain development with tools trusted by professionals.