Każdy chce to mieć, to ja też – tworzymy tokeny NFT

Przemek/ 29 sierpnia, 2022

Jeszcze niedawno trwał ogromny boom na NFT. Nieważne czy był to obrazek małpy, psa czy frytek z McDonald’s, tokeny rozchodziły się jak świeże bułeczki. Obecnie sytuacja się uspokoiła, ale zainteresowanie tą technologią wciąż jest ogromne. Dlatego dzisiaj napiszemy swój smart kontrakt, który pozwoli na tworzenie własnych tokenów NFT. Przyda się do tego wiedza z poprzednich wpisów, dlatego polecam zapoznać się najpierw z nimi:

Przyjacielskie Pingwiny

Rysunek 1. Pingwiny
(źródło: https://wildexplained.com/are-penguins-friendly/)

Na początku zdefiniujmy, co takiego będziemy chcieli napisać. Nasze NFT będzie bardzo proste. Kontrakt oprzemy o standard ERC-721. Skorzystamy z gotowej implementacji dostarczonej przez OpenZeppelin, którą jedynie dostosujemy pod swoje potrzeby. Tokeny, które będzie można tworzyć przy pomocy kontraktu będą reprezentowały kolekcję pod tytułem “Przyjacielske Pingwiny”. Właścicielami tokenów zostaną trzej użytkownicy, którzy jako pierwsi się po nie zgłoszą. Każdy token losowany będzie z dostępnej puli, aż do wyczerpania zapasów. Żeby uprościć cały proces, tym razem skorzystamy z pseudolosowości wygenerowanej w obrębie smart kontraktu. Jednak należy pamiętać, że w przypadku wypuszczenia tego rodzaju tokenów dla szerokiej publiczności, lepszą opcją byłoby skorzystanie z Chainlink VRF. Jeżeli jeszcze nie wiesz jak używać tego mechanizmu, to przykład znajdziesz w tym wpisie.

Wymagań do kontraktu nie ma dużo, dlatego możemy przejść do działania. Wszystkie wykorzystane pliki wraz z kodem kontraktu znajdziesz pod tym adresem.

Metadane i obrazki

Rozpoczniemy od przygotowania obrazków reprezentujących pingwiny oraz plików zawierających metadane tokenów, a następnie umieścimy je na IPFS-ie. Grafiki pobierzemy z serwisu freepick. Po zebraniu obrazków, logujemy się do swojego konta w serwisie Pinata.cloud. Jeżeli jeszcze nie miałeś okazji utworzyć tam profilu, to teraz jest na to dobry moment :). Po wgraniu plików, wygenerują się dla nich ich identyfikatory, dzięki którym będzie można je odszukać w sieci IPFS.

Teraz możemy stworzyć pliki z metadanymi. Ponieważ będziemy chcieli, aby nasze NFT wyświetlało się automatycznie w OpenSea (największy marketplace NFT), skorzystamy ze standardu, który dostarcza wspomniany serwis. 

Ponieważ nasze tokeny będą bardzo proste, dlatego w metadanych umieścimy jedynie opis kolekcji, nazwy poszczególnych tokenów oraz adres obrazka. Przykładowy plik wygląda następująco:

{
  "description": "Friendly Penguins", 
  "image": "ipfs://QmYZxoaJgNSXiaTTFSUuWFYtkkYeBW8iKWuSGJXsiga76G", 
  "name": "Hockey Player"
}

Utworzone dokumenty nazwiemy kolejnymi cyframi od 0 do 2, które będą odpowiadały identyfikatorom tokenów. Aby uprościć późniejszą implementację, zapiszemy pliki bez żadnego rozszerzenia. Po utworzeniu dokumentów umieszczamy je w jednym folderze, a następnie wgrywamy go na Pinatę.

Kodujemy

Rysunek 2. Kodujemy
(źródło: https://www.badgerbots.org/code-at-home-with-free-lessons/)

Wszystkie niezbędne pliki mamy już przygotowane, czas przejść do napisania kontraktu. Poniżej znajduje się cały kod naszego NFT.

///SPDX-License-Identifier: MIT
pragma solidity 0.8.7;
 
import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol";
 
error NFT_LimitReached();
 
contract NFT is ERC721 {
   uint256 private s_counter;
   uint256 private s_limit;
   string private s_cid;
   uint256[] private s_remainingTokens;
 
   event Minted(address _who, uint256 _id);
 
   constructor(string memory _name, string memory _symbol, uint256 _limit, string memory _cid)
       ERC721(_name, _symbol)
   {
       s_limit = _limit;
       s_cid = _cid;
 
       for(uint i = 0; i < _limit; i++) {
           s_remainingTokens.push(i);
       }
   }
 
   function mint() public {
       if(s_counter == s_limit) {
           revert NFT_LimitReached();
       }
      
       uint256 id = drawToken();
       _mint(msg.sender, id);
      
       s_counter++;
       emit Minted(msg.sender, id);
   }
 
    function _baseURI() internal view override returns (string memory) {
       return s_cid;
   }
 
   function drawToken() private returns(uint256) {
       uint256 randomNumber = uint256(keccak256(abi.encodePacked(block.timestamp, block.number)));
       uint256 tokenIndex = randomNumber % s_remainingTokens.length;
      
       uint256 tokenId = s_remainingTokens[tokenIndex];
      
       s_remainingTokens[tokenIndex] = s_remainingTokens[s_remainingTokens.length - 1];
       s_remainingTokens.pop();
 
       return tokenId;
   }
}

Kontrakt dziedziczy po implementacji ERC721 dostarczonej przez OpenZeppelin. Dzięki temu nie musimy tworzyć wszystkich niezbędnych funkcjonalności od zera, a skupimy się jedynie na mechanizmie “wykuwania” tokenów. Na początku kontraktu znajdują się zmienne “s_counter” oraz “s_limit”, które posłużą nam do ograniczenia liczby istniejących pingwinów. Pole “s_cid” będzie przechowywało identyfikator folderu z metadanymi. Ostatnie pole to tablica, która zawiera informacje o tokenach, które nie zostały jeszcze wydobyte. Poszczególne wartości zostaną zainicjalizowane przy pomocy konstruktora.

Funkcja, która pozwoli na losowanie tokenów nosi nazwę “mint” i wywołana może być przez dowolną osobę. Na początku sprawdzamy, czy istnieją jeszcze jakieś tokeny do wylosowania i jeśli nie, to “rzucamy” błąd. W przeciwnym wypadku przystępujemy do losowania. Cała logika znajduje się w funkcji “drawToken”. Jedyne co musimy zrobić, to wylosować unikatowe id, które będzie odpowiadało konkretnemu plikowi z metadanymi. Po wyborze identyfikatora przypisujemy token do użytkownika przy pomocy funkcji “_mint” pochodzącej z kontraktu ERC721. Na końcu zwiększamy licznik stworzonych tokenów oraz emitujemy event.

Jeszcze jedną rzeczą, którą musimy zrobić to nadpisanie funkcji “baseURI”. Jeżeli zagłębilibyśmy się w implementację kontraktu ERC721 od OpenZeppelina, to znaleźlibyśmy tam funkcję “tokenURI”, której zadaniem jest zwrócenie adresu, pod którym znajdują się metadane konkretnego tokena. Jej implementacja polega na zwróceniu wartości, która jest połączeniem rezultatu funkcji “baseURI” oraz id tokena. 

Mogę już dostać swoje NFT?

Wszystko co niezbędne mamy już gotowe, dlatego teraz możemy sprawdzić, jak to zadziała. Na początek musimy wgrać smart kontrakt na blockchain. Robiliśmy to już nie raz, więc nie będę się zagłębiał w cały proces. Ważne jest jedynie, aby skorzystać z sieci testowej Rinkeby, a nie imitacji blockchaina. Jeżeli nie masz środków na swoim koncie, to skorzystaj z tego serwisu. Pamiętaj aby parametr “_cid” podać w odpowiedniej formie. W innym przypadku OpenSea, nie będzie w stanie poprawnie odczytać danych. Przykładowa wartość powinna wyglądać następująco: 

ipfs://QmaCsaJKhq5WCJHY1D43eW7L2xeyXrvn4mej7CygxWVMFn/

Po wgraniu smart kontraktu zauważysz, że posiada on zdecydowanie więcej funkcji, niż te które zaimplementowaliśmy. Wszystkie one pochodzą z implementacji ERC721. My natomiast skorzystamy z funkcji “mint”. Po jej wywołaniu możemy przejść na testowy portal OpenSea i wyszukać stworzony token. Robimy to podając adres kontraktu NFT lub adres portfela, do którego token został przypisany. Może się okazać, że nie od razu zobaczysz token, ponieważ czasem synchronizacja chwilę zajmuje. Ale po krótszej lub dłuższej chwili token powinien ukazać się Twoim oczom. Mój pierwszy wylosowany token to Fisherman 🙂


Wywołajmy jeszcze dwa razy funkcję “mint”, aby utworzyć wszystkie możliwe tokeny. Po chwili powinny być one widoczne na OpenSea.


Teraz już wiesz, na czym polega tworzenie tokenów NFT. Mimo że nasz kontrakt nie wyróżnia się niczym szczególnym, to jest to dobry punkt startowy do rozpoczęcia przygody z tą technologią. Pamiętaj, że takie tokeny mogą być czymś więcej niż reprezentacją cyfrowych obrazów, a ich zastosowanie zależy jedynie od Twojej wyobraźni.

W tym miejscu zakończę wpisy związane z NFT, ponieważ czeka na nas kolejny niezwykle interesujący temat ze świata Web 3.0, jakim jest DeFi. Do zobaczenia w kolejnym wpisie 🙂

Share this Post
Subscribe
Powiadom o
guest
0 komentarzy
Inline Feedbacks
View all comments