Inteligentny kontrakt Hello World dla początkujących – Fullstack
Ten przewodnik jest dla Ciebie, jeśli dopiero zaczynasz programowanie w technologii blockchain i nie wiesz, od czego zacząć ani jak wdrażać inteligentne kontrakty i wchodzić z nimi w interakcje. Przejdziemy przez proces tworzenia i wdrażania prostego inteligentnego kontraktu w sieci testowej Goerli przy użyciu MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab) oraz Alchemy (opens in a new tab).
Aby ukończyć ten samouczek, będziesz potrzebować konta Alchemy. Zarejestruj darmowe konto (opens in a new tab).
Jeśli w którymkolwiek momencie będziesz mieć pytania, śmiało napisz na Discordzie Alchemy (opens in a new tab)!
Część 1 – Tworzenie i wdrażanie inteligentnego kontraktu za pomocą narzędzia Hardhat
Połącz się z siecią Ethereum
Istnieje wiele sposobów na wysyłanie żądań do łańcucha Ethereum. Dla uproszczenia użyjemy darmowego konta na platformie Alchemy, która jest platformą dla programistów blockchain i API, pozwalającą na komunikację z łańcuchem Ethereum bez konieczności samodzielnego uruchamiania węzła. Alchemy posiada również narzędzia programistyczne do monitorowania i analityki; wykorzystamy je w tym samouczku, aby zrozumieć, jak technicznie działa wdrożenie naszego inteligentnego kontraktu.
Utwórz swoją aplikację i klucz API
Po utworzeniu konta Alchemy możesz wygenerować klucz API, tworząc aplikację. Pozwoli to na wysyłanie żądań do sieci testowej Goerli. Jeśli nie znasz sieci testowych, możesz przeczytać przewodnik Alchemy dotyczący wyboru sieci (opens in a new tab).
W panelu nawigacyjnym Alchemy znajdź menu rozwijane Apps i kliknij Create App.
Nadaj swojej aplikacji nazwę „Hello World” i napisz krótki opis. Wybierz Staging jako środowisko i Goerli jako sieć.
Uwaga: upewnij się, że wybierasz Goerli, w przeciwnym razie ten samouczek nie zadziała.
Kliknij Create app. Twoja aplikacja pojawi się w tabeli poniżej.
Utwórz konto Ethereum
Potrzebujesz konta Ethereum, aby wysyłać i odbierać transakcje. Użyjemy MetaMask, wirtualnego portfela w przeglądarce, który pozwala użytkownikom zarządzać adresem ich konta Ethereum.
Możesz pobrać i utworzyć konto MetaMask za darmo tutaj (opens in a new tab). Podczas tworzenia konta, lub jeśli już je posiadasz, upewnij się, że przełączyłeś się na „Goerli Test Network” w prawym górnym rogu (abyśmy nie operowali prawdziwymi pieniędzmi).
Krok 4: Dodaj ether z kranika
Aby wdrożyć swój inteligentny kontrakt w sieci testowej, będziesz potrzebować trochę fałszywego ETH. Aby zdobyć ETH w sieci Goerli, przejdź do kranika Goerli i wprowadź adres swojego konta Goerli. Zauważ, że kraniki Goerli mogą być ostatnio nieco zawodne – zobacz stronę sieci testowych, aby uzyskać listę opcji do wypróbowania:
Uwaga: ze względu na przeciążenie sieci może to chwilę potrwać. ``
Krok 5: Sprawdź swoje saldo
Aby upewnić się, że ETH znajduje się w Twoim portfelu, wyślijmy żądanie eth_getBalance (opens in a new tab) za pomocą narzędzia composer od Alchemy (opens in a new tab). Zwróci to ilość ETH w naszym portfelu. Aby dowiedzieć się więcej, sprawdź krótki samouczek Alchemy na temat korzystania z narzędzia composer (opens in a new tab).
Wprowadź adres swojego konta MetaMask i kliknij Send Request. Zobaczysz odpowiedź, która wygląda jak poniższy fragment kodu.
{ "jsonrpc": "2.0", "id": 0, "result": "0x2B5E3AF16B1880000" }
Uwaga: Ten wynik jest w wei, a nie w ETH. Wei jest używane jako najmniejszy nominał etheru.
Uff! Nasze fałszywe pieniądze są na miejscu.
Krok 6: Zainicjuj nasz projekt
Najpierw musimy utworzyć folder dla naszego projektu. Przejdź do wiersza poleceń i wpisz poniższe komendy.
mkdir hello-world
cd hello-world
Teraz, gdy jesteśmy w folderze naszego projektu, użyjemy npm init, aby zainicjować projekt.
Jeśli nie masz jeszcze zainstalowanego npm, postępuj zgodnie z tymi instrukcjami, aby zainstalować Node.js i npm (opens in a new tab).
Na potrzeby tego samouczka nie ma znaczenia, jak odpowiesz na pytania inicjalizacyjne. Oto jak zrobiliśmy to w celach informacyjnych:
package name: (hello-world)
version: (1.0.0)
description: hello world smart contract
entry point: (index.js)
test command:
git repository:
keywords:
author:
license: (ISC)
About to write to /Users/.../.../.../hello-world/package.json:
{
"name": "hello-world",
"version": "1.0.0",
"description": "hello world smart contract",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC"
}
Zatwierdź plik package.json i gotowe!
Krok 7: Pobierz Hardhat
Hardhat to środowisko programistyczne do kompilacji, wdrażania, testowania i debugowania oprogramowania Ethereum. Pomaga programistom w budowaniu inteligentnych kontraktów i zdecentralizowanych aplikacji (dapp) lokalnie przed wdrożeniem ich w głównym łańcuchu.
Wewnątrz naszego projektu hello-world uruchom:
npm install --save-dev hardhat
Sprawdź tę stronę, aby uzyskać więcej szczegółów na temat instrukcji instalacji (opens in a new tab).
Krok 8: Utwórz projekt Hardhat
Wewnątrz folderu naszego projektu hello-world uruchom:
npx hardhat
Powinieneś wtedy zobaczyć wiadomość powitalną i opcję wyboru tego, co chcesz zrobić. Wybierz „create an empty hardhat.config.js”:
888 888 888 888 888
888 888 888 888 888
888 888 888 888 888
8888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
888 888 "88b 888P" d88" 888 888 "88b "88b 888
888 888 .d888888 888 888 888 888 888 .d888888 888
888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
👷 Welcome to Hardhat v2.0.11 👷
What do you want to do? …
Create a sample project
❯ Create an empty hardhat.config.js
Quit
Spowoduje to wygenerowanie pliku hardhat.config.js w projekcie. Użyjemy go w dalszej części samouczka, aby określić konfigurację naszego projektu.
Krok 9: Dodaj foldery projektu
Aby utrzymać porządek w projekcie, utwórzmy dwa nowe foldery. W wierszu poleceń przejdź do katalogu głównego swojego projektu hello-world i wpisz:
mkdir contracts
mkdir scripts
contracts/to miejsce, w którym będziemy przechowywać plik z kodem naszego inteligentnego kontraktu hello worldscripts/to miejsce, w którym będziemy przechowywać skrypty do wdrażania i interakcji z naszym kontraktem
Krok 10: Napisz nasz kontrakt
Możesz zadawać sobie pytanie, kiedy w końcu zaczniemy pisać kod? Właśnie teraz!
Otwórz projekt hello-world w swoim ulubionym edytorze. Inteligentne kontrakty najczęściej pisane są w języku Solidity, którego użyjemy do napisania naszego inteligentnego kontraktu.
- Przejdź do folderu
contractsi utwórz nowy plik o nazwieHelloWorld.sol - Poniżej znajduje się przykładowy inteligentny kontrakt Hello World, którego będziemy używać w tym samouczku. Skopiuj poniższą zawartość do pliku
HelloWorld.sol.
Uwaga: Pamiętaj, aby przeczytać komentarze, aby zrozumieć, co robi ten kontrakt.
// Określa wersję Solidity, używając wersjonowania semantycznego.
// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity >=0.7.3;
// Definiuje kontrakt o nazwie `HelloWorld`.
// Kontrakt to zbiór funkcji i danych (jego stan). Po wdrożeniu kontrakt znajduje się pod określonym adresem na blockchainie Ethereum. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
contract HelloWorld {
// Emitowane po wywołaniu funkcji update
// Zdarzenia inteligentnego kontraktu to sposób, w jaki Twój kontrakt komunikuje front-endowi aplikacji, że coś wydarzyło się na blockchainie. Front-end może „nasłuchiwać” określonych zdarzeń i podejmować działania, gdy one wystąpią.
event UpdatedMessages(string oldStr, string newStr);
// Deklaruje zmienną stanu `message` typu `string`.
// Zmienne stanu to zmienne, których wartości są trwale przechowywane w pamięci kontraktu. Słowo kluczowe `public` sprawia, że zmienne są dostępne z zewnątrz kontraktu i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać, aby uzyskać dostęp do wartości.
string public message;
// Podobnie jak w wielu klasowych językach obiektowych, konstruktor jest specjalną funkcją, która jest wykonywana tylko podczas tworzenia kontraktu.
// Konstruktory służą do inicjowania danych kontraktu. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
constructor(string memory initMessage) {
// Przyjmuje argument typu string `initMessage` i ustawia jego wartość w zmiennej pamięci kontraktu `message`.
message = initMessage;
}
// Publiczna funkcja, która przyjmuje argument typu string i aktualizuje zmienną pamięci `message`.
function update(string memory newMessage) public {
string memory oldMsg = message;
message = newMessage;
emit UpdatedMessages(oldMsg, newMessage);
}
}
Jest to podstawowy inteligentny kontrakt, który przechowuje wiadomość po utworzeniu. Można ją zaktualizować, wywołując funkcję update.
Krok 11: Połącz MetaMask i Alchemy ze swoim projektem
Utworzyliśmy portfel MetaMask, konto Alchemy i napisaliśmy nasz inteligentny kontrakt, teraz nadszedł czas, aby połączyć te trzy elementy.
Każda transakcja wysłana z Twojego portfela wymaga podpisu przy użyciu Twojego unikalnego klucza prywatnego. Aby zapewnić naszemu programowi to uprawnienie, możemy bezpiecznie przechowywać nasz klucz prywatny w pliku środowiskowym. Będziemy tu również przechowywać klucz API dla Alchemy.
Aby dowiedzieć się więcej o wysyłaniu transakcji, sprawdź ten samouczek (opens in a new tab) dotyczący wysyłania transakcji za pomocą Web3.
Najpierw zainstaluj pakiet dotenv w katalogu swojego projektu:
npm install dotenv --save
Następnie utwórz plik .env w katalogu głównym projektu. Dodaj do niego swój klucz prywatny MetaMask oraz adres URL HTTP API Alchemy.
Twój plik środowiskowy musi nazywać się .env, w przeciwnym razie nie zostanie rozpoznany jako plik środowiskowy.
Nie nazywaj go process.env ani .env-custom ani w żaden inny sposób.
- Postępuj zgodnie z tymi instrukcjami (opens in a new tab), aby wyeksportować swój klucz prywatny
- Zobacz poniżej, jak uzyskać adres URL HTTP API Alchemy
Twój plik .env powinien wyglądać tak:
API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PRIVATE_KEY = "your-metamask-private-key"
Aby faktycznie połączyć je z naszym kodem, odwołamy się do tych zmiennych w naszym pliku hardhat.config.js w kroku 13.
Krok 12: Zainstaluj Ethers.js
Ethers.js to biblioteka, która ułatwia interakcję i wysyłanie żądań do Ethereum poprzez opakowanie standardowych metod JSON-RPC (opens in a new tab) w bardziej przyjazne dla użytkownika metody.
Hardhat pozwala nam na integrację wtyczek (opens in a new tab) w celu uzyskania dodatkowych narzędzi i rozszerzonej funkcjonalności. Wykorzystamy wtyczkę Ethers (opens in a new tab) do wdrożenia kontraktu.
W katalogu swojego projektu wpisz:
npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"
Krok 13: Zaktualizuj plik hardhat.config.js
Do tej pory dodaliśmy kilka zależności i wtyczek, teraz musimy zaktualizować plik hardhat.config.js, aby nasz projekt wiedział o nich wszystkich.
Zaktualizuj swój plik hardhat.config.js, aby wyglądał tak:
/**
* @type import('hardhat/config').HardhatUserConfig
*/
require("dotenv").config()
require("@nomiclabs/hardhat-ethers")
const { API_URL, PRIVATE_KEY } = process.env
module.exports = {
solidity: "0.7.3",
defaultNetwork: "goerli",
networks: {
hardhat: {},
goerli: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`],
},
},
}
Krok 14: Skompiluj nasz kontrakt
Aby upewnić się, że wszystko do tej pory działa, skompilujmy nasz kontrakt. Zadanie compile jest jednym z wbudowanych zadań narzędzia Hardhat.
Z wiersza poleceń uruchom:
npx hardhat compile
Możesz otrzymać ostrzeżenie o SPDX license identifier not provided in source file, ale nie musisz się tym martwić — miejmy nadzieję, że wszystko inne wygląda dobrze! Jeśli nie, zawsze możesz napisać na Discordzie Alchemy (opens in a new tab).
Krok 15: Napisz nasz skrypt wdrożeniowy
Teraz, gdy nasz kontrakt jest napisany, a plik konfiguracyjny jest gotowy, nadszedł czas, aby napisać nasz skrypt wdrożeniowy kontraktu.
Przejdź do folderu scripts/ i utwórz nowy plik o nazwie deploy.js, dodając do niego następującą zawartość:
async function main() {
const HelloWorld = await ethers.getKontraktFactory("HelloWorld")
// Rozpocznij wdrożenie, zwracając obietnicę, która rozwiązuje się do obiektu kontraktu
const hello_world = await HelloWorld.deploy("Hello World!")
console.log("Contract deployed to address:", hello_world.address)
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error)
process.exit(1)
})
Hardhat świetnie wyjaśnia, co robi każda z tych linii kodu w swoim samouczku dotyczącym kontraktów (opens in a new tab), zaadaptowaliśmy ich wyjaśnienia tutaj.
const HelloWorld = await ethers.getContractFactory("HelloWorld")
ContractFactory w ethers.js to abstrakcja używana do wdrażania nowych inteligentnych kontraktów, więc HelloWorld jest tutaj fabryką (opens in a new tab) dla instancji naszego kontraktu hello world. Podczas korzystania z wtyczki hardhat-ethers ContractFactory i Contract, instancje są domyślnie połączone z pierwszym podpisującym (właścicielem).
const hello_world = await HelloWorld.deploy()
Wywołanie deploy() na ContractFactory rozpocznie wdrożenie i zwróci Promise, który rozwiązuje się do obiektu Contract. Jest to obiekt, który posiada metodę dla każdej z funkcji naszego inteligentnego kontraktu.
Krok 16: Wdróż nasz kontrakt
W końcu jesteśmy gotowi na wdrożenie naszego inteligentnego kontraktu! Przejdź do wiersza poleceń i uruchom:
npx hardhat run scripts/deploy.js --network goerli
Powinieneś wtedy zobaczyć coś w stylu:
Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570
Proszę, zapisz ten adres. Będziemy go używać w dalszej części samouczka.
Jeśli przejdziemy do Etherscan dla Goerli (opens in a new tab) i wyszukamy adres naszego kontraktu, powinniśmy zobaczyć, że został on pomyślnie wdrożony. Transakcja będzie wyglądać mniej więcej tak:
Adres From powinien odpowiadać adresowi Twojego konta MetaMask, a adres To będzie wskazywał Contract Creation. Jeśli klikniemy w transakcję, zobaczymy adres naszego kontraktu w polu To.
Gratulacje! Właśnie wdrożyłeś inteligentny kontrakt w sieci testowej Ethereum.
Aby zrozumieć, jak to technicznie działa, przejdźmy do zakładki Explorer w naszym panelu Alchemy (opens in a new tab). Jeśli masz wiele aplikacji Alchemy, upewnij się, że filtrujesz po aplikacji i wybierasz Hello World.
Tutaj zobaczysz garść metod JSON-RPC, które Hardhat/Ethers wykonały dla nas w tle, gdy wywołaliśmy funkcję .deploy(). Dwie ważne metody to eth_sendRawTransaction (opens in a new tab), która jest żądaniem zapisania naszego kontraktu w łańcuchu Goerli, oraz eth_getTransactionByHash (opens in a new tab), która jest żądaniem odczytania informacji o naszej transakcji na podstawie hasha. Aby dowiedzieć się więcej o wysyłaniu transakcji, sprawdź nasz samouczek dotyczący wysyłania transakcji za pomocą Web3.
Część 2: Interakcja z inteligentnym kontraktem
Skoro pomyślnie wdrożyliśmy inteligentny kontrakt w sieci Goerli, dowiedzmy się, jak wejść z nim w interakcję.
Utworzenie pliku interact.js
To jest plik, w którym napiszemy nasz skrypt interakcji. Będziemy używać biblioteki Ethers.js, którą zainstalowałeś wcześniej w Części 1.
Wewnątrz folderu scripts/, utwórz nowy plik o nazwie interact.js i dodaj następujący kod:
// interact.js
const API_KEY = process.env.API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
Aktualizacja pliku .env
Będziemy używać nowych zmiennych środowiskowych, więc musimy je zdefiniować w pliku .env, który utworzyliśmy wcześniej.
Będziemy musieli dodać definicję dla naszego API_KEY z Alchemy oraz CONTRACT_ADDRESS, pod którym wdrożono Twój inteligentny kontrakt.
Twój plik .env powinien wyglądać mniej więcej tak:
# .env
API_URL = "https://eth-goerli.alchemyapi.io/v2/<your-api-key>"
API_KEY = "<your-api-key>"
PRIVATE_KEY = "<your-metamask-private-key>"
CONTRACT_ADDRESS = "0x<your contract address>"
Pobranie ABI kontraktu
naszego kontraktu to interfejs umożliwiający interakcję z naszym inteligentnym kontraktem. Hardhat automatycznie generuje ABI i zapisuje je w HelloWorld.json. Aby użyć ABI, będziemy musieli przeanalizować jego zawartość, dodając następujące wiersze kodu do naszego pliku interact.js:
// interact.js
const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
Jeśli chcesz zobaczyć ABI, możesz je wydrukować w konsoli:
console.log(JSON.stringify(contract.abi))
Aby zobaczyć ABI wydrukowane w konsoli, przejdź do terminala i uruchom:
npx hardhat run scripts/interact.js
Utworzenie instancji kontraktu
Aby wejść w interakcję z naszym kontraktem, musimy utworzyć instancję kontraktu w naszym kodzie. Aby to zrobić za pomocą Ethers.js, będziemy musieli pracować z trzema pojęciami:
- Dostawca - dostawca węzła, który daje Ci dostęp do odczytu i zapisu w blockchainie
- Podpisujący - reprezentuje konto Ethereum, które może podpisywać transakcje
- Contract - obiekt Ethers.js reprezentujący konkretny kontrakt wdrożony w łańcuchu
Użyjemy ABI kontraktu z poprzedniego kroku, aby utworzyć naszą instancję kontraktu:
// interact.js
// Provider
const alchemyProvider = new ethers.providers.AlchemyProvider(
(network = "goerli"),
API_KEY
)
// Signer
const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
// Contract
const helloWorldContract = new ethers.Contract(
CONTRACT_ADDRESS,
contract.abi,
signer
)
Dowiedz się więcej o Providerach, Signerach i Kontraktach w dokumentacji ethers.js (opens in a new tab).
Odczytanie wiadomości początkowej
Pamiętasz, jak wdrożyliśmy nasz kontrakt za pomocą initMessage = "Hello world!"? Teraz odczytamy tę wiadomość zapisaną w naszym inteligentnym kontrakcie i wydrukujemy ją w konsoli.
W języku JavaScript funkcje asynchroniczne są używane podczas interakcji z sieciami. Aby dowiedzieć się więcej o funkcjach asynchronicznych, przeczytaj ten artykuł na Medium (opens in a new tab).
Użyj poniższego kodu, aby wywołać funkcję message w naszym inteligentnym kontrakcie i odczytać wiadomość początkową:
// interact.js
// ...
async function main() {
const message = await helloWorldContract.message()
console.log("The message is: " + message)
}
main()
Po uruchomieniu pliku za pomocą npx hardhat run scripts/interact.js w terminalu powinniśmy zobaczyć taką odpowiedź:
Wiadomość to: Hello world!
Gratulacje! Właśnie pomyślnie odczytałeś dane inteligentnego kontraktu z blockchaina Ethereum, tak trzymaj!
Aktualizacja wiadomości
Zamiast tylko odczytywać wiadomość, możemy również zaktualizować wiadomość zapisaną w naszym inteligentnym kontrakcie za pomocą funkcji update! Całkiem fajnie, prawda?
Aby zaktualizować wiadomość, możemy bezpośrednio wywołać funkcję update na naszym utworzonym obiekcie Contract:
// interact.js
// ...
async function main() {
const message = await helloWorldContract.message()
console.log("The message is: " + message)
console.log("Updating the message...")
const tx = await helloWorldContract.update("This is the new message.")
await tx.wait()
}
main()
Zauważ, że w linii 11 wywołujemy .wait() na zwróconym obiekcie transakcji. Zapewnia to, że nasz skrypt poczeka na wydobycie transakcji w blockchainie przed zakończeniem funkcji. Jeśli wywołanie .wait() nie zostanie uwzględnione, skrypt może nie zobaczyć zaktualizowanej wartości message w kontrakcie.
Odczytanie nowej wiadomości
Powinieneś być w stanie powtórzyć poprzedni krok, aby odczytać zaktualizowaną wartość message. Poświęć chwilę i sprawdź, czy potrafisz wprowadzić zmiany niezbędne do wydrukowania tej nowej wartości!
Jeśli potrzebujesz podpowiedzi, oto jak powinien wyglądać Twój plik interact.js w tym momencie:
// interact.js
const API_KEY = process.env.API_KEY
const PRIVATE_KEY = process.env.PRIVATE_KEY
const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
// dostawca - Alchemy
const alchemyProvider = new ethers.providers.AlchemyProvider(
(network = "goerli"),
API_KEY
)
// podpisujący - ty
const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
// instancja kontraktu
const helloWorldContract = new ethers.Contract(
CONTRACT_ADDRESS,
contract.abi,
signer
)
async function main() {
const message = await helloWorldContract.message()
console.log("The message is: " + message)
console.log("Updating the message...")
const tx = await helloWorldContract.update("this is the new message")
await tx.wait()
const newMessage = await helloWorldContract.message()
console.log("The new message is: " + newMessage)
}
main()
Teraz po prostu uruchom skrypt, a powinieneś zobaczyć starą wiadomość, status aktualizacji i nową wiadomość wydrukowaną w terminalu!
npx hardhat run scripts/interact.js --network goerli
Wiadomość to: Hello World!
Aktualizowanie wiadomości...
Nowa wiadomość to: This is the new message.
Podczas uruchamiania tego skryptu możesz zauważyć, że krok Updating the message... ładuje się przez chwilę, zanim załaduje się nowa wiadomość. Wynika to z procesu wydobywania; jeśli jesteś ciekawy śledzenia transakcji podczas ich wydobywania, odwiedź mempool Alchemy (opens in a new tab), aby zobaczyć status transakcji. Jeśli transakcja zostanie odrzucona, warto również sprawdzić Goerli Etherscan (opens in a new tab) i wyszukać hash swojej transakcji.
Część 3: Opublikuj swój inteligentny kontrakt w Etherscan
Wykonałeś całą ciężką pracę, aby ożywić swój inteligentny kontrakt; teraz nadszedł czas, aby podzielić się nim ze światem!
Dzięki weryfikacji inteligentnego kontraktu w Etherscan, każdy może zobaczyć jego kod źródłowy i wejść z nim w interakcję. Zaczynajmy!
Krok 1: Wygeneruj klucz API na swoim koncie Etherscan
Klucz API Etherscan jest niezbędny do zweryfikowania, że jesteś właścicielem inteligentnego kontraktu, który próbujesz opublikować.
Jeśli nie masz jeszcze konta Etherscan, zarejestruj się (opens in a new tab).
Po zalogowaniu znajdź swoją nazwę użytkownika na pasku nawigacyjnym, najedź na nią i wybierz przycisk My profile.
Na stronie swojego profilu powinieneś zobaczyć boczny pasek nawigacyjny. Z bocznego paska nawigacyjnego wybierz API Keys. Następnie naciśnij przycisk „Add”, aby utworzyć nowy klucz API, nazwij swoją aplikację hello-world i naciśnij przycisk Create New API Key.
Twój nowy klucz API powinien pojawić się w tabeli kluczy API. Skopiuj klucz API do schowka.
Następnie musimy dodać klucz API Etherscan do naszego pliku .env.
Po jego dodaniu, Twój plik .env powinien wyglądać tak:
API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
PUBLIC_KEY = "your-public-account-address"
PRIVATE_KEY = "your-private-account-address"
CONTRACT_ADDRESS = "your-contract-address"
ETHERSCAN_API_KEY = "your-etherscan-key"
Inteligentne kontrakty wdrożone za pomocą Hardhat
Zainstaluj hardhat-etherscan
Publikowanie kontraktu w Etherscan za pomocą Hardhat jest proste. Aby zacząć, musisz najpierw zainstalować wtyczkę hardhat-etherscan. hardhat-etherscan automatycznie zweryfikuje kod źródłowy inteligentnego kontraktu i ABI w Etherscan. Aby to dodać, w katalogu hello-world uruchom:
npm install --save-dev @nomiclabs/hardhat-etherscan
Po zainstalowaniu dołącz następującą instrukcję na górze pliku hardhat.config.js i dodaj opcje konfiguracji Etherscan:
// hardhat.config.js
require("dotenv").config()
require("@nomiclabs/hardhat-ethers")
require("@nomiclabs/hardhat-etherscan")
const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
module.exports = {
solidity: "0.7.3",
defaultNetwork: "goerli",
networks: {
hardhat: {},
goerli: {
url: API_URL,
accounts: [`0x${PRIVATE_KEY}`],
},
},
etherscan: {
// Twój klucz API dla Etherscan
// Zdobądź go na https://etherscan.io/
apiKey: ETHERSCAN_API_KEY,
},
}
Zweryfikuj swój inteligentny kontrakt w Etherscan
Upewnij się, że wszystkie pliki są zapisane, a wszystkie zmienne .env są poprawnie skonfigurowane.
Uruchom zadanie verify, przekazując adres kontraktu i sieć, w której został wdrożony:
npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'
Upewnij się, że DEPLOYED_CONTRACT_ADDRESS to adres Twojego wdrożonego inteligentnego kontraktu w sieci testowej Goerli. Ponadto ostatni argument ('Hello World!') musi być tą samą wartością ciągu znaków, która została użyta podczas kroku wdrażania w części 1.
Jeśli wszystko pójdzie dobrze, w terminalu zobaczysz następującą wiadomość:
Successfully submitted source code for contract
contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
for verification on Etherscan. Waiting for verification result...
Successfully verified contract HelloWorld on Etherscan.
https://goerli.etherscan.io/address/<contract-address>#contracts
Gratulacje! Kod Twojego inteligentnego kontraktu jest w Etherscan!
Sprawdź swój inteligentny kontrakt w Etherscan!
Po przejściu do linku podanego w terminalu powinieneś zobaczyć kod swojego inteligentnego kontraktu i ABI opublikowane w Etherscan!
Hura - udało Ci się, mistrzu! Teraz każdy może wywoływać lub zapisywać dane w Twoim inteligentnym kontrakcie! Nie możemy się doczekać, aby zobaczyć, co zbudujesz w następnej kolejności!
Część 4 – Integracja inteligentnego kontraktu z frontendem
Pod koniec tego samouczka będziesz wiedzieć, jak:
- Podłączyć portfel MetaMask do swojej zdecentralizowanej aplikacji (dapp)
- Odczytywać dane ze swojego inteligentnego kontraktu za pomocą API Alchemy Web3 (opens in a new tab)
- Podpisywać transakcje Ethereum za pomocą MetaMask
W przypadku tej aplikacji dapp użyjemy React (opens in a new tab) jako naszego frameworka frontendowego; warto jednak zauważyć, że nie będziemy poświęcać dużo czasu na omawianie jego podstaw, ponieważ skupimy się głównie na wprowadzeniu funkcjonalności Web3 do naszego projektu.
Wymagane jest podstawowe zrozumienie Reacta. Jeśli go nie masz, zalecamy ukończenie oficjalnego samouczka Wprowadzenie do React (opens in a new tab).
Sklonuj pliki startowe
Najpierw przejdź do repozytorium hello-world-part-four na GitHubie (opens in a new tab), aby pobrać pliki startowe dla tego projektu i sklonuj to repozytorium na swój komputer lokalny.
Otwórz sklonowane repozytorium lokalnie. Zauważ, że zawiera ono dwa foldery: starter-files i completed.
starter-files– będziemy pracować w tym katalogu, połączymy interfejs użytkownika z Twoim portfelem Ethereum i inteligentnym kontraktem, który opublikowaliśmy w Etherscan w Części 3.completedzawiera cały ukończony samouczek i powinien być używany tylko jako punkt odniesienia, jeśli utkniesz.
Następnie otwórz swoją kopię starter-files w ulubionym edytorze kodu, a następnie przejdź do folderu src.
Cały kod, który napiszemy, będzie znajdował się w folderze src. Będziemy edytować komponent HelloWorld.js oraz pliki JavaScript util/interact.js, aby nadać naszemu projektowi funkcjonalność Web3.
Sprawdź pliki startowe
Zanim zaczniemy kodować, sprawdźmy, co zostało nam udostępnione w plikach startowych.
Uruchom swój projekt React
Zacznijmy od uruchomienia projektu React w naszej przeglądarce. Piękno Reacta polega na tym, że gdy już uruchomimy nasz projekt w przeglądarce, wszelkie zapisane przez nas zmiany będą aktualizowane na żywo.
Aby uruchomić projekt, przejdź do katalogu głównego folderu starter-files i uruchom npm install w terminalu, aby zainstalować zależności projektu:
cd starter-files
npm install
Po zakończeniu ich instalacji uruchom npm start w terminalu:
npm start
Spowoduje to otwarcie http://localhost:3000/ (opens in a new tab) w Twojej przeglądarce, gdzie zobaczysz frontend naszego projektu. Powinien on składać się z jednego pola (miejsca do aktualizacji wiadomości przechowywanej w Twoim inteligentnym kontrakcie), przycisku „Connect Wallet” (Połącz portfel) oraz przycisku „Update” (Aktualizuj).
Jeśli spróbujesz kliknąć którykolwiek z przycisków, zauważysz, że nie działają – to dlatego, że wciąż musimy zaprogramować ich funkcjonalność.
Komponent HelloWorld.js
Wróćmy do folderu src w naszym edytorze i otwórzmy plik HelloWorld.js. To bardzo ważne, abyśmy zrozumieli wszystko w tym pliku, ponieważ jest to główny komponent React, nad którym będziemy pracować.
Na górze tego pliku zauważysz kilka instrukcji importu, które są niezbędne do uruchomienia naszego projektu, w tym bibliotekę React, hooki useEffect i useState, niektóre elementy z ./util/interact.js (wkrótce opiszemy je bardziej szczegółowo!) oraz logo Alchemy.
// HelloWorld.js
import React from "react"
import { useEffect, useState } from "react"
import {
helloWorldContract,
connectWallet,
updateMessage,
loadCurrentMessage,
getCurrentWalletConnected,
} from "./util/interact.js"
import alchemylogo from "./alchemylogo.svg"
Następnie mamy nasze zmienne stanu, które będziemy aktualizować po określonych zdarzeniach.
// HelloWorld.js
//Zmienne stanu
const [walletAddress, setWallet] = useState("")
const [status, setStatus] = useState("")
const [message, setMessage] = useState("No connection to the network.")
const [newMessage, setNewMessage] = useState("")
Oto co reprezentuje każda ze zmiennych:
walletAddress– ciąg znaków przechowujący adres portfela użytkownikastatus– ciąg znaków przechowujący pomocną wiadomość, która instruuje użytkownika, jak wchodzić w interakcję z aplikacją dappmessage– ciąg znaków przechowujący bieżącą wiadomość w inteligentnym kontrakcienewMessage– ciąg znaków przechowujący nową wiadomość, która zostanie zapisana w inteligentnym kontrakcie
Po zmiennych stanu zobaczysz pięć niezaimplementowanych funkcji: useEffect, addSmartContractListener, addWalletListener, connectWalletPressed oraz onUpdatePressed. Poniżej wyjaśnimy, co one robią:
// HelloWorld.js
//wywoływane tylko raz
useEffect(async () => {
//TODO: zaimplementuj
}, [])
function addSmartContractListener() {
//TODO: zaimplementuj
}
function addWalletListener() {
//TODO: zaimplementuj
}
const connectWalletPressed = async () => {
//TODO: zaimplementuj
}
const onUpdatePressed = async () => {
//TODO: zaimplementuj
}
useEffect(opens in a new tab) – to hook Reacta, który jest wywoływany po wyrenderowaniu komponentu. Ponieważ przekazano do niego pustą tablicę[](zobacz wiersz 4), zostanie on wywołany tylko przy pierwszym renderowaniu komponentu. W tym miejscu załadujemy bieżącą wiadomość przechowywaną w naszym inteligentnym kontrakcie, wywołamy nasłuchiwacze (listeners) naszego inteligentnego kontraktu i portfela oraz zaktualizujemy nasz interfejs użytkownika, aby odzwierciedlić, czy portfel jest już podłączony.addSmartContractListener– ta funkcja konfiguruje nasłuchiwacz, który będzie obserwował zdarzenieUpdatedMessagesnaszego kontraktu HelloWorld i zaktualizuje nasz interfejs użytkownika, gdy wiadomość w naszym inteligentnym kontrakcie ulegnie zmianie.addWalletListener– ta funkcja konfiguruje nasłuchiwacz, który wykrywa zmiany w stanie portfela MetaMask użytkownika, na przykład gdy użytkownik odłączy swój portfel lub zmieni adresy.connectWalletPressed– ta funkcja zostanie wywołana w celu podłączenia portfela MetaMask użytkownika do naszej aplikacji dapp.onUpdatePressed– ta funkcja zostanie wywołana, gdy użytkownik będzie chciał zaktualizować wiadomość przechowywaną w inteligentnym kontrakcie.
Pod koniec tego pliku znajduje się interfejs użytkownika naszego komponentu.
// HelloWorld.js
//interfejs użytkownika naszego komponentu
return (
<div id="container">
<img id="logo" src={alchemylogo}></img>
<button id="walletButton" onClick={connectWalletPressed}>
{walletAddress.length > 0 ? (
"Connected: " +
String(walletAddress).substring(0, 6) +
"..." +
String(walletAddress).substring(38)
) : (
<span>Connect Wallet</span>
)}
</button>
<h2 style={{ paddingTop: "50px" }}>Current Message:</h2>
<p>{message}</p>
<h2 style={{ paddingTop: "18px" }}>New Message:</h2>
<div>
<input
type="text"
placeholder="Update the message in your smart contract."
onChange={(e) => setNewMessage(e.target.value)}
value={newMessage}
/>
<p id="status">{status}</p>
<button id="publishButton" onClick={onUpdatePressed}>
Update
</button>
</div>
</div>
)
Jeśli uważnie przeanalizujesz ten kod, zauważysz, gdzie używamy naszych różnych zmiennych stanu w interfejsie użytkownika:
- W wierszach 6-12, jeśli portfel użytkownika jest podłączony (tj.
walletAddress.length > 0), wyświetlamy skróconą wersjęwalletAddressużytkownika na przycisku o identyfikatorze „walletButton”; w przeciwnym razie widnieje na nim po prostu napis „Connect Wallet”. - W wierszu 17 wyświetlamy bieżącą wiadomość przechowywaną w inteligentnym kontrakcie, która jest przechwytywana w ciągu znaków
message. - W wierszach 23-26 używamy komponentu kontrolowanego (opens in a new tab), aby zaktualizować naszą zmienną stanu
newMessage, gdy zmienią się dane wejściowe w polu tekstowym.
Oprócz naszych zmiennych stanu zobaczysz również, że funkcje connectWalletPressed i onUpdatePressed są wywoływane po kliknięciu odpowiednio przycisków o identyfikatorach publishButton i walletButton.
Na koniec zajmijmy się tym, gdzie dodawany jest ten komponent HelloWorld.js.
Jeśli przejdziesz do pliku App.js, który jest głównym komponentem w React działającym jako kontener dla wszystkich innych komponentów, zobaczysz, że nasz komponent HelloWorld.js jest wstrzykiwany w wierszu 7.
Ostatnią, ale nie mniej ważną rzeczą jest sprawdzenie jeszcze jednego udostępnionego pliku, pliku interact.js.
Plik interact.js
Ponieważ chcemy trzymać się paradygmatu M-V-C (opens in a new tab), będziemy potrzebować osobnego pliku, który zawiera wszystkie nasze funkcje do zarządzania logiką, danymi i regułami naszej aplikacji dapp, a następnie móc wyeksportować te funkcje do naszego frontendu (naszego komponentu HelloWorld.js).
👆🏽Dokładnie taki jest cel naszego pliku interact.js!
Przejdź do folderu util w katalogu src, a zauważysz, że dołączyliśmy plik o nazwie interact.js, który będzie zawierał wszystkie nasze funkcje i zmienne dotyczące interakcji z inteligentnym kontraktem oraz portfelem.
// interact.js
//export const helloWorldContract;
export const loadCurrentMessage = async () => {}
export const connectWallet = async () => {}
const getCurrentWalletConnected = async () => {}
export const updateMessage = async (message) => {}
Zauważysz na górze pliku, że zakomentowaliśmy obiekt helloWorldContract. W dalszej części tego samouczka odkomentujemy ten obiekt i utworzymy instancję naszego inteligentnego kontraktu w tej zmiennej, którą następnie wyeksportujemy do naszego komponentu HelloWorld.js.
Cztery niezaimplementowane funkcje po naszym obiekcie helloWorldContract wykonują następujące czynności:
loadCurrentMessage– ta funkcja obsługuje logikę ładowania bieżącej wiadomości przechowywanej w inteligentnym kontrakcie. Wykona ona wywołanie odczytu do inteligentnego kontraktu Hello World za pomocą API Alchemy Web3 (opens in a new tab).connectWallet– ta funkcja połączy MetaMask użytkownika z naszą aplikacją dapp.getCurrentWalletConnected– ta funkcja sprawdzi, czy konto Ethereum jest już podłączone do naszej aplikacji dapp podczas ładowania strony i odpowiednio zaktualizuje nasz interfejs użytkownika.updateMessage– ta funkcja zaktualizuje wiadomość przechowywaną w inteligentnym kontrakcie. Wykona ona wywołanie zapisu do inteligentnego kontraktu Hello World, więc portfel MetaMask użytkownika będzie musiał podpisać transakcję Ethereum, aby zaktualizować wiadomość.
Teraz, gdy rozumiemy, z czym pracujemy, dowiedzmy się, jak odczytywać dane z naszego inteligentnego kontraktu!
Krok 3: Odczyt z inteligentnego kontraktu
Aby odczytywać dane ze swojego inteligentnego kontraktu, musisz pomyślnie skonfigurować:
- Połączenie API z łańcuchem Ethereum
- Załadowaną instancję swojego inteligentnego kontraktu
- Funkcję do wywołania funkcji Twojego inteligentnego kontraktu
- Nasłuchiwacz do obserwowania aktualizacji, gdy dane odczytywane z inteligentnego kontraktu ulegną zmianie
Może to brzmieć jak wiele kroków, ale nie martw się! Przeprowadzimy Cię przez każdy z nich krok po kroku! :)
Nawiąż połączenie API z łańcuchem Ethereum
Pamiętasz, jak w Części 2 tego samouczka użyliśmy naszego klucza Alchemy Web3 do odczytu z naszego inteligentnego kontraktu (opens in a new tab)? Będziesz również potrzebować klucza Alchemy Web3 w swojej aplikacji dapp, aby odczytywać dane z łańcucha.
Jeśli jeszcze go nie masz, najpierw zainstaluj Alchemy Web3 (opens in a new tab), przechodząc do katalogu głównego starter-files i uruchamiając następujące polecenie w terminalu:
npm install @alch/alchemy-web3
Alchemy Web3 (opens in a new tab) to wrapper dla Web3.js (opens in a new tab), zapewniający ulepszone metody API i inne kluczowe korzyści, które ułatwią Ci życie jako programiście Web3. Został zaprojektowany tak, aby wymagał minimalnej konfiguracji, dzięki czemu możesz od razu zacząć go używać w swojej aplikacji!
Następnie zainstaluj pakiet dotenv (opens in a new tab) w katalogu swojego projektu, abyśmy mieli bezpieczne miejsce do przechowywania naszego klucza API po jego pobraniu.
npm install dotenv --save
W naszej aplikacji dapp będziemy używać naszego klucza API Websockets zamiast klucza API HTTP, ponieważ pozwoli nam to skonfigurować nasłuchiwacz, który wykrywa, kiedy wiadomość przechowywana w inteligentnym kontrakcie ulegnie zmianie.
Gdy masz już swój klucz API, utwórz plik .env w katalogu głównym i dodaj do niego swój adres URL Alchemy Websockets. Następnie Twój plik .env powinien wyglądać tak:
REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<klucz>
Teraz jesteśmy gotowi do skonfigurowania naszego punktu końcowego Alchemy Web3 w naszej aplikacji dapp! Wróćmy do naszego pliku interact.js, który jest zagnieżdżony w folderze util i dodajmy następujący kod na górze pliku:
// interact.js
require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)
//export const helloWorldContract;
Powyżej najpierw zaimportowaliśmy klucz Alchemy z naszego pliku .env, a następnie przekazaliśmy nasz alchemyKey do createAlchemyWeb3, aby ustanowić nasz punkt końcowy Alchemy Web3.
Mając gotowy ten punkt końcowy, nadszedł czas na załadowanie naszego inteligentnego kontraktu!
Ładowanie inteligentnego kontraktu Hello World
Aby załadować swój inteligentny kontrakt Hello World, będziesz potrzebować jego adresu kontraktu i ABI, z których oba można znaleźć w Etherscan, jeśli ukończyłeś Część 3 tego samouczka.
Jak uzyskać ABI kontraktu z Etherscan
Jeśli pominąłeś Część 3 tego samouczka, możesz użyć kontraktu HelloWorld z adresem 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). Jego ABI można znaleźć tutaj (opens in a new tab).
ABI kontraktu jest niezbędne do określenia, którą funkcję wywoła kontrakt, a także do upewnienia się, że funkcja zwróci dane w oczekiwanym formacie. Po skopiowaniu naszego ABI kontraktu zapiszmy je jako plik JSON o nazwie contract-abi.json w Twoim katalogu src.
Twój plik contract-abi.json powinien być przechowywany w folderze src.
Uzbrojeni w nasz adres kontraktu, ABI i punkt końcowy Alchemy Web3, możemy użyć metody contract (opens in a new tab), aby załadować instancję naszego inteligentnego kontraktu. Zaimportuj swoje ABI kontraktu do pliku interact.js i dodaj swój adres kontraktu.
// interact.js
const contractABI = require("../contract-abi.json")
const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
Możemy teraz w końcu odkomentować naszą zmienną helloWorldContract i załadować inteligentny kontrakt za pomocą naszego punktu końcowego AlchemyWeb3:
// interact.js
export const helloWorldContract = new web3.eth.Contract(
contractABI,
contractAddress
)
Podsumowując, pierwsze 12 wierszy Twojego pliku interact.js powinno teraz wyglądać tak:
// interact.js
require("dotenv").config()
const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
const web3 = createAlchemyWeb3(alchemyKey)
const contractABI = require("../contract-abi.json")
const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
export const helloWorldContract = new web3.eth.Contract(
contractABI,
contractAddress
)
Teraz, gdy mamy załadowany nasz kontrakt, możemy zaimplementować naszą funkcję loadCurrentMessage!
Implementacja loadCurrentMessage w pliku interact.js
Ta funkcja jest bardzo prosta. Wykonamy proste asynchroniczne wywołanie web3, aby odczytać dane z naszego kontraktu. Nasza funkcja zwróci wiadomość przechowywaną w inteligentnym kontrakcie:
Zaktualizuj loadCurrentMessage w swoim pliku interact.js na następujący kod:
// interact.js
export const loadCurrentMessage = async () => {
const message = await helloWorldContract.methods.message().call()
return message
}
Ponieważ chcemy wyświetlić ten inteligentny kontrakt w naszym interfejsie użytkownika, zaktualizujmy funkcję useEffect w naszym komponencie HelloWorld.js na następującą:
// HelloWorld.js
//wywoływane tylko raz
useEffect(async () => {
const message = await loadCurrentMessage()
setMessage(message)
}, [])
Zauważ, że chcemy, aby nasza funkcja loadCurrentMessage została wywołana tylko raz podczas pierwszego renderowania komponentu. Wkrótce zaimplementujemy addSmartContractListener, aby automatycznie aktualizować interfejs użytkownika po zmianie wiadomości w inteligentnym kontrakcie.
Zanim zagłębimy się w nasz nasłuchiwacz, sprawdźmy, co mamy do tej pory! Zapisz swoje pliki HelloWorld.js i interact.js, a następnie przejdź do http://localhost:3000/ (opens in a new tab)
Zauważysz, że bieżąca wiadomość nie mówi już „Brak połączenia z siecią”. Zamiast tego odzwierciedla wiadomość przechowywaną w inteligentnym kontrakcie. Super!
Twój interfejs użytkownika powinien teraz odzwierciedlać wiadomość przechowywaną w inteligentnym kontrakcie
A skoro mowa o tym nasłuchiwaczu...
Zaimplementuj addSmartContractListener
Jeśli przypomnisz sobie plik HelloWorld.sol, który napisaliśmy w Części 1 tej serii samouczków (opens in a new tab), przypomnisz sobie, że istnieje zdarzenie inteligentnego kontraktu o nazwie UpdatedMessages, które jest emitowane po wywołaniu funkcji update naszego inteligentnego kontraktu (zobacz wiersze 9 i 27):
// HelloWorld.sol
// Określa wersję Solidity, używając wersjonowania semantycznego.
// Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
pragma solidity ^0.7.3;
// Definiuje kontrakt o nazwie `HelloWorld`.
// Kontrakt to zbiór funkcji i danych (jego stan). Po wdrożeniu, kontrakt znajduje się pod określonym adresem na blockchainie Ethereum. Dowiedz się więcej: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
contract HelloWorld {
//Emitowane, gdy wywoływana jest funkcja aktualizacji
//Zdarzenia inteligentnego kontraktu to sposób, w jaki twój kontrakt komunikuje, że coś wydarzyło się na blockchainie do front-endu twojej aplikacji, który może 'nasłuchiwać' określonych zdarzeń i podejmować działania, gdy one wystąpią.
event UpdatedMessages(string oldStr, string newStr);
// Deklaruje zmienną stanu `message` typu `string`.
// Zmienne stanu to zmienne, których wartości są trwale przechowywane w pamięci kontraktu. Słowo kluczowe `public` sprawia, że zmienne są dostępne z zewnątrz kontraktu i tworzy funkcję, którą inne kontrakty lub klienci mogą wywołać, aby uzyskać dostęp do wartości.
string public message;
// Podobnie jak w wielu językach obiektowych opartych na klasach, konstruktor to specjalna funkcja, która jest wykonywana tylko podczas tworzenia kontraktu.
// Konstruktory służą do inicjowania danych kontraktu. Dowiedz się więcej:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
constructor(string memory initMessage) {
// Przyjmuje argument typu string `initMessage` i ustawia wartość w zmiennej przechowywania `message` kontraktu).
message = initMessage;
}
// Publiczna funkcja, która przyjmuje argument typu string i aktualizuje zmienną przechowywania `message`.
function update(string memory newMessage) public {
string memory oldMsg = message;
message = newMessage;
emit UpdatedMessages(oldMsg, newMessage);
}
}
Zdarzenia inteligentnego kontraktu to sposób, w jaki Twój kontrakt komunikuje Twojej aplikacji frontendowej, że coś się wydarzyło (tj. miało miejsce zdarzenie) na blockchainie. Aplikacja może „nasłuchiwać” określonych zdarzeń i podejmować działania, gdy one wystąpią.
Funkcja addSmartContractListener będzie w szczególności nasłuchiwać zdarzenia UpdatedMessages naszego inteligentnego kontraktu Hello World i aktualizować nasz interfejs użytkownika, aby wyświetlić nową wiadomość.
Zmodyfikuj addSmartContractListener na następujący kod:
// HelloWorld.js
function addSmartContractListener() {
helloWorldContract.events.UpdatedMessages({}, (error, data) => {
if (error) {
setStatus("😥 " + error.message)
} else {
setMessage(data.returnValues[1])
setNewMessage("")
setStatus("🎉 Your message has been updated!")
}
})
}
Przeanalizujmy, co się dzieje, gdy nasłuchiwacz wykryje zdarzenie:
- Jeśli podczas emitowania zdarzenia wystąpi błąd, zostanie to odzwierciedlone w interfejsie użytkownika za pośrednictwem naszej zmiennej stanu
status. - W przeciwnym razie użyjemy zwróconego obiektu
data.data.returnValuesto tablica indeksowana od zera, w której pierwszy element przechowuje poprzednią wiadomość, a drugi element przechowuje zaktualizowaną. Podsumowując, w przypadku pomyślnego zdarzenia ustawimy nasz ciąg znakówmessagena zaktualizowaną wiadomość, wyczyścimy ciąg znakównewMessagei zaktualizujemy naszą zmienną stanustatus, aby odzwierciedlić, że nowa wiadomość została opublikowana w naszym inteligentnym kontrakcie.
Na koniec wywołajmy nasz nasłuchiwacz w naszej funkcji useEffect, aby został zainicjowany podczas pierwszego renderowania komponent HelloWorld.js. Podsumowując, Twoja funkcja useEffect powinna wyglądać tak:
// HelloWorld.js
useEffect(async () => {
const message = await loadCurrentMessage()
setMessage(message)
addSmartContractListener()
}, [])
Teraz, gdy jesteśmy w stanie odczytywać dane z naszego inteligentnego kontraktu, byłoby wspaniale dowiedzieć się, jak również do niego zapisywać! Jednak aby zapisywać w naszej aplikacji dapp, musimy najpierw mieć podłączony do niej portfel Ethereum.
Więc w następnej kolejności zajmiemy się konfiguracją naszego portfela Ethereum (MetaMask), a następnie podłączeniem go do naszej aplikacji dapp!
Krok 4: Skonfiguruj swój portfel Ethereum
Aby zapisać cokolwiek w łańcuchu Ethereum, użytkownicy muszą podpisywać transakcje za pomocą kluczy prywatnych swojego wirtualnego portfela. W tym samouczku użyjemy MetaMask (opens in a new tab), wirtualnego portfela w przeglądarce używanego do zarządzania adresem konta Ethereum, ponieważ sprawia on, że podpisywanie transakcji jest bardzo łatwe dla użytkownika końcowego.
Jeśli chcesz dowiedzieć się więcej o tym, jak działają transakcje w Ethereum, sprawdź tę stronę od Ethereum Foundation.
Pobierz MetaMask
Możesz pobrać i utworzyć konto MetaMask za darmo tutaj (opens in a new tab). Podczas tworzenia konta lub jeśli już je masz, upewnij się, że przełączyłeś się na „Goerli Test Network” w prawym górnym rogu (abyśmy nie operowali prawdziwymi pieniędzmi).
Dodaj ether z kranika
Aby podpisać transakcję na blockchainie Ethereum, będziemy potrzebować trochę fałszywego ETH. Aby zdobyć ETH, możesz przejść do FaucETH (opens in a new tab) i wprowadzić adres swojego konta Goerli, kliknąć „Request funds” (Poproś o środki), następnie wybrać „Ethereum Testnet Goerli” z listy rozwijanej i na koniec ponownie kliknąć przycisk „Request funds”. Wkrótce potem powinieneś zobaczyć ETH na swoim koncie MetaMask!
Sprawdź swoje saldo
Aby upewnić się, że nasze saldo tam jest, wykonajmy żądanie eth_getBalance (opens in a new tab) za pomocą narzędzia composer od Alchemy (opens in a new tab). Zwróci to ilość ETH w naszym portfelu. Po wprowadzeniu adresu konta MetaMask i kliknięciu „Send Request” (Wyślij żądanie), powinieneś zobaczyć odpowiedź podobną do tej:
{"jsonrpc": "2.0", "id": 0, "result": "0xde0b6b3a7640000"}
UWAGA: Ten wynik jest w wei, a nie w ETH. Wei jest używane jako najmniejszy nominał etheru. Przelicznik z wei na ETH to: 1 ETH = 10¹⁸ wei. Więc jeśli przekonwertujemy 0xde0b6b3a7640000 na system dziesiętny, otrzymamy 1*10¹⁸, co równa się 1 ETH.
Uff! Nasze fałszywe pieniądze są na miejscu! 🤑
Krok 5: Podłącz MetaMask do swojego interfejsu użytkownika
Teraz, gdy nasz portfel MetaMask jest skonfigurowany, podłączmy do niego naszą aplikację dapp!
Funkcja connectWallet
W naszym pliku interact.js zaimplementujmy funkcję connectWallet, którą następnie będziemy mogli wywołać w naszym komponencie HelloWorld.js.
Zmodyfikujmy connectWallet na następujący kod:
// interact.js
export const connectWallet = async () => {
if (window.ethereum) {
try {
const addressArray = await window.ethereum.request({
method: "eth_requestAccounts",
})
const obj = {
status: "👆🏽 Write a message in the text-field above.",
address: addressArray[0],
}
return obj
} catch (err) {
return {
address: "",
status: "😥 " + err.message,
}
}
} else {
return {
address: "",
status: (
<span>
<p>
{" "}
🦊 <a target="_blank" href={`https://metamask.io/download`}>
You must install MetaMask, a virtual Ethereum wallet, in your
browser.
</a>
</p>
</span>
),
}
}
}
Więc co dokładnie robi ten gigantyczny blok kodu?
Cóż, po pierwsze, sprawdza, czy window.ethereum jest włączone w Twojej przeglądarce.
window.ethereum to globalne API wstrzykiwane przez MetaMask i innych dostawców portfeli, które pozwala stronom internetowym na żądanie kont Ethereum użytkowników. W przypadku zatwierdzenia może odczytywać dane z blockchainów, do których podłączony jest użytkownik, i sugerować użytkownikowi podpisywanie wiadomości oraz transakcji. Sprawdź dokumentację MetaMask (opens in a new tab), aby uzyskać więcej informacji!
Jeśli window.ethereum nie jest obecne, oznacza to, że MetaMask nie jest zainstalowany. Skutkuje to zwróceniem obiektu JSON, w którym zwrócony address jest pustym ciągiem znaków, a obiekt JSX status przekazuje informację, że użytkownik musi zainstalować MetaMask.
Teraz, jeśli window.ethereum jest obecne, wtedy robi się ciekawie.
Używając bloku try/catch, spróbujemy połączyć się z MetaMask, wywołując window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Wywołanie tej funkcji otworzy MetaMask w przeglądarce, po czym użytkownik zostanie poproszony o podłączenie swojego portfela do Twojej aplikacji dapp.
- Jeśli użytkownik zdecyduje się połączyć,
method: "eth_requestAccounts"zwróci tablicę zawierającą wszystkie adresy kont użytkownika, które połączyły się z aplikacją dapp. Podsumowując, nasza funkcjaconnectWalletzwróci obiekt JSON, który zawiera pierwszyaddressw tej tablicy (zobacz wiersz 9) oraz wiadomośćstatus, która zachęca użytkownika do zapisania wiadomości w inteligentnym kontrakcie. - Jeśli użytkownik odrzuci połączenie, obiekt JSON będzie zawierał pusty ciąg znaków dla zwróconego
addressoraz wiadomośćstatus, która odzwierciedla, że użytkownik odrzucił połączenie.
Teraz, gdy napisaliśmy tę funkcję connectWallet, następnym krokiem jest wywołanie jej w naszym komponencie HelloWorld.js.
Dodaj funkcję connectWallet do swojego komponentu interfejsu użytkownika HelloWorld.js
Przejdź do funkcji connectWalletPressed w HelloWorld.js i zaktualizuj ją na następującą:
// HelloWorld.js
const connectWalletPressed = async () => {
const walletResponse = await connectWallet()
setStatus(walletResponse.status)
setWallet(walletResponse.address)
}
Zauważasz, jak większość naszej funkcjonalności jest wyabstrahowana z naszego komponentu HelloWorld.js do pliku interact.js? Dzieje się tak, abyśmy byli zgodni z paradygmatem M-V-C!
W connectWalletPressed po prostu wykonujemy wywołanie await do naszej zaimportowanej funkcji connectWallet, a korzystając z jej odpowiedzi, aktualizujemy nasze zmienne status i walletAddress za pośrednictwem ich hooków stanu.
Teraz zapiszmy oba pliki (HelloWorld.js i interact.js) i przetestujmy nasz dotychczasowy interfejs użytkownika.
Otwórz przeglądarkę na stronie http://localhost:3000/ (opens in a new tab) i naciśnij przycisk „Connect Wallet” w prawym górnym rogu strony.
Jeśli masz zainstalowany MetaMask, powinieneś zostać poproszony o podłączenie swojego portfela do aplikacji dapp. Zaakceptuj zaproszenie do połączenia.
Powinieneś zobaczyć, że przycisk portfela odzwierciedla teraz, że Twój adres jest podłączony! Taaak 🔥
Następnie spróbuj odświeżyć stronę... to dziwne. Nasz przycisk portfela prosi nas o podłączenie MetaMask, mimo że jest już podłączony...
Jednak nie obawiaj się! Możemy łatwo to rozwiązać, implementując getCurrentWalletConnected, co sprawdzi, czy adres jest już podłączony do naszej aplikacji dapp i odpowiednio zaktualizuje nasz interfejs użytkownika!
Funkcja getCurrentWalletConnected
Zaktualizuj swoją funkcję getCurrentWalletConnected w pliku interact.js na następującą:
// interact.js
export const getCurrentWalletConnected = async () => {
if (window.ethereum) {
try {
const addressArray = await window.ethereum.request({
method: "eth_accounts",
})
if (addressArray.length > 0) {
return {
address: addressArray[0],
status: "👆🏽 Write a message in the text-field above.",
}
} else {
return {
address: "",
status: "🦊 Connect to MetaMask using the top right button.",
}
}
} catch (err) {
return {
address: "",
status: "😥 " + err.message,
}
}
} else {
return {
address: "",
status: (
<span>
<p>
{" "}
🦊 <a target="_blank" href={`https://metamask.io/download`}>
You must install MetaMask, a virtual Ethereum wallet, in your
browser.
</a>
</p>
</span>
),
}
}
}
Ten kod jest bardzo podobny do funkcji connectWallet, którą właśnie napisaliśmy w poprzednim kroku.
Główna różnica polega na tym, że zamiast wywoływać metodę eth_requestAccounts, która otwiera MetaMask, aby użytkownik mógł podłączyć swój portfel, tutaj wywołujemy metodę eth_accounts, która po prostu zwraca tablicę zawierającą adresy MetaMask aktualnie podłączone do naszej aplikacji dapp.
Aby zobaczyć tę funkcję w akcji, wywołajmy ją w naszej funkcji useEffect naszego komponentu HelloWorld.js:
// HelloWorld.js
useEffect(async () => {
const message = await loadCurrentMessage()
setMessage(message)
addSmartContractListener()
const { address, status } = await getCurrentWalletConnected()
setWallet(address)
setStatus(status)
}, [])
Zauważ, że używamy odpowiedzi z naszego wywołania getCurrentWalletConnected, aby zaktualizować nasze zmienne stanu walletAddress i status.
Teraz, gdy dodałeś ten kod, spróbujmy odświeżyć okno naszej przeglądarki.
Świetnie! Przycisk powinien informować, że jesteś podłączony, i pokazywać podgląd adresu Twojego podłączonego portfela – nawet po odświeżeniu!
Zaimplementuj addWalletListener
Ostatnim krokiem w konfiguracji portfela naszej aplikacji dapp jest zaimplementowanie nasłuchiwacza portfela, aby nasz interfejs użytkownika aktualizował się, gdy stan naszego portfela ulegnie zmianie, na przykład gdy użytkownik się odłączy lub zmieni konta.
W swoim pliku HelloWorld.js zmodyfikuj funkcję addWalletListener w następujący sposób:
// HelloWorld.js
function addWalletListener() {
if (window.ethereum) {
window.ethereum.on("accountsChanged", (accounts) => {
if (accounts.length > 0) {
setWallet(accounts[0])
setStatus("👆🏽 Write a message in the text-field above.")
} else {
setWallet("")
setStatus("🦊 Connect to MetaMask using the top right button.")
}
})
} else {
setStatus(
<p>
{" "}
🦊 <a target="_blank" href={`https://metamask.io/download`}>
You must install MetaMask, a virtual Ethereum wallet, in your browser.
</a>
</p>
)
}
}
Założę się, że na tym etapie nie potrzebujesz nawet naszej pomocy, aby zrozumieć, co się tutaj dzieje, ale dla pewności szybko to przeanalizujmy:
- Po pierwsze, nasza funkcja sprawdza, czy
window.ethereumjest włączone (tj. czy MetaMask jest zainstalowany).- Jeśli nie jest, po prostu ustawiamy naszą zmienną stanu
statusna ciąg znaków JSX, który zachęca użytkownika do zainstalowania MetaMask. - Jeśli jest włączone, konfigurujemy nasłuchiwacz
window.ethereum.on("accountsChanged")w wierszu 3, który nasłuchuje zmian stanu w portfelu MetaMask, co obejmuje sytuacje, gdy użytkownik podłącza dodatkowe konto do aplikacji dapp, zmienia konta lub odłącza konto. Jeśli podłączone jest co najmniej jedno konto, zmienna stanuwalletAddressjest aktualizowana jako pierwsze konto w tablicyaccountszwróconej przez nasłuchiwacz. W przeciwnym raziewalletAddressjest ustawiane jako pusty ciąg znaków.
- Jeśli nie jest, po prostu ustawiamy naszą zmienną stanu
Ostatnią, ale nie mniej ważną rzeczą jest wywołanie jej w naszej funkcji useEffect:
// HelloWorld.js
useEffect(async () => {
const message = await loadCurrentMessage()
setMessage(message)
addSmartContractListener()
const { address, status } = await getCurrentWalletConnected()
setWallet(address)
setStatus(status)
addWalletListener()
}, [])
I to wszystko! Pomyślnie zakończyliśmy programowanie całej funkcjonalności naszego portfela! Teraz przejdźmy do naszego ostatniego zadania: aktualizacji wiadomości przechowywanej w naszym inteligentnym kontrakcie!
Krok 6: Zaimplementuj funkcję updateMessage
Dobra ekipa, dotarliśmy na ostatnią prostą! W updateMessage Twojego pliku interact.js zrobimy następujące rzeczy:
- Upewnimy się, że wiadomość, którą chcemy opublikować w naszym inteligentnym kontrakcie, jest prawidłowa
- Podpiszemy naszą transakcję za pomocą MetaMask
- Wywołamy tę funkcję z naszego komponentu frontendowego
HelloWorld.js
Nie potrwa to długo; dokończmy tę aplikację dapp!
Obsługa błędów wejściowych
Naturalnie, sensowne jest posiadanie jakiegoś rodzaju obsługi błędów wejściowych na początku funkcji.
Będziemy chcieli, aby nasza funkcja zwróciła wartość wcześniej, jeśli nie ma zainstalowanego rozszerzenia MetaMask, nie ma podłączonego portfela (tj. przekazany address jest pustym ciągiem znaków) lub message jest pustym ciągiem znaków. Dodajmy następującą obsługę błędów do updateMessage:
// interact.js
export const updateMessage = async (address, message) => {
if (!window.ethereum || address === null) {
return {
status:
"💡 Connect your MetaMask wallet to update the message on the blockchain.",
}
}
if (message.trim() === "") {
return {
status: "❌ Your message cannot be an empty string.",
}
}
}
Teraz, gdy ma odpowiednią obsługę błędów wejściowych, nadszedł czas na podpisanie transakcji za pośrednictwem MetaMask!
Podpisywanie naszej transakcji
Jeśli już czujesz się komfortowo z tradycyjnymi transakcjami Ethereum w Web3, kod, który napiszemy w następnej kolejności, będzie bardzo znajomy. Poniżej kodu obsługi błędów wejściowych dodaj następujący kod do updateMessage:
// interact.js
//ustaw parametry transakcji
const transactionParameters = {
to: contractAddress, // Wymagane z wyjątkiem publikacji kontraktu.
from: address, // musi pasować do aktywnego adresu użytkownika.
data: helloWorldContract.methods.update(message).encodeABI(),
}
//podpisz transakcję
try {
const txHash = await window.ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
})
return {
status: (
<span>
✅{" "}
<a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
View the status of your transaction on Etherscan!
</a>
<br />
ℹ️ Once the transaction is verified by the network, the message will be
updated automatically.
</span>
),
}
} catch (error) {
return {
status: "😥 " + error.message,
}
}
Przeanalizujmy, co się dzieje. Najpierw konfigurujemy parametry naszej transakcji, gdzie:
tookreśla adres odbiorcy (nasz inteligentny kontrakt)fromokreśla osobę podpisującą transakcję, zmiennąaddress, którą przekazaliśmy do naszej funkcjidatazawiera wywołanie metodyupdatenaszego inteligentnego kontraktu Hello World, otrzymując naszą zmienną znakowąmessagejako dane wejściowe
Następnie wykonujemy wywołanie await, window.ethereum.request, w którym prosimy MetaMask o podpisanie transakcji. Zauważ, że w wierszach 11 i 12 określamy naszą metodę eth, eth_sendTransaction i przekazujemy nasze transactionParameters.
W tym momencie MetaMask otworzy się w przeglądarce i poprosi użytkownika o podpisanie lub odrzucenie transakcji.
- Jeśli transakcja się powiedzie, funkcja zwróci obiekt JSON, w którym ciąg znaków JSX
statuszachęca użytkownika do sprawdzenia Etherscan w celu uzyskania dodatkowych informacji o jego transakcji. - Jeśli transakcja się nie powiedzie, funkcja zwróci obiekt JSON, w którym ciąg znaków
statusprzekazuje komunikat o błędzie.
Podsumowując, nasza funkcja updateMessage powinna wyglądać tak:
// interact.js
export const updateMessage = async (address, message) => {
//obsługa błędów wejścia
if (!window.ethereum || address === null) {
return {
status:
"💡 Connect your MetaMask wallet to update the message on the blockchain.",
}
}
if (message.trim() === "") {
return {
status: "❌ Your message cannot be an empty string.",
}
}
//ustaw parametry transakcji
const transactionParameters = {
to: contractAddress, // Wymagane z wyjątkiem publikacji kontraktu.
from: address, // musi pasować do aktywnego adresu użytkownika.
data: helloWorldContract.methods.update(message).encodeABI(),
}
//podpisz transakcję
try {
const txHash = await window.ethereum.request({
method: "eth_sendTransaction",
params: [transactionParameters],
})
return {
status: (
<span>
✅{" "}
<a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
View the status of your transaction on Etherscan!
</a>
<br />
ℹ️ Once the transaction is verified by the network, the message will
be updated automatically.
</span>
),
}
} catch (error) {
return {
status: "😥 " + error.message,
}
}
}
Ostatnią, ale nie mniej ważną rzeczą jest podłączenie naszej funkcji updateMessage do naszego komponentu HelloWorld.js.
Podłącz updateMessage do frontendu HelloWorld.js
Nasza funkcja onUpdatePressed powinna wykonać wywołanie await do zaimportowanej funkcji updateMessage i zmodyfikować zmienną stanu status, aby odzwierciedlić, czy nasza transakcja się powiodła, czy nie:
// HelloWorld.js
const onUpdatePressed = async () => {
const { status } = await updateMessage(walletAddress, newMessage)
setStatus(status)
}
To bardzo przejrzyste i proste. I zgadnij co... TWOJA APLIKACJA DAPP JEST GOTOWA!!!
Śmiało, przetestuj przycisk Update (Aktualizuj)!
Stwórz własną niestandardową aplikację dapp
Hura, dotarłeś do końca samouczka! Podsumowując, nauczyłeś się, jak:
- Podłączyć portfel MetaMask do swojego projektu aplikacji dapp
- Odczytywać dane ze swojego inteligentnego kontraktu za pomocą API Alchemy Web3 (opens in a new tab)
- Podpisywać transakcje Ethereum za pomocą MetaMask
Teraz jesteś w pełni wyposażony, aby zastosować umiejętności z tego samouczka do zbudowania własnego niestandardowego projektu aplikacji dapp! Jak zawsze, jeśli masz jakiekolwiek pytania, nie wahaj się skontaktować z nami w celu uzyskania pomocy na Discordzie Alchemy (opens in a new tab). 🧙♂️
Po ukończeniu tego samouczka daj nam znać, jakie były Twoje wrażenia lub czy masz jakieś uwagi, oznaczając nas na Twitterze @alchemyplatform (opens in a new tab)!





