Blockchain Solidity

Ethereum Smart Contract
Geliştirme - Solidity 101

Sıfırdan başlayarak Ethereum blockchain üzerinde akıllı sözleşme geliştirmeyi öğrenin

11 Kasım 2025 20 dk okuma WebKodlama.net

$2000+

ETH Fiyatı
(Yaklaşık)

1M+

Aktif Smart
Contract

50 Gwei

Ortalama
Gas Fiyatı

Smart Contract Nedir?

Smart Contract (Akıllı Sözleşme), Ethereum blockchain ağı üzerinde çalışan, önceden tanımlanmış koşulları otomatik olarak yerine getiren programlardır. Bu sözleşmeler, merkezi bir otoriteye ihtiyaç duymadan güvenli ve şeffaf işlemler yapılmasını sağlar.

1. Solidity Temelleri

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

contract SimpleStorage {
    // State variable
    uint256 private favoriteNumber;
    
    // Events
    event NumberChanged(uint256 newNumber, address changer);
    
    // Function to store a number
    function store(uint256 _favoriteNumber) public {
        favoriteNumber = _favoriteNumber;
        emit NumberChanged(_favoriteNumber, msg.sender);
    }
    
    // Function to retrieve the number
    function retrieve() public view returns (uint256) {
        return favoriteNumber;
    }
}

2. Veri Tipleri ve Yapıları

pragma solidity ^0.8.19;

contract DataTypes {
    // Elementary types
    bool public isActive = true;
    uint256 public myUint = 123;
    int256 public myInt = -123;
    address public myAddress = 0x1234567890123456789012345678901234567890;
    bytes32 public myBytes = "Hello World";
    string public myString = "Hello Ethereum";
    
    // Arrays
    uint256[] public dynamicArray;
    uint256[5] public fixedArray;
    
    // Mappings
    mapping(address => uint256) public balances;
    mapping(address => mapping(address => bool)) public approvals;
    
    // Structs
    struct Person {
        string name;
        uint256 age;
        bool isActive;
    }
    
    Person[] public people;
    mapping(address => Person) public personMapping;
    
    // Enums
    enum Status {
        Pending,
        Active,
        Inactive
    }
    
    Status public currentStatus = Status.Pending;
}

3. ERC-20 Token Contract

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

interface IERC20 {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function approve(address spender, uint256 amount) external returns (bool);
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
}

contract MyToken is IERC20 {
    mapping(address => uint256) private _balances;
    mapping(address => mapping(address => uint256)) private _allowances;
    
    string public name = "My Token";
    string public symbol = "MTK";
    uint8 public decimals = 18;
    uint256 private _totalSupply;
    address public owner;
    
    modifier onlyOwner() {
        require(msg.sender == owner, "Not the owner");
        _;
    }
    
    constructor(uint256 _initialSupply) {
        owner = msg.sender;
        _totalSupply = _initialSupply * 10**decimals;
        _balances[msg.sender] = _totalSupply;
        emit Transfer(address(0), msg.sender, _totalSupply);
    }
    
    function totalSupply() public view override returns (uint256) {
        return _totalSupply;
    }
    
    function balanceOf(address account) public view override returns (uint256) {
        return _balances[account];
    }
    
    function transfer(address recipient, uint256 amount) public override returns (bool) {
        require(recipient != address(0), "Transfer to zero address");
        require(_balances[msg.sender] >= amount, "Insufficient balance");
        
        _balances[msg.sender] -= amount;
        _balances[recipient] += amount;
        
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }
    
    function approve(address spender, uint256 amount) public override returns (bool) {
        _allowances[msg.sender][spender] = amount;
        emit Approval(msg.sender, spender, amount);
        return true;
    }
    
    function allowance(address owner, address spender) public view override returns (uint256) {
        return _allowances[owner][spender];
    }
    
    function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
        require(_allowances[sender][msg.sender] >= amount, "Transfer amount exceeds allowance");
        require(_balances[sender] >= amount, "Insufficient balance");
        
        _balances[sender] -= amount;
        _balances[recipient] += amount;
        _allowances[sender][msg.sender] -= amount;
        
        emit Transfer(sender, recipient, amount);
        return true;
    }
}

4. NFT (ERC-721) Contract

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

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFT is ERC721, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdCounter;
    
    mapping(uint256 => string) private _tokenURIs;
    uint256 public maxSupply = 10000;
    uint256 public mintPrice = 0.01 ether;
    
    constructor() ERC721("My NFT Collection", "MNC") {}
    
    function mint(address to, string memory tokenURI) public payable {
        require(_tokenIdCounter.current() < maxSupply, "Max supply reached");
        require(msg.value >= mintPrice, "Insufficient payment");
        
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        
        _safeMint(to, tokenId);
        _setTokenURI(tokenId, tokenURI);
    }
    
    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal {
        require(_exists(tokenId), "Token does not exist");
        _tokenURIs[tokenId] = _tokenURI;
    }
    
    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "Token does not exist");
        return _tokenURIs[tokenId];
    }
    
    function withdraw() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(owner()).transfer(balance);
    }
}

Güvenlik Uyarısı

Smart contract'ları deploy etmeden önce mutlaka security audit yapın. Reentrancy, Integer Overflow/Underflow, Access Control gibi yaygın güvenlik açıklarına dikkat edin.

5. DeFi Staking Contract

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

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract StakingContract is ReentrancyGuard {
    IERC20 public stakingToken;
    IERC20 public rewardToken;
    
    uint256 public rewardRate = 100; // 100 tokens per second
    uint256 public lastUpdateTime;
    uint256 public rewardPerTokenStored;
    
    mapping(address => uint256) public userRewardPerTokenPaid;
    mapping(address => uint256) public rewards;
    mapping(address => uint256) public balances;
    
    uint256 private _totalSupply;
    
    event Staked(address indexed user, uint256 amount);
    event Withdrawn(address indexed user, uint256 amount);
    event RewardPaid(address indexed user, uint256 reward);
    
    constructor(address _stakingToken, address _rewardToken) {
        stakingToken = IERC20(_stakingToken);
        rewardToken = IERC20(_rewardToken);
    }
    
    modifier updateReward(address account) {
        rewardPerTokenStored = rewardPerToken();
        lastUpdateTime = block.timestamp;
        
        if (account != address(0)) {
            rewards[account] = earned(account);
            userRewardPerTokenPaid[account] = rewardPerTokenStored;
        }
        _;
    }
    
    function rewardPerToken() public view returns (uint256) {
        if (_totalSupply == 0) {
            return rewardPerTokenStored;
        }
        return rewardPerTokenStored + 
               (((block.timestamp - lastUpdateTime) * rewardRate * 1e18) / _totalSupply);
    }
    
    function earned(address account) public view returns (uint256) {
        return ((balances[account] * 
                (rewardPerToken() - userRewardPerTokenPaid[account])) / 1e18) + 
                rewards[account];
    }
    
    function stake(uint256 amount) external nonReentrant updateReward(msg.sender) {
        require(amount > 0, "Cannot stake 0");
        _totalSupply += amount;
        balances[msg.sender] += amount;
        stakingToken.transferFrom(msg.sender, address(this), amount);
        emit Staked(msg.sender, amount);
    }
    
    function withdraw(uint256 amount) public nonReentrant updateReward(msg.sender) {
        require(amount > 0, "Cannot withdraw 0");
        require(balances[msg.sender] >= amount, "Insufficient balance");
        _totalSupply -= amount;
        balances[msg.sender] -= amount;
        stakingToken.transfer(msg.sender, amount);
        emit Withdrawn(msg.sender, amount);
    }
    
    function getReward() public nonReentrant updateReward(msg.sender) {
        uint256 reward = rewards[msg.sender];
        if (reward > 0) {
            rewards[msg.sender] = 0;
            rewardToken.transfer(msg.sender, reward);
            emit RewardPaid(msg.sender, reward);
        }
    }
    
    function exit() external {
        withdraw(balances[msg.sender]);
        getReward();
    }
}

6. Web3.js ile Frontend Entegrasyonu

// Web3.js ile contract etkileşimi
import Web3 from 'web3';

const contractABI = [/* Contract ABI */];
const contractAddress = '0x...';

class Web3Service {
    constructor() {
        this.web3 = null;
        this.contract = null;
        this.account = null;
    }
    
    async init() {
        // MetaMask kontrolü
        if (window.ethereum) {
            this.web3 = new Web3(window.ethereum);
            await window.ethereum.enable();
            
            const accounts = await this.web3.eth.getAccounts();
            this.account = accounts[0];
            
            this.contract = new this.web3.eth.Contract(
                contractABI, 
                contractAddress
            );
        } else {
            throw new Error('MetaMask not found');
        }
    }
    
    async mintNFT(tokenURI) {
        const mintPrice = await this.contract.methods.mintPrice().call();
        
        return await this.contract.methods
            .mint(this.account, tokenURI)
            .send({
                from: this.account,
                value: mintPrice,
                gas: 300000
            });
    }
    
    async getBalance() {
        return await this.contract.methods
            .balanceOf(this.account)
            .call();
    }
    
    async stake(amount) {
        const weiAmount = this.web3.utils.toWei(amount.toString(), 'ether');
        
        return await this.contract.methods
            .stake(weiAmount)
            .send({
                from: this.account,
                gas: 200000
            });
    }
}

// Kullanım örneği
const web3Service = new Web3Service();

document.getElementById('connectWallet').onclick = async () => {
    try {
        await web3Service.init();
        console.log('Wallet connected:', web3Service.account);
    } catch (error) {
        console.error('Connection failed:', error);
    }
};

document.getElementById('mintNFT').onclick = async () => {
    try {
        const tokenURI = 'https://ipfs.io/ipfs/QmYourHash';
        const tx = await web3Service.mintNFT(tokenURI);
        console.log('NFT minted:', tx.transactionHash);
    } catch (error) {
        console.error('Minting failed:', error);
    }
};

Gas Optimizasyonu İpuçları

  • Packed structs kullanın (uint256 yerine uint128)
  • Memory yerine calldata kullanın
  • Loop'larda gas limit'e dikkat edin
  • Events storage'dan daha ucuz

7. Testing (Hardhat ile)

// test/MyToken.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");

describe("MyToken", function () {
    let myToken;
    let owner;
    let addr1;
    let addr2;

    beforeEach(async function () {
        [owner, addr1, addr2] = await ethers.getSigners();
        
        const MyToken = await ethers.getContractFactory("MyToken");
        myToken = await MyToken.deploy(1000000); // 1M tokens
        await myToken.deployed();
    });

    describe("Deployment", function () {
        it("Should set the right owner", async function () {
            expect(await myToken.owner()).to.equal(owner.address);
        });

        it("Should assign the total supply of tokens to the owner", async function () {
            const ownerBalance = await myToken.balanceOf(owner.address);
            expect(await myToken.totalSupply()).to.equal(ownerBalance);
        });
    });

    describe("Transactions", function () {
        it("Should transfer tokens between accounts", async function () {
            await myToken.transfer(addr1.address, 50);
            const addr1Balance = await myToken.balanceOf(addr1.address);
            expect(addr1Balance).to.equal(50);

            await myToken.connect(addr1).transfer(addr2.address, 50);
            const addr2Balance = await myToken.balanceOf(addr2.address);
            expect(addr2Balance).to.equal(50);
        });

        it("Should fail if sender doesn't have enough tokens", async function () {
            const initialOwnerBalance = await myToken.balanceOf(owner.address);

            await expect(
                myToken.connect(addr1).transfer(owner.address, 1)
            ).to.be.revertedWith("Insufficient balance");

            expect(await myToken.balanceOf(owner.address)).to.equal(
                initialOwnerBalance
            );
        });
    });
});

8. Deployment Script

// scripts/deploy.js
const hre = require("hardhat");

async function main() {
    // Contract'ı derle
    await hre.run('compile');

    // Deploy edilecek parametreler
    const initialSupply = 1000000;
    
    // Contract factory'yi al
    const MyToken = await hre.ethers.getContractFactory("MyToken");
    
    // Deploy et
    console.log("Deploying MyToken...");
    const myToken = await MyToken.deploy(initialSupply);
    
    // Deployment'in tamamlanmasını bekle
    await myToken.deployed();
    
    console.log("MyToken deployed to:", myToken.address);
    console.log("Owner:", await myToken.owner());
    console.log("Total Supply:", await myToken.totalSupply());

    // Contract'ı verify et (Etherscan için)
    if (network.name !== "hardhat") {
        console.log("Verifying contract on Etherscan...");
        try {
            await hre.run("verify:verify", {
                address: myToken.address,
                constructorArguments: [initialSupply],
            });
        } catch (error) {
            console.log("Verification failed:", error);
        }
    }
}

main()
    .then(() => process.exit(0))
    .catch((error) => {
        console.error(error);
        process.exit(1);
    });

9. Güvenlik Best Practices