Kendi token’ınızı kripto dünyasında oluşturmak, merkeziyetsiz finans (DeFi) uygulamaları geliştirmek veya basitçe blok zinciri teknolojisi hakkında daha fazla bilgi edinmek için harika bir adımdır. Bu kılavuzda, ERC20 uyumlu bir token oluşturmayı, bunu yönetmek için bir DEX inşa etmeyi ve bu akıllı sözleşmeleri Hardhat kullanarak dağıtmayı öğreneceksiniz.

1. Gereksinimler

Projeye başlamadan önce aşağıdaki araçlara ve bilgilere sahip olduğunuzdan emin olun:

2. Kurulum

Proje dizininizi oluşturun ve gerekli paketleri yükleyin:

mkdir rohan-token
cd rohan-token
npm init -y
npm install --save-dev hardhat @nomiclabs/hardhat-ethers @nomiclabs/hardhat-etherscan ethers
npm install @openzeppelin/contracts

3. Hardhat Projesini Başlatma

Hardhat projesini başlatmak için aşağıdaki komutu çalıştırın. Bu komut, Hardhat yapılandırma dosyalarını oluşturmanıza yardımcı olacaktır:

npx hardhat

Komutu çalıştırdıktan sonra, projeyi kurmak için birkaç seçenekle karşılaşacaksınız. “Temel bir örnek proje oluştur” seçeneğini seçin.

4. Hardhat Projesini Derleme

Bu adımı uygulamadan önce akıllı sözleşmelerinizi oluşturmanız gerekiyor. Ardından aşağıdaki komut ile projenizi derleyin:

npx hardhat compile

5. Akıllı Sözleşmeleri Yazma

Projemizde iki ana akıllı sözleşme olacak:

contracts/RohanToken.sol dosyasını oluşturun ve aşağıdaki kodu ekleyin:

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

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

contract RohanToken is ERC20, Ownable {
    constructor(address initialOwner)
        ERC20("kendi token adınızı yazın", "kendi sembolünüzü yazın")
        Ownable(initialOwner)
    {
        // Toplam arz olarak 10 milyon token basılıyor
        _mint(msg.sender, 10_000_000 * 10 ** decimals());
    }
}

Yukarıdaki kodda, "kendi token adınızı yazın" ve "kendi sembolünüzü yazın" kısımlarını kendi token adınız ve sembolünüzle değiştirin. Bu sözleşme, sahip olduğunuz adrese 10 milyon token mint eder.

contracts/RohanDex.sol dosyasını oluşturun:

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

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

contract RohanDex {
    using SafeERC20 for IERC20;

    IERC20 public token;
    uint256 public reserveEth;
    uint256 public reserveToken;
    uint256 public constant FEE_PERCENTAGE = 3; // %0.3 işlem ücreti (1000 üzerinden)

    event LiquidityAdded(address indexed provider, uint256 ethAmount, uint256 tokenAmount);
    event LiquidityRemoved(address indexed provider, uint256 ethAmount, uint256 tokenAmount);
    event Swapped(address indexed swapper, uint256 ethAmountIn, uint256 tokenAmountOut);

    constructor(IERC20 _token) {
        token = _token;
    }

    function swapEthForTokens(uint256 minTokens) external payable {
        require(msg.value > 0, "ETH göndermeniz gerekiyor");

        uint256 tokensOut = getAmountOut(msg.value, reserveEth, reserveToken);
        require(tokensOut >= minTokens, "Yetersiz çıktı miktarı");

        reserveEth += msg.value;
        reserveToken -= tokensOut;

        token.safeTransfer(msg.sender, tokensOut);
        emit Swapped(msg.sender, msg.value, tokensOut);
    }

    function swapTokensForEth(uint256 tokenAmount, uint256 minEth) external {
        require(tokenAmount > 0, "Token göndermeniz gerekiyor");

        uint256 ethOut = getAmountOut(tokenAmount, reserveToken, reserveEth);
        require(ethOut >= minEth, "Yetersiz çıktı miktarı");

        reserveToken += tokenAmount;
        reserveEth -= ethOut;

        token.safeTransferFrom(msg.sender, address(this), tokenAmount);
        payable(msg.sender).transfer(ethOut);

        emit Swapped(msg.sender, ethOut, tokenAmount);
    }

    function addLiquidity(uint256 tokenAmount) external payable {
        require(msg.value > 0 && tokenAmount > 0, "ETH ve token sağlamanız gerekiyor");

        if (reserveEth == 0 && reserveToken == 0) {
            reserveEth = msg.value;
            reserveToken = tokenAmount;
        } else {
            uint256 requiredTokenAmount = (msg.value * reserveToken) / reserveEth;
            require(tokenAmount >= requiredTokenAmount, "Likidite için yeterli token yok");

            reserveEth += msg.value;
            reserveToken += tokenAmount;
        }

        token.safeTransferFrom(msg.sender, address(this), tokenAmount);
        emit LiquidityAdded(msg.sender, msg.value, tokenAmount);
    }

    function removeLiquidity(uint256 ethAmount) external {
        require(ethAmount > 0 && ethAmount <= reserveEth, "Geçersiz ETH miktarı");

        uint256 tokenAmount = (ethAmount * reserveToken) / reserveEth;

        reserveEth -= ethAmount;
        reserveToken -= tokenAmount;

        payable(msg.sender).transfer(ethAmount);
        token.safeTransfer(msg.sender, tokenAmount);

        emit LiquidityRemoved(msg.sender, ethAmount, tokenAmount);
    }

    function getAmountOut(uint256 inputAmount, uint256 inputReserve, uint256 outputReserve)
        internal pure returns (uint256)
    {
        uint256 inputAmountWithFee = inputAmount * (1000 - FEE_PERCENTAGE);
        uint256 numerator = inputAmountWithFee * outputReserve;
        uint256 denominator = (inputReserve * 1000) + inputAmountWithFee;
        return numerator / denominator;
    }

    function getReserves() external view returns (uint256 ethReserve, uint256 tokenReserve) {
        return (reserveEth, reserveToken);
    }
}

6. Hardhat Ayarları

Hardhat, Ethereum akıllı sözleşmelerini derlemek, test etmek ve dağıtmak için güçlü bir geliştirme ortamıdır. ./hardhat.config.js dosyasını oluşturun ve aşağıdaki kodu ekleyin:

require("@nomiclabs/hardhat-ethers");
require("@nomiclabs/hardhat-etherscan");

module.exports = {
    solidity: "0.8.24",
    networks: {
        sepolia: {
            url: "", // Alchemy veya Infura API anahtarı
            accounts: [`0x${"---"}`], // Cüzdan özel anahtarı
        },
    },
    etherscan: {
        apiKey: "----", // Etherscan API anahtarı
    },
};

Bu yapılandırma dosyası:

7. Sözleşmelerin Dağıtımı

deploy.js betiği, akıllı sözleşmeleri Sepolia test ağına dağıtmak için kullanılacak. scripts/deploy.js dosyasını oluşturun:

async function main() {
    const [deployer] = await ethers.getSigners();
    console.log("Deploying contracts with the account:", deployer.address);

    const RohanToken = await ethers.getContractFactory("RohanToken");
    const RohanDex = await ethers.getContractFactory("RohanDex");

    // Token dağıtımı
    const token = await RohanToken.deploy(deployer.address, { gasPrice, gasLimit });
    await token.deployed();
    console.log("Rohan Token deployed to:", token.address);

    // Dex dağıtımı
    const rohanTokenAddress = token.address;
    const rohanDex = await RohanDex.deploy(rohanTokenAddress, { gasPrice, gasLimit });
    await rohanDex.deployed();
    console.log("Rohan Dex deployed to:", rohanDex.address);

    // Token miktarını burada belirleyin
    const tokenAmount = ethers.utils.parseEther("1000000"); // 1 milyon token

    // Token onayı
    const approvalTx = await token.approve(rohanDex.address, tokenAmount);
    await approvalTx.wait();
    console.log("Token onayı verildi");

    // Likidite ekleme
    try {
        const tx = await rohanDex.addLiquidity(tokenAmount, {
            value: ethers.utils.parseEther("0.001"),
            gasPrice,
            gasLimit
        });
        await tx.wait();
        console.log("Likidite eklendi");
    } catch (error) {
        console.error("Hata:", error);
    }

    const balance = await token.balanceOf(deployer.address);
    console.log("Token bakiyesi:", ethers.utils.formatEther(balance));

    const ethBalance = await deployer.getBalance();
    console.log("ETH bakiyesi:", ethers.utils.formatEther(ethBalance));
}

const gasPrice = ethers.utils.parseUnits('3', 'gwei');
const gasLimit = 2500000;

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

Ardından dağıtımı gerçekleştirmek için:

npx hardhat run scripts/deploy.js --network sepolia

Dağıtım tamamlandığında, konsol çıktısında dağıtılan sözleşmelerin adreslerini göreceksiniz.

8. Sözleşmeleri Doğrulama

Dağıtılan sözleşmelerin Etherscan üzerinde doğrulanması, kaynak kodunu şeffaf hale getirir ve güvenilirliği sağlar. scripts/verify.js dosyasını oluşturun:

const hre = require("hardhat");

async function main() {
    const contractAddress = "your token address";
    const initialOwnerAddress = "the address you used to deploy the token";

    const contractArguments = [initialOwnerAddress];

    console.log("RohanToken sözleşmesi doğrulanıyor...");

    try {
        await hre.run("verify:verify", {
            address: contractAddress,
            constructorArguments: contractArguments,
        });
        console.log("RohanToken sözleşmesi başarıyla doğrulandı!");
    } catch (error) {
        console.error("Doğrulama sırasında hata oluştu:", error);
    }
}

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

Doğrulama işlemini başlatmak için:

npx hardhat run scripts/verify.js --network sepolia

Doğrulama başarılı olursa, Etherscan üzerinde sözleşmelerinizi görüntüleyebilir ve kullanıcılarla güvenilir bir şekilde paylaşabilirsiniz.

9. DEX ile Etkileşim

scripts/liquidity.js dosyasını oluşturun:

const { Web3 } = require('web3');
const web3 = new Web3('https://sepolia.infura.io/v3/-your-infura-api');

const dexABI = []; // DEX sözleşmesinin ABI'si. Artifacts klasöründe.
const dexAddress = 'token dex adresi';
const dexContract = new web3.eth.Contract(dexABI, dexAddress);

const tokenABI = []; // Token sözleşmesinin ABI'si. Artifacts klasöründe.
const tokenAddress = 'token sözleşmesi adresi';
const tokenContract = new web3.eth.Contract(tokenABI, tokenAddress);

async function checkLiquidity() {
    const reserveEth = await dexContract.methods.reserveEth().call();
    const reserveToken = await dexContract.methods.reserveToken().call();
    console.log('Mevcut likidite:', {
        ETH: web3.utils.fromWei(reserveEth, 'ether'),
        Token: web3.utils.fromWei(reserveToken, 'ether')
    });
    return { reserveEth, reserveToken };
}

async function addLiquidity(privateKey, ethAmount, tokenAmount) {
    const account = web3.eth.accounts.privateKeyToAccount(privateKey);
    web3.eth.accounts.wallet.add(account);
    web3.eth.defaultAccount = account.address;

    try {
        console.log('Mevcut likidite kontrol ediliyor...');
        const { reserveEth, reserveToken } = await checkLiquidity();

        if (reserveEth !== '0' && reserveToken !== '0') {
            const requiredTokenAmount = BigInt(ethAmount) * BigInt(reserveToken) / BigInt(reserveEth);
            tokenAmount = requiredTokenAmount.toString();
            console.log('Gerekli token miktarı:', web3.utils.fromWei(tokenAmount, 'ether'));
        }

        console.log('Token onayı veriliyor...');
        const approvalTx = await tokenContract.methods
            .approve(dexAddress, tokenAmount)
            .send({ from: account.address });
        console.log("Token onayı verildi. İşlem hash'i:", approvalTx.transactionHash);

        console.log('Likidite ekleme işlemi başlatılıyor...');
        const gasPrice = await web3.eth.getGasPrice();
        const gasEstimate = await dexContract.methods.addLiquidity(tokenAmount).estimateGas({
            from: account.address,
            value: ethAmount
        });

        const tx = {
            from: account.address,
            to: dexAddress,
            gas: Math.floor(Number(gasEstimate) * 1.5),
            gasPrice: gasPrice,
            value: ethAmount,
            data: dexContract.methods.addLiquidity(tokenAmount).encodeABI()
        };

        const signedTx = await web3.eth.accounts.signTransaction(tx, privateKey);
        const receipt = await web3.eth.sendSignedTransaction(signedTx.rawTransaction);

        console.log('İşlem başarılı:', receipt.transactionHash);
        console.log('Kullanılan gas:', receipt.gasUsed);
        await checkLiquidity();
    } catch (error) {
        console.error('Hata detayı:', error);
    }
}

const privateKey = 'metamask cüzdanının özel anahtarı';
const ethToAdd = web3.utils.toWei('0.15', 'ether');
let tokenToAdd = web3.utils.toWei('10', 'ether');

addLiquidity(privateKey, ethToAdd, tokenToAdd)
    .then(() => console.log('İşlem tamamlandı'))
    .catch((error) => console.error('Genel hata:', error));

10. Likidite Ekleme ve Kontrol Etme

Bu script, DEX’inizdeki mevcut likiditeyi kolayca kontrol etmenizi ve belirli miktarda ETH ve token eklemenizi sağlar. Likidite eklerken, özel anahtarınızı kullandığınızdan emin olun. Dostça bir hatırlatma: gerçek projelerde özel anahtarınızı asla paylaşmayın ve her zaman güvenli tutun.

Sonuç

Bu rehberde Solidity ve OpenZeppelin kullanarak kendi ERC20 token’ınızı nasıl oluşturacağınızı öğrendiniz. Ayrıca, bu token’ı yönetmek için merkeziyetsiz bir borsa (DEX) geliştirdiniz ve her şeyi Hardhat ile deploy ettiniz. Yol boyunca, sözleşmelerinizi Etherscan üzerinde nasıl doğrulayacağınızı ve DEX ile nasıl etkileşime geçeceğinizi de ele aldık.

Bu bilgilerle, kendi kripto projelerinize başlamaya hazırsınız 💪

Son bir not: Akıllı sözleşmeler kalıcıdır. Yapılacak hatalar önemli kayıplara yol açabilir, bu yüzden kapsamlı testler yapmayı ve sözleşmelerinizi deploy etmeden önce güvenlik denetimleri yaptırmayı düşünmelisiniz.