Daha önce İlk NFT’ni Oluşturmak: Bölüm 1 – Blockchain’i anlamak kısmında NFT oluşturulması için bir içerik yazacağımdan bahsetmiştim. Konudan çok soğuduğum için bir daha geri dönmedim ama merak edenler için blog yazısını tamamlayacağım.
Bu yazı Solidity kodunu içeren teknik bir yazı olacak. O sebeple bir noktada kopabilirsiniz çünkü bir noktada işler karışacak. Hala devam ediyor musunuz? Haydi başlayalım o zaman.
Öncelikle bazı araç gereçlere ihtiyacımız olacak; kısaca onlardan bahsedeyim.
- Alchemy (alchemy.com) : Bu bizim Ethereum ağı ile haberleşmemizi, kontrat göndermemizi sağlayan bir API. Neden API kullanıyoruz da Ethereum’da direkt işlem yapmıyoruz? Çünkü Ethereum arşivler hariç 1TB, arşivler dahil 4-5TB büyüklüğünde. Tüm bu dosyaları indirip işlem yapmak pek kolay değil ve çok sorun olabiliyor. O sebeple bir API kullanmak hızlı ve kolay.
- MetaMask (metamask.io) : Tilkili cüzdan olarak da bilinir. Ethereum (veya ERC20 uyumlu) kayıtzincirleri üzerinde işlem yapabilmenizi sağlayan bir cüzdan yazılımı.
- Visual Studio Core : IDE veya editor. Ben çok konforla kullanıyorum tavsiye ederim.
- HardHat (hardhat.org) : Solidity kodunu compile ve debug etmenize izin veren yazılım.
- Pinata veya Cloudflare IPFS : Dağıtık dosya sistemi. NFT’lerdeki fotoğrafları upload edeceğiniz yer.
- NodeJS : HardHat çalışmak için NodeJS’a ihtiyaç duyuyor.
- Örnek Repo : https://github.com/orcunbaslak/cenabindan adresinde buradaki tüm kodları bulabilirsiniz ve bu çalışmada bu repoyu clone edeceğiz.
Şimdi tüm araç gereçler hazırsa işleme başlayabiliriz.
Ethereum’un kendi ağında işlem yapmak için gerçek paraya ihtiyacınız var o sebeple biz tüm test işlemlerinin gerçekleştirildiği testnet olarak bilinen Ropsten’i kullanacağız. Öncelikle Alchemy’de bir hesap açıp Ropsten ağı üzerinde bir APP oluşturun ve şu bilgileri alın.
Şimdi bir de cüzdan oluşturalım. Bir Chrome eklentisi olan tilkili cüzdanı browserimize kuralım ve ağımızı “Ropsten” olarak seçelim.
Burada bir Ethereum hesabı yarattık. Ethereum’da hesaplar bir herkes ile paylaştığınız “Public Key” ve sadece size özel olan “Private Key” lerden oluşur. Menülerinde bu değerleri görebilirsiniz. Şimdi hem private hem de public key’i bir yere kaydedin.
Uygulamanız hazırsa şimdi github reposunu klonlayabiliriz.
git clone https://github.com/orcunbaslak/cenabindan.git
Klonlama işlemi tamamsa repo içerisinde .env dosyasını oluşturalım.
Yukarıda gördüğünüz değişkenleri Alchemy’den ve tilkili cüzdandan (MetaMask) kendi çalışmalarınıza göre doldurun.
Daha sonra klasörün içine girerek NodeJS’i init edin ve HardHat’i kurun.
npm init
npm install --save-dev hardhat
npm install --save dotenv
npm install @openzeppelin/contracts
npm install @alch/alchemy-web3
npm install --save-dev @nomiclabs/hardhat-ethers ethers@^5.0.0
Şimdiye kadar neler yaptık?
- Ethereum Ropsten blockchain’i ile haberleşmek için Alchemy API’si aldık.
- MetaMask üzerinden kendimize bir ERC20 adresi oluşturduk.
- Hardhat’in çalışabilmesi için NodeJS’i kurduk.
- Solidity kodunun compile edilebilmesi için Hardhat’i kurduk
- Kontratta kullanacağımız EtherJS, OpenZeppelin gibi kütüphaneleri kurduk
- Template repomuzu klonladık.
Artık kendi tokenimizi oluşturmaya ve kendi NFT’miz için akıllı kontrat yazmaya hazırız (Evet bu kadar aşama sadece kontrat yazmaya başlamak için gerekliydi. Henüz yazmadık).
Tabi kayıtzincirinde her işlemin bir bedeli var. Ee bizim hiç ETH’ımız yok? O zaman “faucet” diye isimlendirilen bize kullanmalık ETH gönderen sitelere başvuracağız. Ör: https://faucet.egorfine.com/ adresindeki kutucuğa PUBLIC adresinizi yazarak ETH talep edebilirsiniz. Faucet’ler her zaman sağlam çalışmıyor o sebeple bu adım biraz vaktinizi alabilir. Bende çalışan bir faucet bulmak 2-3 gün sürmüştü.
Diyelim onu da hallettiniz hesabınızda çok temiz 5 ETH yüklendi. Şimdi işlemlere başlayabiliriz. Öncelikle kontrat dosyamızı inceleyelim.
//Contract based on [https://docs.openzeppelin.com/contracts/3.x/erc721](https://docs.openzeppelin.com/contracts/3.x/erc721)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
contract Cenabindan is ERC721URIStorage, Ownable {
using Counters for Counters.Counter;
Counters.Counter private _tokenIds;
constructor() ERC721("Cenabindan", "CNB") {}
function mintNFT(address recipient, string memory tokenURI)
public onlyOwner
returns (uint256)
{
_tokenIds.increment();
uint256 newItemId = _tokenIds.current();
_mint(recipient, newItemId);
_setTokenURI(newItemId, tokenURI);
return newItemId;
}
}
Burada öncelikle “Cenabindan” isimli bir Token oluşturuyoruz ve bu token’a “CNB” ticker’ını veriyoruz. Bu kod OpenZeppelin’in orjinal kod örneği.
Buradaki import ettiğimiz kısımlar oluşturduğumuz Token’e belirli özellikler katıyor. İlk import satırımız bir Token oluşturulması için gereken minimal özellikleri içeriyor. İkincisi ise bu Token’in üretilmesi aşamasında kullanılacak olan sayaçları ve eşsiz belirteçleri belirliyor. Üçüncü satır ise bu Token’in sahibini belirliyor. Mevcut kodumuzda bu Token’i sadece yaratıcı kazıp sahip olabilir. Dördüncü ve sonucu satır ise bu Token’in bir NFT barındırabilmesine olanak tanıyor.
Şimdi ilk kontratımızı oluşturduk. Bi compile edip bakalım.
npx hardhat compile
Eğer herşey yolunda gittiyse kodumuzu Ropsten ağına gönderebiliriz. scripts/deploy.js altındaki dosyamıza bakalım.
async function main() {
const Cenabindan = await ethers.getContractFactory("Cenabindan")
// Start deployment, returning a promise that resolves to a contract object
const myNFT = await Cenabindan.deploy()
await myNFT.deployed()
console.log("Contract deployed to address:", myNFT.address)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
Şimdi kontratımızı ropsten ağına gönderelim. Bash’e aşağıdaki komutu yazalım.
npx hardhat --network ropsten run scripts/deploy.js
Kontrat başarıyla oluşturulduğunda kontratın oluşturulduğu adresi verecek. O adresi https://ropsten.etherscan.io/ adresinden kontrol edebiliriz.
Contract deployed to address: 0x7A35Fdb13B9c88D892876Efb4f9Fe3C8246D7181
Şimdi gerçekten kontrol gönderilmiş mi bakalım?
https://ropsten.etherscan.io/address/0x7A35Fdb13B9c88D892876Efb4f9Fe3C8246D7181
Hayırlı olsun. İlk kontratımızı oluşturduk 🙂
Burada bitti mi? Hayır tabiki. Şimdi bu Token üzerinden bir NFT oluşturacağız. Nası yani hala NFT olmadı mı? Maalesef olmadı.
Şimdi bir fotoğrafı NFT haline getireceğiz diyelim, bunun için 2 dosyaya ihtiyacımız var;
- Bu NFT’nin açıklamasını içeren bir JSON Metadata dosyası
- Bu fotoğrafın bir URL’si
- İlk seçenekteki metadata’nın JSON dosyasının URL’si.
Bu tarz dosyaları dağıtık bir dosya sistemi olan IPFS üzerinde tutuyorlar genelde. Siz kendi sunucunuzu da kullanabilirsiniz ama IPFS daha kabul gören bir seçenek. Pinata.cloud adresinden IPFS’e dosya yükleyebiliyoruz. Dosyalar zaten klonladığınız repo’da mevcut ama ben aşağıda bir örnek daha paylaşayım.
{
"attributes": [
{
"trait_type": "Taş",
"value": "Beyaz Taş"
}
],
"description": "The first NTF stone for the Devil.",
"image": "https://gateway.pinata.cloud/ipfs/QmeC4TkDgs9g1qMi7xx5bm9n2f9HSYD5Qamdjc82Hr5u6A",
"name": "Taş_1"
}
Bu benim IPFS’de oluşturduğum beyaz taş dosyasının URL’sini gösterir JSON dosyası.
Şimdi bu dosyayı ve fotoğraf dosyasını Pinata’ya yükleyelim.
Bir NFT’yi oluşturan 2 ana dosyayı oluşturup böylelikle IPFS’e koyduk. Şimdi bir Token kazıp Token’in içerisine bu JSON dosyasının URL’sini gömmemiz gerekiyor.
require('dotenv').config();
const API_URL = process.env.API_URL;
const PUBLIC_KEY = process.env.PUBLIC_KEY;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const { createAlchemyWeb3 } = require("@alch/alchemy-web3");
const web3 = createAlchemyWeb3(API_URL);
const contract = require("../artifacts/contracts/Cenabindan.sol/Cenabindan.json")
const contractAddress = "0x7A35Fdb13B9c88D892876Efb4f9Fe3C8246D7181";
const nftContract = new web3.eth.Contract(contract.abi, contractAddress);
async function mintNFT(tokenURI) {
const nonce = await web3.eth.getTransactionCount(PUBLIC_KEY, 'latest'); //get latest nonce
//the transaction
const tx = {
'from': PUBLIC_KEY,
'to': contractAddress,
'nonce': nonce,
'gas': 500000,
'data': nftContract.methods.mintNFT(PUBLIC_KEY, tokenURI).encodeABI()
};
const signPromise = web3.eth.accounts.signTransaction(tx, PRIVATE_KEY)
signPromise
.then((signedTx) => {
web3.eth.sendSignedTransaction(
signedTx.rawTransaction,
function (err, hash) {
if (!err) {
console.log(
"The hash of your transaction is: ",
hash,
"\nCheck Alchemy's Mempool to view the status of your transaction!"
)
} else {
console.log(
"Something went wrong when submitting your transaction:",
err
)
}
}
)
})
.catch((err) => {
console.log(" Promise failed:", err)
})
}
mintNFT(
"https://gateway.pinata.cloud/ipfs/QmR1j4MCKZdNnwmyN2vWGDFKBY8VMUzK7evtMAzgoVy99e"
)
Bu kod ile birlikte 1 adet NFT mint edip en alttaki Pinata JSON dosyası ile ilişkilendiriyoruz. Bu dosya da clone ettiğiniz repo’da mevcut. Şimdi kodu çalıştıralım.
node scripts/mint-nft.js
Bize şöyle bir cevap dönmesi gerekli.
The hash of your transaction is: 0xbe049ce570904ef0eb9e5ccc9acc576e9a1fc35f735d17cfdf0220d1d8020678
Check Alchemy's Mempool to view the status of your transaction!
Voila! İlk NFT’mizi oluşturduk. Peki bu taş nerede? Nasıl göstereceğiz millete nasıl satacağız? Hani çıkmadı ya bu tilkili cüzdanda? Önce şu kontratın detaylarını bir görelim.
Şimdi bunu tilkili cüzdana ekleyelim. Ethereum mainnet’de çoğu zaman NFT’ler otomatik olarak görünüyor ama testnet’lerde manuel eklemek gerekebiliyor.
sağdaki fotoğrafta gireceğiniz bilgiler bir yukarıdaki fotoğrafta kırmızı karenin içerisine alınmış halde.
Hayırlı olsun, ilk oluşturduğunuz NFT’yi MetaMask’de görüntülüyorsunuz. MetaMask’ın iOS sürümünde NFT import etme var ama browser’da çalışanında yok. O sebeple “Import NFT” seçeneği sadece mobil uygulamalarda var. Browser eklentisinde neden yok diye sorgulamayın.
Peki ben bu NFT vb şeyler hakkında ne mi düşünüyorum? Buyrun.
Bir yanıt yazın