Smart Contract Development 101
Back to Guides
🔴 advanced
smart contracts
18 min read

Smart Contract Development 101

Write, test, and deploy your first smart contract with Solidity - complete walkthrough from Remix IDE to mainnet deployment.

Smart Contract Development 101

Smart contracts are self-executing programs stored on the blockchain. This guide walks you through writing, testing, and deploying your first smart contract on Ethereum using Solidity.


A simple storage contract that lets users:

  • Store and retrieve a number on-chain
  • Increment and decrement that number
  • Track who last updated the value
NOTE

Prerequisites: Basic programming knowledge (variables, functions, types). No blockchain experience required.


bash
mkdir my-first-contract
cd my-first-contract
npm init -y
npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox
npx hardhat init  # Choose "Create a JavaScript project"

solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

contract SimpleStorage {
    // State variable - stored permanently on-chain
    uint256 private storedNumber;
    
    // Track who last interacted
    address public lastUpdater;
    
    // Event - logged on-chain for frontends to listen to
    event NumberChanged(uint256 newValue, address changedBy);
    
    // Store a new number
    function set(uint256 _number) public {
        storedNumber = _number;
        lastUpdater = msg.sender;
        emit NumberChanged(_number, msg.sender);
    }
    
    // Retrieve the stored number
    function get() public view returns (uint256) {
        return storedNumber;
    }
    
    // Increment by 1
    function increment() public {
        storedNumber += 1;
        lastUpdater = msg.sender;
        emit NumberChanged(storedNumber, msg.sender);
    }
    
    // Decrement by 1 (with safety check)
    function decrement() public {
        require(storedNumber > 0, "Already zero - cannot decrement");
        storedNumber -= 1;
        lastUpdater = msg.sender;
        emit NumberChanged(storedNumber, msg.sender);
    }
}

| Component | Purpose | |---|---| |

pragma solidity ^0.8.19
| Specifies the compiler version | |
contract SimpleStorage
| Defines the contract (like a class) | |
uint256 private storedNumber
| State variable - persists on-chain | |
msg.sender
| Address of whoever called the function | |
view
| Function does not modify state (free to call) | |
event
| Logs data on-chain for UIs to read | |
require()
| Validates conditions, reverts if false |


  1. Click the Solidity Compiler tab (left sidebar, S icon)
  2. Select compiler version
    0.8.19
  3. Click Compile Storage.sol
  4. Green checkmark = success
  1. Click the Deploy & Run tab (Ethereum logo icon)
  2. Under "Environment", select Injected Provider - MetaMask
  3. Make sure MetaMask is connected to Sepolia testnet
  4. Click the orange Deploy button
  5. Confirm the transaction in MetaMask
  6. Wait ~15 seconds for confirmation

Once deployed, your contract appears under "Deployed Contracts":

  • Click
    set
    → enter a number → confirm → check
    get
    to verify
  • Click
    increment
    → confirm → the number increases by 1
  • Click
    lastUpdater
    → shows your wallet address

Testnets let you deploy and test for free using test ETH:

| Network | Faucet | |---|---| | Sepolia (Ethereum) | sepoliafaucet.com | | Goerli (Ethereum) | goerlifaucet.com | | Mumbai (Polygon) | mumbaifaucet.com |

Never deploy to mainnet until you have tested on a testnet first.


Every operation on Ethereum costs gas:

| Operation | Approximate Gas | |---|---| | Simple transfer (ETH) | 21,000 | | Token transfer (ERC-20) | ~45,000 | | Deploy simple contract | ~150,000-300,000 | | Complex DeFi interaction | 100,000-500,000 |

Gas price fluctuates with network demand. Use etherscan.io/gastracker to check current rates.

Cost formula:

Gas used x Gas price (in gwei) = Total cost in ETH


solidity
bool public isActive;        // true or false
uint256 public balance;      // unsigned integer (0 to 2^256-1)
int256 public temperature;   // signed integer
address public owner;        // Ethereum address (20 bytes)
string public name;          // text string
bytes32 public hash;         // fixed-size byte array

// Mappings (like dictionaries/hashmaps)
mapping(address => uint256) public balances;

// Arrays
uint256[] public numbers;
address[] public whitelist;

// Structs (custom types)
struct User {
    string name;
    uint256 age;
    bool isActive;
}
mapping(address => User) public users;

| Modifier | Who Can Call | |---|---| |

public
| Anyone (external + contract itself) | |
external
| Only from outside the contract | |
internal
| Only this contract and inherited contracts | |
private
| Only this contract |

solidity
// Custom access control
modifier onlyOwner() {
    require(msg.sender == owner, "Not the owner");
    _;  // Continue executing the function
}

function withdraw() public onlyOwner {
    // Only the owner can call this
    payable(owner).transfer(address(this).balance);
}

Foundry is a fast testing framework written in Solidity:

bash
# Install Foundry
curl -L https://foundry.paradigm.xyz | bash
foundryup

# Create test file: test/Storage.t.sol
solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import "forge-std/Test.sol";
import "../src/Storage.sol";

contract StorageTest is Test {
    SimpleStorage public storage;
    
    function setUp() public {
        storage = new SimpleStorage();
    }
    
    function testSetAndGet() public {
        storage.set(42);
        assertEq(storage.get(), 42);
    }
    
    function testIncrement() public {
        storage.set(10);
        storage.increment();
        assertEq(storage.get(), 11);
    }
    
    function testDecrementRevertsAtZero() public {
        storage.set(0);
        vm.expectRevert("Already zero - cannot decrement");
        storage.decrement();
    }
}
bash
forge test          # Run all tests
forge test -vvv     # Verbose output
forge coverage      # Line coverage report

NOTE

80% of smart contract hacks come from the same 5 vulnerability types.

  1. Reentrancy: Always update state before making external calls
  2. Integer overflow: Solidity 0.8+ protects by default
  3. Access control: Use
    onlyOwner
    or OpenZeppelin
    Ownable
  4. Front-running: Use commit-reveal patterns for sensitive operations
  5. Oracle manipulation: Never use spot DEX prices as oracles

Use OpenZeppelin contracts instead of writing your own:

solidity
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract MyToken is ERC20, Ownable, ReentrancyGuard {
    constructor() ERC20("MyToken", "MTK") {
        _mint(msg.sender, 1_000_000 * 10**decimals());
    }
}

When you are ready for real ETH:

  1. Get audited by a reputable firm (Trail of Bits, OpenZeppelin, Consensys Diligence)
  2. Launch a bug bounty on Immunefi
  3. Use a multisig wallet (Safe) for admin control
  4. Deploy via a deployment script, not Remix
  5. Verify your contract on Etherscan for transparency

  1. Complete this tutorial - deploy to Sepolia testnet
  2. Build an ERC-20 token using OpenZeppelin
  3. Build an ERC-721 NFT collection
  4. Study the Solidity docs
  5. Complete challenges on CryptoZombies
  6. Compete in Code4rena audit contests
  7. Contribute to open-source protocols

Happy building! Remember: test everything, respect user funds, and get audited.

Found this guide helpful? Share it with others