Ana içeriğe geç

NFT Minter Öğreticisi

solidityNFTalchemyakıllı sözleşmelerön uçPinata
Orta düzey
smudgil
6 Ekim 2021
23 dakikalık okuma minute read

Web2 arka planından gelen geliştiriciler için en büyük zorluklardan biri, akıllı bağlantınızı bir ön uç projesine nasıl bağlayacağınızı ve onunla nasıl etkileşimde bulunacağınızı anlamaktır.

Dijital varlığınıza bir bağlantı, bir başlık ve bir açıklama girebileceğiniz basit bir kullanıcı arayüzü olan bir NFT minter oluşturarak şunları nasıl yapacağınızı öğreneceksiniz:

  • Ön uç projeniz aracılığıyla MetaMask'a bağlanma
  • Ön ucunuzdan akıllı sözleşme yöntemlerini çağırma
  • MetaMask kullanarak işlemleri imzalama

Bu öğreticide, ön uç çerçevemiz olarak React(opens in a new tab) kullanacağız. Bu eğitim öncelikle Web3 geliştirmeye odaklandığından, React temellerini açıklamak için fazla zaman harcamayacağız. Bunun yerine, projemize işlevsellik getirmeye odaklanacağız.

Bir ön koşul olarak, başlangıç ​​düzeyinde bir React anlayışına sahip olmalısınız; bileşenlerin, donanımların, useState/useEffect ve temel fonksiyon çağırmanın nasıl çalıştığını bilmeniz gerekir. Bu terimlerden herhangi birini daha önce hiç duymadıysanız, bu React'e Giriş eğitimine(opens in a new tab) göz atmak isteyebilirsiniz. Daha görsel öğrenenler için Net Ninja'nın bu mükemmel Tam Modern React Eğitimi(opens in a new tab) video dizisini şiddetle tavsiye ediyoruz.

Ve henüz yapmadıysanız, bu öğreticiyi tamamlamak ve blok zincirinde herhangi bir şey oluşturmak için kesinlikle bir Alchemy hesabına ihtiyacınız olacak. Buradan(opens in a new tab) ücretsiz bir hesap için kaydolun.

Lafı fazla uzatmadan başlayalım!

NFT Yapma 101

Herhangi bir koda bakmaya başlamadan önce, bir NFT yapmanın nasıl çalıştığını anlamak önemlidir. İki adım içerir:

Ethereum blok zincirinde bir NFT akıllı sözleşmesi yayınlayın

İki NFT akıllı iletişim standardı arasındaki en büyük fark, ERC-1155'in çok token'lı bir standart olması ve toplu işlevsellik içermesi; ERC-721'in ise tek token'lı bir standart olması ve bu nedenle bir seferde yalnızca bir token'ın aktarılmasını desteklemesidir.

Basma fonksiyonunu çağırın

Genellikle, bu mint (basma) fonksiyonu, parametre olarak iki değişken girmenizi gerektirir: İlk olarak, yeni basılmış NFT'nizi alacak adresi belirten recipient ve ikinci olarak NFT'nin meta verilerini açıklayan bir JSON belgesine çözümlenen bir dize olan NFT'nin tokenURI'ı.

Bir NFT'nin meta verileri gerçekten onu hayata geçiren şeydir ve bir isim, açıklama, görüntü (veya farklı dijital varlık) ve diğer nitelikler gibi özelliklere sahip olmasına izin verir. İşte bir NFT'nin meta verilerini içeren bir tokenURI örneği(opens in a new tab).

Bu öğreticide, React UI'ımızı kullanarak mevcut bir NFT'nin akıllı sözleşme basım fonksiyonunu çağırarak 2. bölüme odaklanacağız.

Bu öğreticide çağıracağımız ERC-721 NFT akıllı sözleşmesinin bağlantısı(opens in a new tab). Bunu nasıl yaptığımızı öğrenmek istiyorsanız, diğer öğreticimiz "Bir NFT Nasıl Oluşturulur"(opens in a new tab)a göz atmanızı şiddetle tavsiye ederiz.

Harika, şimdi bir NFT yapmanın nasıl çalıştığını anladığımıza göre, başlangıç ​​dosyalarımızı klonlayalım!

Başlangıç ​​dosyalarını klonlayın

İlk olarak, bu proje için başlangıç ​​dosyalarını almak için nft-minter-tutorial GitHub deposuna(opens in a new tab) gidin. Bu depoyu yerel ortamınıza klonlayın.=

Bu klonlanmış nft-minter-tutorial deposunu açtığınızda, iki klasör içerdiğini fark edeceksiniz: minter-starter-files ve nft-minter.

  • minter-starter-files, bu proje için başlangıç ​​dosyalarını (esas olarak React UI'ını) içerir. Bu öğreticide, bu dizinde çalışarak bu kullanıcı arayüzünü Ethereum cüzdanınıza ve bir NFT akıllı sözleşmesine bağlayarak nasıl hayata geçireceğinizi öğreneceğiz.
  • nft-minter, tamamlanmış öğreticinin tamamını içerir ve takılırsanız size referans olması için oradadır.

Ardından, kod düzenleyicinizde minter-starter-files kopyanızı açın ve ardından src klasörünüze gidin.

Yazacağımız tüm kodlar src klasörünün altında yer alacaktır. Projemize Web3 işlevselliği kazandırmak için Minter.js bileşenini düzenleyeceğiz ve ek javascript dosyaları yazacağız.

2. Adım: Başlangıç ​​dosyalarımıza göz atın

Kodlamaya başlamadan önce, başlangıç ​​dosyalarında bizim için nelerin sağlandığını kontrol etmek önemlidir.

React projenizi çalıştırın

Tarayıcımızda React projesini çalıştırarak başlayalım. React'in güzelliği, projemizi tarayıcımızda çalıştırdıktan sonra, kaydettiğimiz tüm değişikliklerin tarayıcımızda canlı olarak güncellenmesidir.

Projeyi çalıştırmak için minter-starter-files klasörünün kök dizinine gidin ve projenin bağımlılıklarını yüklemek için terminalinizde npm install komutunu çalıştırın:

cd minter-starter-files
npm install

Bunların kurulumu tamamlandıktan sonra terminalinizde npm start komutunu çalıştırın:

npm start

Bunu yapmak, tarayıcınızda projemizin ön ucunu göreceğiniz http://localhost:3000/ adresini açmalıdır. 3 alandan oluşmalıdır: NFT'nizin varlığına bir bağlantı yerleştireceğiniz, NFT'nizin adını gireceğiniz ve bir açıklama sağlayabileceğiniz bir yer.

"Connect Wallet" (Cüzdanı Bağla) veya "Mint NFT" (NFT Bas) düğmelerinE tıklamayı denerseniz, çalışmadıklarını fark edeceksiniz. Çünkü hâlâ işlevlerini kodlamamız gerekiyor! :)

Minter.js bileşeni

NOT: nft-minter klasöründe değil, minter-starter-files klasöründe olduğunuzdan emin olun!

Editörümüzdeki src klasörüne geri dönelim ve Minter.js dosyasını açalım. Üzerinde çalışacağımız birincil React bileşeni olduğu için bu dosyadaki her şeyi anlamamız çok önemlidir.

Bu dosyamızın en üstünde, belirli olaylardan sonra güncelleyeceğimiz durum değişkenlerimiz var.

1//State variables
2const [walletAddress, setWallet] = useState("")
3const [status, setStatus] = useState("")
4const [name, setName] = useState("")
5const [description, setDescription] = useState("")
6const [url, setURL] = useState("")

React durum değişkenlerini veya durum kancalarını hiç duymadınız mı? Bu(opens in a new tab) belgelere göz atın.

Değişkenlerin her birinin temsil ettiği şey:

  • walletAddress - kullanıcının cüzdan adresini saklayan bir dize
  • status - kullanıcı arayüzünün altında görüntülenecek bir mesaj içeren bir dize
  • name - NFT'nin adını saklayan bir dize
  • description - NFT'nin açıklamasını saklayan bir dize
  • url - NFT'nin dijital varlığına bağlantı olan bir dize

Durum değişkenlerinden sonra, uygulanmamış üç fonksiyon göreceksiniz: useEffect, connectWalletPressed ve onMintPressed. Tüm bu fonksiyonların async olduğunu fark edeceksiniz, çünkü bu fonksiyonlarda eşzamansız API çağrıları yapacağız! Adları, fonksiyonlarıyla aynıdır:

1useEffect(async () => {
2 //TODO: implement
3}, [])
4
5const connectWalletPressed = async () => {
6 //TODO: implement
7}
8
9const onMintPressed = async () => {
10 //TODO: implement
11}
Tümünü göster
  • useEffect(opens in a new tab) - bu, bileşeniniz oluşturulduktan sonra çağrılan bir React kancasıdır. İçine geçirilen boş bir [] dizisine sahip olduğundan (3. satıra bakın), yalnızca bileşenin ilk oluşturmasında çağrılır. Burada, bir cüzdanın zaten bağlı olup olmadığını yansıtacak şekilde kullanıcı arayüzünü güncellemek için cüzdan dinleyicimizi ve başka bir cüzdan fonksiyonunu çağıracağız.
  • connectWalletPressed - bu fonksiyon, kullanıcının MetaMask cüzdanını merkeziyetsiz uygulamamıza bağlamak için çağrılır.
  • onMintPressed - bu fonksiyon, kullanıcının NFT'sini basmak için çağrılır.

Bu dosyanın sonuna doğru, bileşenimizin kullanıcı arayüzü bulunuyor. Bu kodu dikkatli bir şekilde tararsanız, url, ad ve description durum değişkenlerimizi, bunlara denk gelen metin alanları değiştiğinde güncellediğimizi fark edeceksiniz.

Ayrıca, sırasıyla mintButton ve walletButton kimliklerine sahip düğmelere tıklandığında connectWalletPressed ve onMintPressed'in çağrıldığını göreceksiniz.

1//the UI of our component
2return (
3 <div className="Minter">
4 <button id="walletButton" onClick={connectWalletPressed}>
5 {walletAddress.length > 0 ? (
6 "Connected: " +
7 String(walletAddress).substring(0, 6) +
8 "..." +
9 String(walletAddress).substring(38)
10 ) : (
11 <span>Connect Wallet</span>
12 )}
13 </button>
14
15 <br></br>
16 <h1 id="title">🧙‍♂️ Alchemy NFT Minter</h1>
17 <p>
18 Simply add your asset's link, name, and description, then press "Mint."
19 </p>
20 <form>
21 <h2>🖼 Link to asset: </h2>
22 <input
23 type="text"
24 placeholder="e.g. https://gateway.pinata.cloud/ipfs/<hash>"
25 onChange={(event) => setURL(event.target.value)}
26 />
27 <h2>🤔 Name: </h2>
28 <input
29 type="text"
30 placeholder="e.g. My first NFT!"
31 onChange={(event) => setName(event.target.value)}
32 />
33 <h2>✍️ Description: </h2>
34 <input
35 type="text"
36 placeholder="e.g. Even cooler than cryptokitties ;)"
37 onChange={(event) => setDescription(event.target.value)}
38 />
39 </form>
40 <button id="mintButton" onClick={onMintPressed}>
41 Mint NFT
42 </button>
43 <p id="status">{status}</p>
44 </div>
45)
Tümünü göster

Son olarak bu Minter bileşeninin nereye eklendiğine değinelim.

React'teki diğer tüm bileşenler için bir kapsayıcı görevi gören ana bileşen olan App.js dosyasına giderseniz, Minter bileşenimizin 7. satıra enjekte edildiğini göreceksiniz.

Bu öğreticide, yalnızca Minter.js file'ı düzenleyeceğiz ve src klasörümüze dosyalar ekleyeceğiz.

Artık ne üzerinde çalıştığımızı anladığımıza göre, Ethereum cüzdanımızı oluşturalım!

: Ethereum cüzdanınızı kurun

Kullanıcıların akıllı sözleşmenizle etkileşime girebilmeleri için Ethereum cüzdanlarını merkeziyetsiz uygulamanıza bağlamaları gerekir.

MetaMask'i indirin

Bu öğretici için, Ethereum hesap adresinizi yönetmek için kullanılan tarayıcıda sanal bir cüzdan olan MetaMask'ı kullanacağız. Ethereum'daki işlemlerin nasıl çalıştığı hakkında daha fazla bilgi edinmek istiyorsanız, bu sayfaya bakın.

Buradan(opens in a new tab) ücretsiz olarak bir MetaMask hesabı indirebilir ve oluşturabilirsiniz. Bir hesap oluşturuyorsanız veya zaten bir hesabınız varsa, sağ üstteki "Ropsten Test Ağı"na geçtiğinizden emin olun (böylece gerçek parayla uğraşmayız).

Bir Musluktan ether ekleyin

NFT'lerimizi basmak (veya Ethereum blok zincirindeki herhangi bir işlemi imzalamak) için biraz sahte Eth'e ihtiyacımız olacak. Eth almak için Ropsten musluğuna(opens in a new tab) gidebilir ve Ropsten hesap adresinizi girip "Send Ropsten Eth"e (Ropsten Eth Gönder) tıklayabilirsiniz Kısa bir süre sonra MetaMask hesabınızda Eth'i görmelisiniz!

Bakiyenizi kontrol edin

Bakiyemizin yerinde olduğundan emin olmak için Alchemy'nin düzenleyici aracını(opens in a new tab) kullanarak bir eth_getBalance(opens in a new tab) isteği oluşturalım. Bu, cüzdanımızdaki Eth miktarını döndürür. MetaMask hesap adresinizi girdikten ve "Send Request"e tıkladıktan sonra aşağıdaki gibi bir yanıt görmelisiniz:

1{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}

NOT: Bu sonuç eth değil wei hâlindedir. Wei, ether'ın en küçük birimi olarak kullanılır. Wei'den eth'e dönüşüm: 1 eth = 10¹⁸ wei. Yani 0xde0b6b3a7640000'ı ondalık sayıya dönüştürürsek 1*10¹⁸ elde ederiz, bu da 1 eth'e eşittir.

Vay be! Tüm sahte paramız yerli yerinde!

MetaMask'i kullanıcı arayüzünüze bağlayın

Artık MetaMask cüzdanımız kurulduğuna göre, merkeziyetsiz uygulamamızı ona bağlayalım!

MVC(opens in a new tab) paradigmasını kurala bağlamak istediğimiz için merkeziyetsiz uygulamamızın mantığını, verilerini ve kurallarını yönetmek amacıyla fonksiyonlarımızı içeren ayrı bir dosya oluşturacağız ve ardından bu fonksiyonları ön ucumuza (Minter.js bileşenimiz) aktaracağız.

connectWalletfonksiyonu

Bunu yapmak için src dizininizde utils adında yeni bir klasör oluşturalım ve içine tüm cüzdan ve akıllı sözleşme etkileşim fonksiyonlarımızı içerecek interact.js adlı bir dosya ekleyelim.

interact.js dosyamızda, bir connectWallet fonksiyonu yazacağız ve bunu daha sonra içe aktarıp Minter.js bileşenimizde çağıracağız.

interact.js dosyanıza aşağıdakini ekleyin

1export const connectWallet = async () => {
2 if (window.ethereum) {
3 try {
4 const addressArray = await window.ethereum.request({
5 method: "eth_requestAccounts",
6 })
7 const obj = {
8 status: "👆🏽 Write a message in the text-field above.",
9 address: addressArray[0],
10 }
11 return obj
12 } catch (err) {
13 return {
14 address: "",
15 status: "😥 " + err.message,
16 }
17 }
18 } else {
19 return {
20 address: "",
21 status: (
22 <span>
23 <p>
24 {" "}
25 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
26 You must install MetaMask, a virtual Ethereum wallet, in your
27 browser.
28 </a>
29 </p>
30 </span>
31 ),
32 }
33 }
34}
Tümünü göster

Şimdi bu kodun ne yaptığını inceleyelim:

İlk olarak, fonksiyonumuz tarayıcınızda window.ethereum'un etkin olup olmadığını kontrol eder.

window.ethereum, MetaMask ve diğer cüzdan sağlayıcıları tarafından enjekte edilen ve web sitelerinin kullanıcıların Ethereum hesaplarını talep etmesine izin veren küresel bir API'dir. Onaylanırsa, kullanıcının bağlı olduğu blok zincirlerinden verileri okuyabilir ve kullanıcının mesajları ve işlemleri imzalamasını önerebilir. Daha fazla bilgi için MetaMask belgelerine(opens in a new tab) göz atın!

window.ethereum yoksa, MetaMask kurulu değil demektir. Bu, bir JSON nesnesinin döndürülmesiyle sonuçlanır; burada döndürülen address boş bir dizedir ve status JSX nesnesi, kullanıcının MetaMask'i yüklemesi gerektiğini bildirir.

Yazdığımız fonksiyonların çoğu, durum değişkenlerimizi ve kullanıcı arayüzünü güncellemek için kullanabileceğimiz JSON nesnelerini döndürecek.

Şimdi, eğer window.ethereum varsa, işte o zaman işler ilginçleşiyor.

Bir deneme/yakalama döngüsü kullanarak, [window.ethereum.request({ method: "eth_requestAccounts" });](https://docs.metamask.io/guide/rpc-api.html#eth-requestaccounts) öğesini çağırarak MetaMask'e bağlanmaya çalışacağız. Bu fonksiyonun çağrılması, tarayıcıda MetaMask'i açar ve bu sayede kullanıcıdan cüzdanını merkeziyetsiz uygulamanıza bağlaması istenir.

  • Kullanıcı bağlanmayı seçerse, method: "eth_requestAccounts", kullanıcının merkeziyetsiz uygulamaya bağlı tüm hesap adreslerini içeren bir dizi döndürür. Toplamda, connectWallet fonksiyonumuz bu dizideki ilk address'i içeren bir JSON nesnesi (9. satıra bakın) ve kullanıcıdan akıllı sözleşmeye bir mesaj yazmasını isteyen bir status mesajı döndürür.
  • Kullanıcı bağlantıyı reddederse, JSON nesnesi, döndürülen address için boş bir dize ve kullanıcının bağlantıyı reddettiğini yansıtan bir status mesajı içerir.

Minter.js UI Bileşeninize connectWallet fonksiyonu ekleyin

Şimdi bu connectWallet fonksiyonunu yazdığımıza göre, onu Minter.js. bileşenimize bağlayalım.

İlk olarak, Minter.js dosyasının en üstüne import { connectWallet } from "./utils/interact.js"; öğesini ekleyerek fonksiyonumuzu Minter.js dosyamıza aktarmamız gerekecek. Minter.js dosyasının ilk 11 satırı şimdi şöyle görünmelidir:

1import { useEffect, useState } from "react";
2import { connectWallet } from "./utils/interact.js";
3
4const Minter = (props) => {
5
6 //State variables
7 const [walletAddress, setWallet] = useState("");
8 const [status, setStatus] = useState("");
9 const [name, setName] = useState("");
10 const [description, setDescription] = useState("");
11 const [url, setURL] = useState("");
Tümünü göster

Ardından, connectWalletPressed fonksiyonumuzun içinde, içe aktarılan connectWallet fonksiyonumuzu şöyle çağıracağız:

1const connectWalletPressed = async () => {
2 const walletResponse = await connectWallet()
3 setStatus(walletResponse.status)
4 setWallet(walletResponse.address)
5}

Fonksiyonlarımızın çoğunun interact.js dosyasındaki Minter.js bileşenimizden nasıl soyutlandığını gördünüz mü? Bu, M-V-C paradigmasına uymamız içindir!

connectWalletPressed'de, içe aktarılan connectWallet fonksiyonumuza bir bekleme çağrısı yaparız ve yanıtını kullanarak durum kancaları aracılığıyla status ve walletAddress değişkenlerimizi güncelleriz.

Şimdi, Minter.js ve interact.js dosyalarını kaydedelim ve şu ana kadarki kullanıcı arayüzümüzü test edelim.

Tarayıcınızı localhost:3000 üzerinde açın ve sayfanın sağ üst köşesindeki "Connect Wallet" düğmesine basın.

MetaMask yüklüyse, cüzdanınızı merkeziyetsiz uygulamanıza bağlamanız istenecektir. Bağlanmak için daveti kabul edin.

Cüzdan düğmesinin artık adresinizin bağlı olduğunu yansıttığını görmelisiniz.

Ardından, sayfayı yenilemeyi deneyin... Garip. Cüzdan düğmemiz zaten bağlı olmasına rağmen MetaMask'i bağlamamızı istiyor...

Ama merak etmeyin! getCurrentWalletConnected adlı bir fonksiyonu uygulayarak bunu kolayca düzeltebiliriz; bu, bir adresin merkeziyetsiz uygulamamıza zaten bağlı olup olmadığını kontrol edecek ve kullanıcı arayüzünü buna göre güncelleyecektir!

GetCurrentWalletConnected fonksiyonu

interact.js dosyanıza aşağıdaki getCurrentWalletConnected fonksiyonunu ekleyin:

1export const getCurrentWalletConnected = async () => {
2 if (window.ethereum) {
3 try {
4 const addressArray = await window.ethereum.request({
5 method: "eth_accounts",
6 })
7 if (addressArray.length > 0) {
8 return {
9 address: addressArray[0],
10 status: "👆🏽 Write a message in the text-field above.",
11 }
12 } else {
13 return {
14 address: "",
15 status: "🦊 Connect to MetaMask using the top right button.",
16 }
17 }
18 } catch (err) {
19 return {
20 address: "",
21 status: "😥 " + err.message,
22 }
23 }
24 } else {
25 return {
26 address: "",
27 status: (
28 <span>
29 <p>
30 {" "}
31 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
32 You must install MetaMask, a virtual Ethereum wallet, in your
33 browser.
34 </a>
35 </p>
36 </span>
37 ),
38 }
39 }
40}
Tümünü göster

Bu kod, az önce yazdığımız connectWallet fonksiyonuna çok benzer.

Temel fark, kullanıcının cüzdanını bağlaması için MetaMask'i açan eth_requestAccounts yöntemini çağırmak yerine, burada yalnızca şu anda merkeziyetsiz uygulamamıza bağlı olan MetaMask adreslerini içeren bir dizi döndüren eth_accounts yöntemini çağırmamızdır.

Bu fonksiyonu çalışırken görmek için, onu Minter.js bileşenimizin useEffect fonksiyonunda çağıralım.

connectWallet için yaptığımız gibi, bu fonksiyonu interact.js dosyamızdan Minter.js dosyamıza şu şekilde aktarmalıyız:

1import { useEffect, useState } from "react"
2import {
3 connectWallet,
4 getCurrentWalletConnected, //import here
5} from "./utils/interact.js"

Şimdi onu useEffect fonksiyonumuzda çağırıyoruz:

1useEffect(async () => {
2 const { address, status } = await getCurrentWalletConnected()
3 setWallet(address)
4 setStatus(status)
5}, [])

Dikkat edin, walletAddress ve status durum değişkenlerimizi güncellemek için getCurrentWalletConnected çağrımızın yanıtını kullanıyoruz.

Bu kodu ekledikten sonra tarayıcı penceremizi yenilemeyi deneyin. Düğme, bağlı olduğunuzu söylemeli ve yeniledikten sonra bile bağlı cüzdanınızın adresinin bir önizlemesini göstermelidir!

AddWalletListener'ı uygulayın

Merkeziyetsiz uygulama cüzdanı kurulumumuzun son adımı, örneğin kullanıcı bağlantısını keserek veya hesap değiştirerek cüzdanımızın durumunu değiştirdiğinde kullanıcı arayüzümüzün güncellenmesi için cüzdan dinleyicisini uygulamaktır.

Minter.js dosyanıza aşağıdakine benzeyen bir addWalletListener fonksiyonu ekleyin:

1function addWalletListener() {
2 if (window.ethereum) {
3 window.ethereum.on("accountsChanged", (accounts) => {
4 if (accounts.length > 0) {
5 setWallet(accounts[0])
6 setStatus("👆🏽 Write a message in the text-field above.")
7 } else {
8 setWallet("")
9 setStatus("🦊 Connect to MetaMask using the top right button.")
10 }
11 })
12 } else {
13 setStatus(
14 <p>
15 {" "}
16 🦊 <a target="_blank" href={`https://metamask.io/download.html`}>
17 You must install MetaMask, a virtual Ethereum wallet, in your browser.
18 </a>
19 </p>
20 )
21 }
22}
Tümünü göster

Burada neler olduğunu hızlıca çözelim:

  • İlk olarak, fonksiyonumuz window.ethereum'un etkin olup olmadığını kontrol eder (yani MetaMask kurulu olup olmadığını).
    • Değilse, status durum değişkenimizi, kullanıcının MetaMask'i yüklemesini isteyen bir JSX dizesine ayarlamamız yeterlidir.
    • Etkinleştirilirse, 3. satırda window.ethereum.on("accountsChanged") dinleyicisini kurarız ve bu dinleyici MetaMask cüzdanındaki, kullanıcının merkeziyetsiz uygulamaya ek bir hesap bağladığı, hesapları değiştirdiği veya bir hesabın bağlantısını kestiği anları da içeren durum değişikliklerini dinler. Bağlı en az bir hesap varsa, walletAddress durum değişkeni, dinleyici tarafından döndürülen accounts dizisindeki ilk hesap olarak güncellenir. Aksi takdirde, walletAddress boş bir dize olarak ayarlanır.

Son olarak, onu useEffect fonksiyonumuzda çağırmalıyız:

1useEffect(async () => {
2 const { address, status } = await getCurrentWalletConnected()
3 setWallet(address)
4 setStatus(status)
5
6 addWalletListener()
7}, [])

İşte oldu! Cüzdan fonksiyonlarımızın tümünü programlamayı tamamladık! Cüzdanımız kurulduğuna göre, şimdi NFT'mizi nasıl basacağımızı bulalım!

NFT Meta Verileri 101

Bu öğreticinin 0. Adımında bahsettiğimiz NFT meta verilerini hatırlayın; dijital varlık, ad, açıklama ve diğer nitelikler gibi özelliklere sahip olmasını sağlayarak bir NFT'ye hayat verirler.

Bu meta verileri bir JSON nesnesi olarak yapılandırmamız ve saklamamız gerekecek, böylece akıllı sözleşmemizin mintNFT fonksiyonunu çağırırken tokenURI parametresi olarak iletebiliriz.

"Link to Asset" (Varlığa Bağlantı), "Name" (Ad) ve "Description" (Açıklama) alanlarındaki metin, NFT'mizin meta verisinin farklı özelliklerini oluşturacaktır. Bu meta verileri bir JSON nesnesi olarak biçimlendireceğiz, ancak bu JSON nesnesini nerede depolayabileceğimiz konusunda birkaç seçenek var:

  • Bunu Ethereum blok zincirinde saklayabiliriz ama bunu yapmak çok pahalı olacaktır.
  • AWS veya Firebase gibi merkezi bir sunucuda depolayabiliriz. Ancak bu, merkeziyetsizlik anlayışımızı bozar.
  • Dağıtılmış bir dosya sisteminde veri depolamak ve paylaşmak için merkeziyetsiz bir protokol ve eşler arası ağ olan IPFS'yi kullanabiliriz. Bu protokol merkeziyetsiz ve ücretsiz olduğu için en iyi seçeneğimizdir!

Meta verilerimizi IPFS'de depolamak için, uygun bir IPFS API'si ve araç takımı olan Pinata(opens in a new tab)'yı kullanacağız. Bir sonraki adımda, bunun tam olarak nasıl yapılacağını açıklayacağız!

(opens in a new tab)Meta verilerinizi IPFS'ye sabitlemek için Pinata'yı kullanın

Pinata(opens in a new tab) hesabınız yoksa, buradan(opens in a new tab) ücretsiz bir hesap için kaydolun ve e-postanızla hesabınızı doğrulamak için adımları tamamlayın.

Pinata API anahtarınızı oluşturun

https://pinata.cloud/keys(opens in a new tab) sayfasına gidin, ardından üst kısımdaki "New Key" (Yeni Anahtar) düğmesini seçin, Yönetici widget'ını ayarlayın etkinleştirin ve anahtarınızı adlandırın.

Ardından, API bilgilerinizi içeren bir açılır pencere gösterilecektir. Bunu güvenli bir yere koyduğunuzdan emin olun.

Artık anahtarımız ayarlandığına göre onu kullanabilmek için projemize ekleyelim.

Bir .env dosyası oluşturun

Pinata anahtarımızı ve sırrımızı bir ortam dosyasında güvenle saklayabiliriz. Proje dizininize dotenv paketini(opens in a new tab) yükleyelim.

Terminalinizde (yerel ana bilgisayarı çalıştırandan ayrı) yeni bir sekme açın ve minter-starter-files klasöründe olduğunuzdan emin olun, ardından terminalinizde aşağıdaki komutu çalıştırın:

1npm install dotenv --save

Ardından, komut satırınıza aşağıdakileri girerek minter-starter-files kök dizininde bir .env dosyası oluşturun:

1vim.env

Bu, .env dosyanızı vim (bir metin editörü) içinde açacaktır. Kaydetmek için klavyenizdeki "esc" + ":" + "q" tuşlarına bu sırayla basın.

Ardından, VSCode'da .env dosyanıza gidin ve Pinata API anahtarınızı ve API sırrınızı şu şekilde ekleyin:

1REACT_APP_PINATA_KEY = <pinata-api-key>
2REACT_APP_PINATA_SECRET = <pinata-api-secret>

Dosyayı kaydettikten sonra JSON meta verilerinizi IPFS'ye yüklemek için fonksiyonu yazmaya başlamaya hazırsınız!

(opens in a new tab)PinJSONToIPFS'yi uygulayın

Neyse ki, Pinata'nın özellikle JSON verilerini IPFS'ye yüklemek için bir API'si(opens in a new tab) ve axios örneğiyle bazı ufak değişiklikler yaparak birlikte kullanabileceğimiz uygun bir JavaScript'i var.

utils klasörünüzde pinata.js adında başka bir dosya oluşturalım ve ardından .env dosyasından Pinata sırrımızı ve anahtarımızı şu şekilde içe aktaralım:

1require("dotenv").config()
2const key = process.env.REACT_APP_PINATA_KEY
3const secret = process.env.REACT_APP_PINATA_SECRET

Ardından, aşağıdaki ek kodu pinata.js dosyanıza yapıştırın. Endişelenmeyin, her şeyin ne anlama geldiğini anlatacağız!

1require("dotenv").config()
2const key = process.env.REACT_APP_PINATA_KEY
3const secret = process.env.REACT_APP_PINATA_SECRET
4
5const axios = require("axios")
6
7export const pinJSONToIPFS = async (JSONBody) => {
8 const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`
9 //making axios POST request to Pinata ⬇️
10 return axios
11 .post(url, JSONBody, {
12 headers: {
13 pinata_api_key: key,
14 pinata_secret_api_key: secret,
15 },
16 })
17 .then(function (response) {
18 return {
19 success: true,
20 pinataUrl:
21 "https://gateway.pinata.cloud/ipfs/" + response.data.IpfsHash,
22 }
23 })
24 .catch(function (error) {
25 console.log(error)
26 return {
27 success: false,
28 message: error.message,
29 }
30 })
31}
Tümünü göster

Peki bu kod tam olarak ne yapıyor?

İlk olarak, Pinata'ya bir istekte bulunmak için kullanacağımız tarayıcı ve node.js için söz tabanlı bir HTTP istemcisi olan axios(opens in a new tab)'u içe aktarır.

Ardından, pinJSONToIPFS API'lerine bir POST isteği yapmak için girişi olarak bir JSONBody ve başlığında Pinata api anahtarını ve sırrını alan eş zamansız fonksiyonumuz pinJSONToIPFS'ye sahibiz.

  • Bu POST isteği başarılı olursa, işlevimiz success boolean'ı true olarak ve meta verilerimizin sabitlendiği pinataUrl ile bir JSON nesnesi döndürür. Akıllı sözleşmemizin mint fonksiyonuna tokenURI girişi olarak döndürülen bu pinataUrl öğesini kullanacağız.
  • Bu post isteği başarısız olursa, fonksiyonumuz success boolean'ı false olan bir JSON nesnesi ve hatamızı ileten bir message dizesi döndürür.

connectWallet fonksiyon dönüş türlerimizde olduğu gibi, durum değişkenlerimizi ve kullanıcı arayüzünü güncellemek amacıyla parametrelerini kullanabilmemiz için JSON nesneleri döndürüyoruz.

Akıllı sözleşmenizi yükleyin

Artık pinJSONToIPFS fonksiyonumuz aracılığıyla NFT meta verilerimizi IPFS'ye yüklemenin bir yolu olduğuna göre, mintNFT fonksiyonunu çağırabilmemiz için akıllı sözleşmemizin bir örneğini yüklemenin bir yoluna ihtiyacımız olacak.

Daha önce bahsettiğimiz gibi, bu öğreticide bu mevcut NFT akıllı sözleşmesini(opens in a new tab) kullanacağız; ancak, bunu nasıl yaptığımızı öğrenmek veya kendiniz yapmak istiyorsanız, diğer "Bir NFT Nasıl Oluşturulur?"(opens in a new tab) öğreticimize göz atmanızı şiddetle tavsiye ederiz.

Sözleşme ABI'ı

Dosyalarımızı yakından incelediyseniz, src dizinimizde bir contract-abi.json dosyası olduğunu fark etmişsinizdir. Bir sözleşmenin hangi fonksiyonu çağıracağını belirlemek ve fonksiyonun beklediğiniz biçimde veri döndürmesini sağlamak için bir ABI gereklidir.

Ayrıca Ethereum blok zincirine bağlanmak ve akıllı sözleşmemizi yüklemek için bir Alchemy API anahtarına ve Alchemy Web3 API'sine ihtiyacımız olacak.

Alchemy API anahtarınızı oluşturun

Henüz bir Alchemy hesabınız yoksa, buradan ücretsiz kaydolun.(opens in a new tab)

Bir Alchemy hesabı oluşturduktan sonra, bir uygulama oluşturarak bir API anahtarı oluşturabilirsiniz. Bu, Ropsten test ağına istekte bulunmamıza izin verecektir.

İmlecinizi gezinme çubuğundaki "Apps"in (Uygulamalar) üzerine gelip "Create App"e (Uygulama Oluştur) tıklayarak Alchemy Gösterge Panelinizdeki "Create App" sayfasına gidin.

Uygulamanıza bir ad verin (biz, "İlk NFT'm!"i seçtik), kısa bir açıklama yazın, Environment (Ortam) için "Staging"i (Hazırlama) seçin (uygulamanızın muhasebesi için kullanılır) ve network (ağ) için "Ropsten"i seçin.

"Create app"e (Uygulama oluştur) tıklamanız yeterlidir! Uygulamanız aşağıdaki tabloda görünmelidir.

Harika, şimdi HTTP Alchemy API URL'mizi oluşturduğumuza göre, onu panonuza kopyalayın...

…ve sonra onu .env dosyamıza ekleyelim. Toplamda, .env dosyanız şöyle görünmelidir:

1REACT_APP_PINATA_KEY = <pinata-key>
2REACT_APP_PINATA_SECRET = <pinata-secret>
3REACT_APP_ALCHEMY_KEY = https://eth-ropsten.alchemyapi.io/v2/<alchemy-key>

Artık sözleşme ABI'ımız ve Alchemy API anahtarımız olduğuna göre, Alchemy Web3(opens in a new tab) kullanarak akıllı sözleşmemizi yüklemeye hazırız.

Alchemy Web3 uç noktanızı ve sözleşmenizi ayarlayın

Öncelikle, henüz sahip değilseniz ana dizine giderek Alchemy Web3(opens in a new tab)'ü yüklemeniz gerekir: terminalde nft-minter-tutorial:

1cd ..
2npm install @alch/alchemy-web3

Şimdi interact.js dosyamıza geri dönelim. Alchemy anahtarınızı .env dosyanızdan içe aktarmak için dosyanın en üstüne aşağıdaki kodu ekleyin ve Alchemy Web3 uç noktanızı ayarlayın:

1require("dotenv").config()
2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
4const web3 = createAlchemyWeb3(alchemyKey)

Alchemy Web3(opens in a new tab), Web3.js(opens in a new tab) paketleyicisidir. Bir web3 geliştiricisi olarak hayatınızı kolaylaştıracak gelişmiş API yöntemleri ve diğer önemli avantajlar sağlar. Uygulamanızda hemen kullanmaya başlayabilmeniz için minimum yapılandırma gerektirecek şekilde tasarlanmıştır!

Ardından sözleşme ABI'ımızı ve sözleşme adresimizi dosyamıza ekleyelim.

1require("dotenv").config()
2const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
3const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
4const web3 = createAlchemyWeb3(alchemyKey)
5
6const contractABI = require("../contract-abi.json")
7const contractAddress = "0x4C4a07F737Bf57F6632B6CAB089B78f62385aCaE"

Her ikisine de sahip olduğumuzda, mint fonksiyonumuzu kodlamaya başlamaya hazırız!

mintNFT fonksiyonunu uygulayın

interact.js dosyanızın içinde, NFT'mizi aynı adla basacak olan mintNFT fonksiyonumuzu tanımlayalım.

Çok sayıda eş zamansız çağrı yapacağımız için (meta verilerimizi IPFS'ye sabitlemek için Pinata'ya, akıllı sözleşmemizi yüklemek için Alchemy Web3'e ve işlemlerimizi imzalamak için MetaMask'e), fonksiyonumuz da eş zamansız olacaktır.

Fonksiyonumuzun üç girdisi, dijital varlığımızın url'si, name'i ve description'ı olacaktır. connectWallet fonksiyonunun altına aşağıdaki fonksiyon imzasını ekleyin:

1export const mintNFT = async (url, name, description) => {}

Girdi hatası işleme

Doğal olarak, girdi parametrelerimiz doğru değilse bu fonksiyondan çıkmak için fonksiyonun başlangıcında bir tür girdi hatası işlemeye sahip olmak mantıklıdır. Fonksiyonumuzun içine aşağıdaki kodu ekleyelim:

1export const mintNFT = async (url, name, description) => {
2 //error handling
3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
4 return {
5 success: false,
6 status: "❗Please make sure all fields are completed before minting.",
7 }
8 }
9}
Tümünü göster

Girdi parametrelerinden herhangi biri boş bir dizeyse, o zaman success boolean'ın false olduğu bir JSON nesnesi döndürürüz ve status dizesi, Kullanıcı Arayüzündeki tüm alanların eksiksiz olması gerektiğini aktarır.

(opens in a new tab)Meta verileri IPFS'ye yükleyin

Meta verilerimizin doğru şekilde biçimlendirildiğini öğrendikten sonraki adım, onu bir JSON nesnesi olarak paketlemek ve yazdığımız pinJSONToIPFS aracılığıyla IPFS'ye yüklemektir!

Bunu yapmak için önce pinJSONToIPFS fonksiyonunu interact.js dosyamıza aktarmamız gerekiyor. interact.js dosyasının en üstüne şunu ekleyelim:

1import { pinJSONToIPFS } from "./pinata.js"

pinJSONToIPFS öğesinin bir JSON gövdesi aldığını hatırlayın. Bu nedenle onu çağırmadan önce url, name ve description parametrelerimizi bir JSON nesnesi olarak biçimlendirmemiz gerekecek.

metadata adında bir JSON nesnesi oluşturmak için kodumuzu güncelleyelim ve ardından bu metadata parametresiyle pinJSONToIPFS'a bir çağrı yapalım:

1export const mintNFT = async (url, name, description) => {
2 //error handling
3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
4 return {
5 success: false,
6 status: "❗Please make sure all fields are completed before minting.",
7 }
8 }
9
10 //make metadata
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 //make pinata call
17 const pinataResponse = await pinJSONToIPFS(metadata)
18 if (!pinataResponse.success) {
19 return {
20 success: false,
21 status: "😢 Something went wrong while uploading your tokenURI.",
22 }
23 }
24 const tokenURI = pinataResponse.pinataUrl
25}
Tümünü göster

Dikkat edin, pinJSONToIPFS(metadata) çağrımızın yanıtını pinataResponse nesnesinde saklıyoruz. Ardından, bu nesneyi herhangi bir hata için ayrıştırırız.

Bir hata varsa, success boolean'ın false olduğu bir JSON nesnesi döndürürüz ve status dizemiz çağrımızın başarısız olduğunu aktarır. Aksi takdirde, pinataURL'u pinataResponse'tan çıkarırız ve onu tokenURI değişkenimiz olarak saklarız.

Şimdi dosyamızın başında başlattığımız Alchemy Web3 API'sini kullanarak akıllı sözleşmemizi yükleme zamanı. Sözleşmeyi window.contract global değişkeninde ayarlamak için mintNFT fonksiyonunun altına aşağıdaki kod satırını ekleyin:

1window.contract = await new web3.eth.Contract(contractABI, contractAddress)

mintNFT fonksiyonumuza eklenecek son şey Ethereum işlemimizdir:

1//set up your Ethereum transaction
2const transactionParameters = {
3 to: contractAddress, // Required except during contract publications.
4 from: window.ethereum.selectedAddress, // must match user's active address.
5 data: window.contract.methods
6 .mintNFT(window.ethereum.selectedAddress, tokenURI)
7 .encodeABI(), //make call to NFT smart contract
8}
9
10//sign the transaction via MetaMask
11try {
12 const txHash = await window.ethereum.request({
13 method: "eth_sendTransaction",
14 params: [transactionParameters],
15 })
16 return {
17 success: true,
18 status:
19 "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" +
20 txHash,
21 }
22} catch (error) {
23 return {
24 success: false,
25 status: "😥 Something went wrong: " + error.message,
26 }
27}
Tümünü göster

Ethereum işlemlerine zaten aşinaysanız, yapının gördüklerinize oldukça benzer olduğunu fark edeceksiniz.

  • İlk olarak işlem parametrelerimizi oluşturuyoruz.
    • to alıcı adresini belirtir (akıllı sözleşmemiz)
    • from, işlemi imzalayanı belirtir (kullanıcının MetaMask'a bağlı adresi: window.ethereum.selectedAddress)
    • data, tokenURI'ımızı ve kullanıcının cüzdan adresi olan window.ethereum.selectedAddress'i girdi olarak alan akıllı sözleşme mintNFT yöntemimize yapılan çağrıyı içerir
  • Ardından, MetaMask'ten işlemi imzalamasını istediğimiz window.ethereum.request adlı bir bekleme çağrısı yaparız. Dikkat edin, bu istekte eth yöntemimizi (eth_SentTransaction) belirtiyoruz ve transactionParameters'ımızı aktarıyoruz. Bu noktada, MetaMask tarayıcıda açılır ve kullanıcıdan işlemi imzalamasını veya reddetmesini ister.
    • İşlem başarılı olursa fonksiyon, success boolean'ının true olarak ayarlandığı bir JSON nesnesi döndürür ve status dizesi kullanıcıdan işlemleri hakkında daha fazla bilgi için Etherscan'i kontrol etmesini ister.
    • İşlem başarısız olursa fonksiyon, success boolean'ının false olarak ayarlandığı bir JSON nesnesi döndürür ve status dizesi hata mesajını aktarır.

Toplamda, mintNFT fonksiyonumuz şöyle görünmelidir:

1export const mintNFT = async (url, name, description) => {
2 //error handling
3 if (url.trim() == "" || name.trim() == "" || description.trim() == "") {
4 return {
5 success: false,
6 status: "❗Please make sure all fields are completed before minting.",
7 }
8 }
9
10 //make metadata
11 const metadata = new Object()
12 metadata.name = name
13 metadata.image = url
14 metadata.description = description
15
16 //pinata pin request
17 const pinataResponse = await pinJSONToIPFS(metadata)
18 if (!pinataResponse.success) {
19 return {
20 success: false,
21 status: "😢 Something went wrong while uploading your tokenURI.",
22 }
23 }
24 const tokenURI = pinataResponse.pinataUrl
25
26 //load smart contract
27 window.contract = await new web3.eth.Contract(contractABI, contractAddress) //loadContract();
28
29 //set up your Ethereum transaction
30 const transactionParameters = {
31 to: contractAddress, // Required except during contract publications.
32 from: window.ethereum.selectedAddress, // must match user's active address.
33 data: window.contract.methods
34 .mintNFT(window.ethereum.selectedAddress, tokenURI)
35 .encodeABI(), //make call to NFT smart contract
36 }
37
38 //sign transaction via MetaMask
39 try {
40 const txHash = await window.ethereum.request({
41 method: "eth_sendTransaction",
42 params: [transactionParameters],
43 })
44 return {
45 success: true,
46 status:
47 "✅ Check out your transaction on Etherscan: https://ropsten.etherscan.io/tx/" +
48 txHash,
49 }
50 } catch (error) {
51 return {
52 success: false,
53 status: "😥 Something went wrong: " + error.message,
54 }
55 }
56}
Tümünü göster

Bu dev bir fonksiyon! Şimdi, mintNFT fonksiyonumuzu Minter.js bileşenimize bağlamamız gerekiyor...

MintNFT'yi Minter.js ön ucumuza bağlayın

Minter.js dosyanızı açın ve en üstteki import { connectWallet, getCurrentWalletConnected } from "./utils/interact.js"; satırını şu şekilde güncelleyin:

1import {
2 connectWallet,
3 getCurrentWalletConnected,
4 mintNFT,
5} from "./utils/interact.js"

Son olarak, içe aktarılan mintNFT fonksiyonunuza bekleme çağrısı yapmak için onMintPressed fonksiyonunu uygulayın ve işlemimizin başarılı mı yoksa başarısız mı olduğunu yansıtmak için status durum değişkenini güncelleyin:

1const onMintPressed = async () => {
2 const { status } = await mintNFT(url, name, description)
3 setStatus(status)
4}

NFT'nizi yayındaki bir web sitesinde yayınlayın

Kullanıcıların etkileşim kurması için projenizi yayınlamaya hazır mısınız? Minter'ınızı canlı bir web sitesine dağıtmak için bu öğreticiye(opens in a new tab) göz atın.

Son bir adım...

Blok zinciri dünyasını kasıp kavurun

Şaka yapıyorum, öğreticiyi tamamladınız!

Özetlemek gerekirse, bir NFT minter oluşturarak şunları nasıl yapacağınızı başarıyla öğrendiniz:

  • Ön uç projeniz aracılığıyla MetaMask'a bağlanma
  • Ön ucunuzdan akıllı sözleşme yöntemlerini arama
  • MetaMask kullanarak işlemleri imzalama

Muhtemelen, cüzdanınızda merkeziyetsiz uygulamanız aracılığıyla basılan NFT'leri gösterebilmek istersiniz. Bu nedenle NFT'nizi Cüzdanınızda Görüntüleme(opens in a new tab) adlı hızlı öğreticimize göz atmayı unutmayın!

Ve her zaman olduğu gibi, herhangi bir sorunuz olursa Alchemy Discord(opens in a new tab)'da size yardım etmeye hazırız. Bu öğreticideki kavramları gelecekteki projelerinize nasıl uygulayacağınızı görmek için sabırsızlanıyoruz!

Son düzenleme: @sekoman01(opens in a new tab), 23 Kasım 2023

Bu rehber yararlı oldu mu?