Lompat ke konten utama

Kontrak Pintar Hello World untuk Pemula - Fullstack

Solidity
Hardhat
Alchemy
kontrak pintar
menerapkan
penjelajah blok
frontend
transaksi
kerangka kerja
Pemula
nstrike2
25 Oktober 2021
44 menit baca

Panduan ini ditujukan untuk Anda jika Anda baru dalam pengembangan blockchain dan tidak tahu harus mulai dari mana atau bagaimana cara menerapkan dan berinteraksi dengan kontrak pintar. Kita akan membahas pembuatan dan penerapan kontrak pintar sederhana di testnet Goerli menggunakan MetaMask (opens in a new tab), Solidity (opens in a new tab), Hardhat (opens in a new tab), dan Alchemy (opens in a new tab).

Anda akan membutuhkan akun Alchemy untuk menyelesaikan tutorial ini. Daftar untuk mendapatkan akun gratis (opens in a new tab).

Jika Anda memiliki pertanyaan kapan saja, jangan ragu untuk menghubungi kami di Discord Alchemy (opens in a new tab)!

Bagian 1 - Membuat dan Menerapkan Kontrak Pintar Anda menggunakan Hardhat

Terhubung ke jaringan Ethereum

Ada banyak cara untuk membuat permintaan ke rantai Ethereum. Untuk mempermudah, kita akan menggunakan akun gratis di Alchemy, sebuah platform pengembang blockchain dan API yang memungkinkan kita untuk berkomunikasi dengan rantai Ethereum tanpa menjalankan node sendiri. Alchemy juga memiliki alat pengembang untuk pemantauan dan analitik; kita akan memanfaatkan ini dalam tutorial ini untuk memahami apa yang terjadi di balik layar dalam penerapan kontrak pintar kita.

Buat aplikasi dan kunci API Anda

Setelah Anda membuat akun Alchemy, Anda dapat menghasilkan kunci API dengan membuat aplikasi. Ini akan memungkinkan Anda untuk membuat permintaan ke testnet Goerli. Jika Anda tidak terbiasa dengan testnet, Anda dapat membaca panduan Alchemy untuk memilih jaringan (opens in a new tab).

Di dasbor Alchemy, temukan menu tarik-turun Apps di bilah navigasi dan klik Create App.

Hello world create app

Beri nama aplikasi Anda 'Hello World' dan tulis deskripsi singkat. Pilih Staging sebagai lingkungan Anda dan Goerli sebagai jaringan Anda.

create app view hello world

Catatan: pastikan untuk memilih Goerli, atau tutorial ini tidak akan berfungsi.

Klik Create app. Aplikasi Anda akan muncul di tabel di bawah ini.

Buat akun Ethereum

Anda memerlukan akun Ethereum untuk mengirim dan menerima transaksi. Kita akan menggunakan MetaMask, dompet virtual di peramban yang memungkinkan pengguna mengelola alamat akun Ethereum mereka.

Anda dapat mengunduh dan membuat akun MetaMask secara gratis di sini (opens in a new tab). Saat Anda membuat akun, atau jika Anda sudah memiliki akun, pastikan untuk beralih ke "Goerli Test Network" di kanan atas (sehingga kita tidak berurusan dengan uang sungguhan).

Langkah 4: Tambahkan ether dari Faucet

Untuk menerapkan kontrak pintar Anda ke jaringan pengujian (testnet), Anda akan memerlukan beberapa ETH palsu. Untuk mendapatkan ETH di jaringan Goerli, buka faucet Goerli dan masukkan alamat akun Goerli Anda. Perhatikan bahwa faucet Goerli akhir-akhir ini bisa sedikit tidak dapat diandalkan - lihat halaman jaringan pengujian untuk daftar opsi yang dapat dicoba:

Catatan: karena kepadatan jaringan, ini mungkin memakan waktu beberapa saat. ``

Langkah 5: Periksa Saldo Anda

Untuk memeriksa kembali apakah ETH ada di dompet Anda, mari buat permintaan eth_getBalance (opens in a new tab) menggunakan alat komposer Alchemy (opens in a new tab). Ini akan mengembalikan jumlah ETH di dompet kita. Untuk mempelajari lebih lanjut, lihat tutorial singkat Alchemy tentang cara menggunakan alat komposer (opens in a new tab).

Masukkan alamat akun MetaMask Anda dan klik Send Request. Anda akan melihat respons yang terlihat seperti cuplikan kode di bawah ini.

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

Catatan: Hasil ini dalam wei, bukan ETH. Wei digunakan sebagai denominasi terkecil dari ether.

Fiuh! Uang palsu kita semuanya ada di sana.

Langkah 6: Inisialisasi proyek kita

Pertama, kita perlu membuat folder untuk proyek kita. Navigasikan ke baris perintah Anda dan masukkan yang berikut ini.

1mkdir hello-world
2cd hello-world

Sekarang kita berada di dalam folder proyek kita, kita akan menggunakan npm init untuk menginisialisasi proyek.

Jika Anda belum menginstal npm, ikuti instruksi ini untuk menginstal Node.js dan npm (opens in a new tab).

Untuk tujuan tutorial ini, tidak masalah bagaimana Anda menjawab pertanyaan inisialisasi. Berikut adalah cara kami melakukannya sebagai referensi:

1package name: (hello-world)
2version: (1.0.0)
3description: hello world smart contract
4entry point: (index.js)
5test command:
6git repository:
7keywords:
8author:
9license: (ISC)
10
11About to write to /Users/.../.../.../hello-world/package.json:
12
13{
14 "name": "hello-world",
15 "version": "1.0.0",
16 "description": "hello world smart contract",
17 "main": "index.js",
18 "scripts": {
19 "test": "echo \"Error: no test specified\" && exit 1"
20 },
21 "author": "",
22 "license": "ISC"
23}
Tampilkan semua

Setujui package.json dan kita siap untuk melanjutkan!

Langkah 7: Unduh Hardhat

Hardhat adalah lingkungan pengembangan untuk mengompilasi, menerapkan, menguji, dan men-debug perangkat lunak Ethereum Anda. Ini membantu pengembang saat membangun kontrak pintar dan dapps secara lokal sebelum menerapkannya ke rantai langsung.

Di dalam proyek hello-world kita, jalankan:

1npm install --save-dev hardhat

Lihat halaman ini untuk detail lebih lanjut tentang instruksi instalasi (opens in a new tab).

Langkah 8: Buat proyek Hardhat

Di dalam folder proyek hello-world kita, jalankan:

1npx hardhat

Anda kemudian akan melihat pesan selamat datang dan opsi untuk memilih apa yang ingin Anda lakukan. Pilih "create an empty hardhat.config.js":

1888 888 888 888 888
2888 888 888 888 888
3888 888 888 888 888
48888888888 8888b. 888d888 .d88888 88888b. 8888b. 888888
5888 888 "88b 888P" d88" 888 888 "88b "88b 888
6888 888 .d888888 888 888 888 888 888 .d888888 888
7888 888 888 888 888 Y88b 888 888 888 888 888 Y88b.
8888 888 "Y888888 888 "Y88888 888 888 "Y888888 "Y888
9
10👷 Welcome to Hardhat v2.0.11 👷‍
11
12What do you want to do? …
13Create a sample project
14❯ Create an empty hardhat.config.js
15Quit
Tampilkan semua

Ini akan menghasilkan file hardhat.config.js di dalam proyek. Kita akan menggunakan ini nanti dalam tutorial untuk menentukan pengaturan proyek kita.

Langkah 9: Tambahkan folder proyek

Untuk menjaga proyek tetap teratur, mari buat dua folder baru. Di baris perintah, navigasikan ke direktori root proyek hello-world Anda dan ketik:

1mkdir contracts
2mkdir scripts
  • contracts/ adalah tempat kita akan menyimpan file kode kontrak pintar hello world kita
  • scripts/ adalah tempat kita akan menyimpan skrip untuk menerapkan dan berinteraksi dengan kontrak kita

Langkah 10: Tulis kontrak kita

Anda mungkin bertanya pada diri sendiri, kapan kita akan menulis kode? Sekarang saatnya!

Buka proyek hello-world di editor favorit Anda. Kontrak pintar paling sering ditulis dalam Solidity, yang akan kita gunakan untuk menulis kontrak pintar kita.‌

  1. Navigasikan ke folder contracts dan buat file baru bernama HelloWorld.sol
  2. Di bawah ini adalah contoh kontrak pintar Hello World yang akan kita gunakan untuk tutorial ini. Salin konten di bawah ini ke dalam file HelloWorld.sol.

Catatan: Pastikan untuk membaca komentar untuk memahami apa yang dilakukan kontrak ini.

1// Specifies the version of Solidity, using semantic versioning.
2// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
3pragma solidity >=0.7.3;
4
5// Defines a contract named `HelloWorld`.
6// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
7contract HelloWorld {
8
9 //Emitted when update function is called
10 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen.
11 event UpdatedMessages(string oldStr, string newStr);
12
13 // Declares a state variable `message` of type `string`.
14 // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value.
15 string public message;
16
17 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation.
18 // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
19 constructor(string memory initMessage) {
20
21 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable).
22 message = initMessage;
23 }
24
25 // A public function that accepts a string argument and updates the `message` storage variable.
26 function update(string memory newMessage) public {
27 string memory oldMsg = message;
28 message = newMessage;
29 emit UpdatedMessages(oldMsg, newMessage);
30 }
31}
Tampilkan semua

Ini adalah kontrak pintar dasar yang menyimpan pesan saat pembuatan. Ini dapat diperbarui dengan memanggil fungsi update.

Langkah 11: Hubungkan MetaMask & Alchemy ke proyek Anda

Kita telah membuat dompet MetaMask, akun Alchemy, dan menulis kontrak pintar kita, sekarang saatnya untuk menghubungkan ketiganya.

Setiap transaksi yang dikirim dari dompet Anda memerlukan tanda tangan digital menggunakan kunci pribadi unik Anda. Untuk memberikan izin ini kepada program kita, kita dapat menyimpan kunci pribadi kita dengan aman di file lingkungan (environment file). Kita juga akan menyimpan kunci API untuk Alchemy di sini.

Untuk mempelajari lebih lanjut tentang mengirim transaksi, lihat tutorial ini (opens in a new tab) tentang mengirim transaksi menggunakan web3.

Pertama, instal paket dotenv di direktori proyek Anda:

1npm install dotenv --save

Kemudian, buat file .env di direktori root proyek. Tambahkan kunci pribadi MetaMask Anda dan URL API HTTP Alchemy ke dalamnya.

File lingkungan Anda harus diberi nama .env atau tidak akan dikenali sebagai file lingkungan.

Jangan menamainya process.env atau .env-custom atau apa pun yang lain.

Animated walkthrough of getting an Alchemy API key

File .env Anda akan terlihat seperti ini:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PRIVATE_KEY = "your-metamask-private-key"

Untuk benar-benar menghubungkan ini ke kode kita, kita akan mereferensikan variabel-variabel ini di file hardhat.config.js kita pada langkah 13.

Langkah 12: Instal Ethers.js

Ethers.js adalah pustaka yang memudahkan untuk berinteraksi dan membuat permintaan ke Ethereum dengan membungkus metode JSON-RPC standar (opens in a new tab) dengan metode yang lebih ramah pengguna.

Hardhat memungkinkan kita untuk mengintegrasikan plugin (opens in a new tab) untuk perkakas tambahan dan fungsionalitas yang diperluas. Kita akan memanfaatkan plugin Ethers (opens in a new tab) untuk penerapan kontrak.

Di direktori proyek Anda, ketik:

npm install --save-dev @nomiclabs/hardhat-ethers "ethers@^5.0.0"

Langkah 13: Perbarui hardhat.config.js

Kita telah menambahkan beberapa dependensi dan plugin sejauh ini, sekarang kita perlu memperbarui hardhat.config.js sehingga proyek kita mengetahui semuanya.

Perbarui hardhat.config.js Anda agar terlihat seperti ini:

1/**
2 * @type import('hardhat/config').HardhatUserConfig
3 */
4
5require("dotenv").config()
6require("@nomiclabs/hardhat-ethers")
7
8const { API_URL, PRIVATE_KEY } = process.env
9
10module.exports = {
11 solidity: "0.7.3",
12 defaultNetwork: "goerli",
13 networks: {
14 hardhat: {},
15 goerli: {
16 url: API_URL,
17 accounts: [`0x${PRIVATE_KEY}`],
18 },
19 },
20}
Tampilkan semua

Langkah 14: Kompilasi kontrak kita

Untuk memastikan semuanya berfungsi sejauh ini, mari kompilasi kontrak kita. Tugas compile adalah salah satu tugas bawaan Hardhat.

Dari baris perintah, jalankan:

npx hardhat compile

Anda mungkin mendapatkan peringatan tentang SPDX license identifier not provided in source file, tetapi tidak perlu khawatir tentang itu — semoga semuanya terlihat bagus! Jika tidak, Anda selalu dapat mengirim pesan di Discord Alchemy (opens in a new tab).

Langkah 15: Tulis skrip penerapan kita

Sekarang setelah kontrak kita ditulis dan file konfigurasi kita siap, saatnya untuk menulis skrip penerapan kontrak kita.

Navigasikan ke folder scripts/ dan buat file baru bernama deploy.js , lalu tambahkan konten berikut ke dalamnya:

1async function main() {
2 const HelloWorld = await ethers.getContractFactory("HelloWorld")
3
4 // Start deployment, returning a promise that resolves to a contract object // Mulai penyebaran, mengembalikan promise yang menghasilkan objek kontrak
5 const hello_world = await HelloWorld.deploy("Hello World!")
6 console.log("Contract deployed to address:", hello_world.address)
7}
8
9main()
10 .then(() => process.exit(0))
11 .catch((error) => {
12 console.error(error)
13 process.exit(1)
14 })
Tampilkan semua

Hardhat melakukan pekerjaan yang luar biasa dalam menjelaskan apa yang dilakukan setiap baris kode ini dalam tutorial Kontrak (opens in a new tab) mereka, kami telah mengadopsi penjelasan mereka di sini.

1const HelloWorld = await ethers.getContractFactory("HelloWorld")

Sebuah ContractFactory di ethers.js adalah abstraksi yang digunakan untuk menerapkan kontrak pintar baru, jadi HelloWorld di sini adalah pabrik (factory) (opens in a new tab) untuk instansiasi kontrak hello world kita. Saat menggunakan plugin hardhat-ethers, instansiasi ContractFactory dan Contract terhubung ke penandatangan pertama (pemilik) secara default.

1const hello_world = await HelloWorld.deploy()

Memanggil deploy() pada ContractFactory akan memulai penerapan, dan mengembalikan Promise yang diselesaikan menjadi objek Contract. Ini adalah objek yang memiliki metode untuk setiap fungsi kontrak pintar kita.

Langkah 16: Terapkan kontrak kita

Kita akhirnya siap untuk menerapkan kontrak pintar kita! Navigasikan ke baris perintah dan jalankan:

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

Anda kemudian akan melihat sesuatu seperti:

Contract deployed to address: 0x6cd7d44516a20882cEa2DE9f205bF401c0d23570

Harap simpan alamat ini. Kita akan menggunakannya nanti dalam tutorial.

Jika kita pergi ke Goerli etherscan (opens in a new tab) dan mencari alamat kontrak kita, kita seharusnya dapat melihat bahwa itu telah berhasil diterapkan. Transaksi akan terlihat seperti ini:

Alamat From harus cocok dengan alamat akun MetaMask Anda dan alamat To akan mengatakan Contract Creation. Jika kita mengklik transaksi, kita akan melihat alamat kontrak kita di bidang To.

Selamat! Anda baru saja menerapkan kontrak pintar ke testnet Ethereum.

Untuk memahami apa yang terjadi di balik layar, mari navigasikan ke tab Explorer di dasbor Alchemy (opens in a new tab) kita. Jika Anda memiliki beberapa aplikasi Alchemy, pastikan untuk memfilter berdasarkan aplikasi dan pilih Hello World.

Di sini Anda akan melihat beberapa metode JSON-RPC yang dibuat Hardhat/Ethers di balik layar untuk kita saat kita memanggil fungsi .deploy(). Dua metode penting di sini adalah eth_sendRawTransaction (opens in a new tab), yang merupakan permintaan untuk menulis kontrak kita ke rantai Goerli, dan eth_getTransactionByHash (opens in a new tab), yang merupakan permintaan untuk membaca informasi tentang transaksi kita berdasarkan hash. Untuk mempelajari lebih lanjut tentang mengirim transaksi, lihat tutorial kami tentang mengirim transaksi menggunakan Web3.

Bagian 2: Berinteraksi dengan Kontrak Pintar Anda

Sekarang setelah kita berhasil menerapkan kontrak pintar ke jaringan Goerli, mari kita pelajari cara berinteraksi dengannya.

Buat file interact.js

Ini adalah file tempat kita akan menulis skrip interaksi kita. Kita akan menggunakan pustaka Ethers.js yang sebelumnya Anda instal di Bagian 1.

Di dalam folder scripts/, buat file baru bernama interact.js dan tambahkan kode berikut:

1// interact.js // interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS

Perbarui file .env Anda

Kita akan menggunakan variabel lingkungan baru, jadi kita perlu mendefinisikannya di file .env yang kita buat sebelumnya.

Kita perlu menambahkan definisi untuk API_KEY Alchemy kita dan CONTRACT_ADDRESS tempat kontrak pintar Anda diterapkan.

File .env Anda akan terlihat seperti ini:

# .env # .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>"

Ambil ABI kontrak Anda

kontrak kita adalah antarmuka untuk berinteraksi dengan kontrak pintar kita. Hardhat secara otomatis menghasilkan ABI dan menyimpannya di HelloWorld.json. Untuk menggunakan ABI, kita perlu mengurai isinya dengan menambahkan baris kode berikut ke file interact.js kita:

1// interact.js // interact.js
2const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")

Jika Anda ingin melihat ABI, Anda dapat mencetaknya ke konsol Anda:

1console.log(JSON.stringify(contract.abi))

Untuk melihat ABI Anda dicetak ke konsol, navigasikan ke terminal Anda dan jalankan:

npx hardhat run scripts/interact.js

Buat instansiasi kontrak Anda

Untuk berinteraksi dengan kontrak kita, kita perlu membuat instansiasi kontrak dalam kode kita. Untuk melakukannya dengan Ethers.js, kita perlu bekerja dengan tiga konsep:

  1. Provider - penyedia node yang memberi Anda akses baca dan tulis ke blockchain
  2. Signer - mewakili akun Ethereum yang dapat menandatangani transaksi
  3. Contract - objek Ethers.js yang mewakili kontrak tertentu yang diterapkan onchain

Kita akan menggunakan ABI kontrak dari langkah sebelumnya untuk membuat instansiasi kontrak kita:

1// interact.js // interact.js
2
3// Provider // Provider
4const alchemyProvider = new ethers.providers.AlchemyProvider(
5 (network = "goerli"),
6 API_KEY
7)
8
9// Signer // Signer
10const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
11
12// Contract // Kontrak
13const helloWorldContract = new ethers.Contract(
14 CONTRACT_ADDRESS,
15 contract.abi,
16 signer
17)
Tampilkan semua

Pelajari lebih lanjut tentang Provider, Signer, dan Contract di dokumentasi ethers.js (opens in a new tab).

Baca pesan inisialisasi

Ingat ketika kita menerapkan kontrak kita dengan initMessage = "Hello world!"? Kita sekarang akan membaca pesan yang disimpan dalam kontrak pintar kita dan mencetaknya ke konsol.

Dalam JavaScript, fungsi asinkron digunakan saat berinteraksi dengan jaringan. Untuk mempelajari lebih lanjut tentang fungsi asinkron, baca artikel medium ini (opens in a new tab).

Gunakan kode di bawah ini untuk memanggil fungsi message dalam kontrak pintar kita dan membaca pesan inisialisasi:

1// interact.js // interact.js
2
3// ... // ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8}
9main()
Tampilkan semua

Setelah menjalankan file menggunakan npx hardhat run scripts/interact.js di terminal, kita akan melihat respons ini:

1The message is: Hello world!

Selamat! Anda baru saja berhasil membaca data kontrak pintar dari blockchain Ethereum, kerja bagus!

Perbarui pesan

Alih-alih hanya membaca pesan, kita juga dapat memperbarui pesan yang disimpan dalam kontrak pintar kita menggunakan fungsi update! Keren, kan?

Untuk memperbarui pesan, kita dapat langsung memanggil fungsi update pada objek Contract yang telah kita instansiasi:

1// interact.js // interact.js
2
3// ... // ...
4
5async function main() {
6 const message = await helloWorldContract.message()
7 console.log("The message is: " + message)
8
9 console.log("Updating the message...")
10 const tx = await helloWorldContract.update("This is the new message.")
11 await tx.wait()
12}
13main()
Tampilkan semua

Perhatikan bahwa pada baris 11, kita memanggil .wait() pada objek transaksi yang dikembalikan. Ini memastikan bahwa skrip kita menunggu transaksi ditambang di blockchain sebelum keluar dari fungsi. Jika panggilan .wait() tidak disertakan, skrip mungkin tidak melihat nilai message yang diperbarui dalam kontrak.

Baca pesan baru

Anda seharusnya dapat mengulangi langkah sebelumnya untuk membaca nilai message yang diperbarui. Luangkan waktu sejenak dan lihat apakah Anda dapat membuat perubahan yang diperlukan untuk mencetak nilai baru tersebut!

Jika Anda butuh petunjuk, inilah tampilan file interact.js Anda pada tahap ini:

1// interact.js // interact.js
2
3const API_KEY = process.env.API_KEY
4const PRIVATE_KEY = process.env.PRIVATE_KEY
5const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS
6
7const contract = require("../artifacts/contracts/HelloWorld.sol/HelloWorld.json")
8
9// provider - Alchemy // provider - Alchemy
10const alchemyProvider = new ethers.providers.AlchemyProvider(
11 (network = "goerli"),
12 API_KEY
13)
14
15// signer - you // signer - Anda
16const signer = new ethers.Wallet(PRIVATE_KEY, alchemyProvider)
17
18// contract instance // instans kontrak
19const helloWorldContract = new ethers.Contract(
20 CONTRACT_ADDRESS,
21 contract.abi,
22 signer
23)
24
25async function main() {
26 const message = await helloWorldContract.message()
27 console.log("The message is: " + message)
28
29 console.log("Updating the message...")
30 const tx = await helloWorldContract.update("this is the new message")
31 await tx.wait()
32
33 const newMessage = await helloWorldContract.message()
34 console.log("The new message is: " + newMessage)
35}
36
37main()
Tampilkan semua

Sekarang jalankan saja skripnya dan Anda seharusnya dapat melihat pesan lama, status pembaruan, dan pesan baru dicetak ke terminal Anda!

npx hardhat run scripts/interact.js --network goerli

1The message is: Hello World!
2Updating the message...
3The new message is: This is the new message.

Saat menjalankan skrip tersebut, Anda mungkin memperhatikan bahwa langkah Updating the message... membutuhkan waktu beberapa saat untuk dimuat sebelum pesan baru dimuat. Hal itu disebabkan oleh proses penambangan; jika Anda penasaran untuk melacak transaksi saat sedang ditambang, kunjungi mempool Alchemy (opens in a new tab) untuk melihat status transaksi. Jika transaksi dibatalkan, ada baiknya juga untuk memeriksa Goerli Etherscan (opens in a new tab) dan mencari hash transaksi Anda.

Bagian 3: Publikasikan Kontrak Pintar Anda ke Etherscan

Anda telah melakukan semua kerja keras untuk menghidupkan kontrak pintar Anda; sekarang saatnya untuk membagikannya kepada dunia!

Dengan memverifikasi kontrak pintar Anda di Etherscan, siapa pun dapat melihat kode sumber Anda dan berinteraksi dengan kontrak pintar Anda. Mari kita mulai!

Langkah 1: Buat Kunci API di akun Etherscan Anda

Kunci API Etherscan diperlukan untuk memverifikasi bahwa Anda memiliki kontrak pintar yang Anda coba publikasikan.

Jika Anda belum memiliki akun Etherscan, daftar untuk membuat akun (opens in a new tab).

Setelah masuk, temukan nama pengguna Anda di bilah navigasi, arahkan kursor ke atasnya dan pilih tombol My profile.

Di halaman profil Anda, Anda akan melihat bilah navigasi samping. Dari bilah navigasi samping, pilih API Keys. Selanjutnya, tekan tombol "Add" untuk membuat kunci API baru, beri nama aplikasi Anda hello-world dan tekan tombol Create New API Key.

Kunci API baru Anda akan muncul di tabel kunci API. Salin kunci API ke papan klip Anda.

Selanjutnya, kita perlu menambahkan kunci API Etherscan ke file .env kita.

Setelah menambahkannya, file .env Anda akan terlihat seperti ini:

1API_URL = "https://eth-goerli.alchemyapi.io/v2/your-api-key"
2PUBLIC_KEY = "your-public-account-address"
3PRIVATE_KEY = "your-private-account-address"
4CONTRACT_ADDRESS = "your-contract-address"
5ETHERSCAN_API_KEY = "your-etherscan-key"

Kontrak pintar yang disebarkan dengan Hardhat

Instal hardhat-etherscan

Mempublikasikan kontrak Anda ke Etherscan menggunakan Hardhat sangatlah mudah. Anda pertama-tama perlu menginstal plugin hardhat-etherscan untuk memulai. hardhat-etherscan akan secara otomatis memverifikasi kode sumber dan ABI kontrak pintar di Etherscan. Untuk menambahkannya, di direktori hello-world jalankan:

1npm install --save-dev @nomiclabs/hardhat-etherscan

Setelah diinstal, sertakan pernyataan berikut di bagian atas hardhat.config.js Anda, dan tambahkan opsi konfigurasi Etherscan:

1// hardhat.config.js // hardhat.config.js
2
3require("dotenv").config()
4require("@nomiclabs/hardhat-ethers")
5require("@nomiclabs/hardhat-etherscan")
6
7const { API_URL, PRIVATE_KEY, ETHERSCAN_API_KEY } = process.env
8
9module.exports = {
10 solidity: "0.7.3",
11 defaultNetwork: "goerli",
12 networks: {
13 hardhat: {},
14 goerli: {
15 url: API_URL,
16 accounts: [`0x${PRIVATE_KEY}`],
17 },
18 },
19 etherscan: {
20 // Your API key for Etherscan // Kunci API Anda untuk Etherscan
21 // Obtain one at https://etherscan.io/ // Dapatkan di https://etherscan.io/
22 apiKey: ETHERSCAN_API_KEY,
23 },
24}
Tampilkan semua

Verifikasi kontrak pintar Anda di Etherscan

Pastikan semua file disimpan dan semua variabel .env dikonfigurasi dengan benar.

Jalankan tugas verify, dengan meneruskan alamat kontrak, dan jaringan tempat kontrak tersebut disebarkan:

1npx hardhat verify --network goerli DEPLOYED_CONTRACT_ADDRESS 'Hello World!'

Pastikan bahwa DEPLOYED_CONTRACT_ADDRESS adalah alamat kontrak pintar Anda yang disebarkan di jaringan pengujian Goerli. Selain itu, argumen terakhir ('Hello World!') harus berupa nilai string yang sama yang digunakan selama langkah penyebaran di bagian 1.

Jika semuanya berjalan lancar, Anda akan melihat pesan berikut di terminal Anda:

1Successfully submitted source code for contract
2contracts/HelloWorld.sol:HelloWorld at 0xdeployed-contract-address
3for verification on Etherscan. Waiting for verification result...
4
5
6Successfully verified contract HelloWorld on Etherscan.
7https://goerli.etherscan.io/address/<contract-address>#contracts // goerli.etherscan.io/address/<contract-address>#contracts

Selamat! Kode kontrak pintar Anda ada di Etherscan!

Lihat kontrak pintar Anda di Etherscan!

Saat Anda menavigasi ke tautan yang disediakan di terminal Anda, Anda seharusnya dapat melihat kode kontrak pintar dan ABI Anda dipublikasikan di Etherscan!

Wahooo - Anda berhasil, juara! Sekarang siapa pun dapat memanggil atau menulis ke kontrak pintar Anda! Kami tidak sabar untuk melihat apa yang Anda bangun selanjutnya!

Bagian 4 - Mengintegrasikan kontrak pintar Anda dengan frontend

Pada akhir tutorial ini, Anda akan mengetahui cara:

  • Menghubungkan dompet MetaMask ke dapp Anda
  • Membaca data dari kontrak pintar Anda menggunakan API Alchemy Web3 (opens in a new tab)
  • Menandatangani transaksi Ethereum menggunakan MetaMask

Untuk dapp ini, kita akan menggunakan React (opens in a new tab) sebagai kerangka kerja frontend kita; namun, penting untuk dicatat bahwa kita tidak akan menghabiskan banyak waktu untuk menguraikan dasar-dasarnya, karena kita sebagian besar akan berfokus pada membawa fungsionalitas Web3 ke proyek kita.

Sebagai prasyarat, Anda harus memiliki pemahaman tingkat pemula tentang React. Jika tidak, kami menyarankan untuk menyelesaikan tutorial Pengantar React (opens in a new tab) resmi.

Kloning file pemula

Pertama, buka repositori GitHub hello-world-part-four (opens in a new tab) untuk mendapatkan file pemula untuk proyek ini dan kloning repositori ini ke mesin lokal Anda.

Buka repositori yang dikloning secara lokal. Perhatikan bahwa repositori ini berisi dua folder: starter-files dan completed.

  • starter-files- kita akan bekerja di direktori ini, kita akan menghubungkan UI ke dompet Ethereum Anda dan kontrak pintar yang kita publikasikan ke Etherscan di Bagian 3.
  • completed berisi seluruh tutorial yang telah selesai dan hanya boleh digunakan sebagai referensi jika Anda mengalami kebuntuan.

Selanjutnya, buka salinan starter-files Anda di editor kode favorit Anda, lalu navigasikan ke dalam folder src.

Semua kode yang akan kita tulis akan berada di bawah folder src. Kita akan mengedit komponen HelloWorld.js dan file JavaScript util/interact.js untuk memberikan fungsionalitas Web3 pada proyek kita.

Periksa file pemula

Sebelum kita mulai membuat kode, mari kita jelajahi apa yang disediakan untuk kita di file pemula.

Jalankan proyek react Anda

Mari kita mulai dengan menjalankan proyek React di peramban kita. Keindahan React adalah setelah kita menjalankan proyek di peramban, setiap perubahan yang kita simpan akan diperbarui secara langsung di peramban kita.

Untuk menjalankan proyek, navigasikan ke direktori root dari folder starter-files, dan jalankan npm install di terminal Anda untuk menginstal dependensi proyek:

cd starter-files
npm install

Setelah selesai menginstal, jalankan npm start di terminal Anda:

npm start

Melakukan hal tersebut akan membuka http://localhost:3000/ (opens in a new tab) di peramban Anda, di mana Anda akan melihat frontend untuk proyek kita. Ini harus terdiri dari satu bidang (tempat untuk memperbarui pesan yang disimpan di kontrak pintar Anda), tombol "Connect Wallet", dan tombol "Update".

Jika Anda mencoba mengklik salah satu tombol, Anda akan menyadari bahwa tombol tersebut tidak berfungsi—itu karena kita masih perlu memprogram fungsionalitasnya.

Komponen HelloWorld.js

Mari kembali ke folder src di editor kita dan buka file HelloWorld.js. Sangat penting bagi kita untuk memahami semua yang ada di file ini, karena ini adalah komponen React utama yang akan kita kerjakan.

Di bagian atas file ini, Anda akan melihat bahwa kita memiliki beberapa pernyataan impor yang diperlukan untuk menjalankan proyek kita, termasuk pustaka React, hook useEffect dan useState, beberapa item dari ./util/interact.js (kita akan menjelaskannya lebih detail segera!), dan logo Alchemy.

1// HelloWorld.js // HelloWorld.js
2
3import React from "react"
4import { useEffect, useState } from "react"
5import {
6 helloWorldContract,
7 connectWallet,
8 updateMessage,
9 loadCurrentMessage,
10 getCurrentWalletConnected,
11} from "./util/interact.js"
12
13import alchemylogo from "./alchemylogo.svg"
Tampilkan semua

Selanjutnya, kita memiliki variabel status yang akan kita perbarui setelah peristiwa tertentu.

1// HelloWorld.js // HelloWorld.js
2
3//State variables // Variabel state
4const [walletAddress, setWallet] = useState("")
5const [status, setStatus] = useState("")
6const [message, setMessage] = useState("No connection to the network.")
7const [newMessage, setNewMessage] = useState("")

Berikut adalah apa yang diwakili oleh masing-masing variabel:

  • walletAddress - string yang menyimpan alamat dompet pengguna
  • status- string yang menyimpan pesan bermanfaat yang memandu pengguna tentang cara berinteraksi dengan dapp
  • message - string yang menyimpan pesan saat ini di kontrak pintar
  • newMessage - string yang menyimpan pesan baru yang akan ditulis ke kontrak pintar

Setelah variabel status, Anda akan melihat lima fungsi yang belum diimplementasikan: useEffect ,addSmartContractListener, addWalletListener , connectWalletPressed, dan onUpdatePressed. Kita akan menjelaskan apa yang mereka lakukan di bawah ini:

1// HelloWorld.js // HelloWorld.js
2
3//called only once // hanya dipanggil sekali
4useEffect(async () => {
5 //TODO: implement // TODO: implementasikan
6}, [])
7
8function addSmartContractListener() {
9 //TODO: implement // TODO: implementasikan
10}
11
12function addWalletListener() {
13 //TODO: implement // TODO: implementasikan
14}
15
16const connectWalletPressed = async () => {
17 //TODO: implement // TODO: implementasikan
18}
19
20const onUpdatePressed = async () => {
21 //TODO: implement // TODO: implementasikan
22}
Tampilkan semua
  • useEffect (opens in a new tab)- ini adalah hook React yang dipanggil setelah komponen Anda dirender. Karena ia memiliki prop array kosong [] yang diteruskan ke dalamnya (lihat baris 4), ia hanya akan dipanggil pada render pertama komponen. Di sini kita akan memuat pesan saat ini yang disimpan di kontrak pintar kita, memanggil pendengar kontrak pintar dan dompet kita, dan memperbarui UI kita untuk mencerminkan apakah dompet sudah terhubung.
  • addSmartContractListener- fungsi ini menyiapkan pendengar yang akan mengawasi peristiwa UpdatedMessages dari kontrak HelloWorld kita dan memperbarui UI kita ketika pesan diubah di kontrak pintar kita.
  • addWalletListener- fungsi ini menyiapkan pendengar yang mendeteksi perubahan pada status dompet MetaMask pengguna, seperti ketika pengguna memutuskan sambungan dompet mereka atau beralih alamat.
  • connectWalletPressed- fungsi ini akan dipanggil untuk menghubungkan dompet MetaMask pengguna ke dapp kita.
  • onUpdatePressed - fungsi ini akan dipanggil ketika pengguna ingin memperbarui pesan yang disimpan di kontrak pintar.

Di dekat akhir file ini, kita memiliki UI dari komponen kita.

1// HelloWorld.js // HelloWorld.js
2
3//the UI of our component // UI dari komponen kita
4return (
5 <div id="container">
6 <img id="logo" src={alchemylogo}></img>
7 <button id="walletButton" onClick={connectWalletPressed}>
8 {walletAddress.length > 0 ? (
9 "Connected: " +
10 String(walletAddress).substring(0, 6) +
11 "..." +
12 String(walletAddress).substring(38)
13 ) : (
14 <span>Connect Wallet</span>
15 )}
16 </button>
17
18 <h2 style={{ paddingTop: "50px" }}>Current Message:</h2>
19 <p>{message}</p>
20
21 <h2 style={{ paddingTop: "18px" }}>New Message:</h2>
22
23 <div>
24 <input
25 type="text"
26 placeholder="Update the message in your smart contract."
27 onChange={(e) => setNewMessage(e.target.value)}
28 value={newMessage}
29 />
30 <p id="status">{status}</p>
31
32 <button id="publishButton" onClick={onUpdatePressed}>
33 Update
34 </button>
35</div>
36
37</div>
38)
Tampilkan semua

Jika Anda memindai kode ini dengan cermat, Anda akan melihat di mana kita menggunakan berbagai variabel status kita di UI kita:

  • Pada baris 6-12, jika dompet pengguna terhubung (yaitu, walletAddress.length > 0), kita menampilkan versi terpotong dari walletAddress pengguna di tombol dengan ID "walletButton;" jika tidak, ia hanya mengatakan "Connect Wallet."
  • Pada baris 17, kita menampilkan pesan saat ini yang disimpan di kontrak pintar, yang ditangkap dalam string message.
  • Pada baris 23-26, kita menggunakan komponen terkontrol (opens in a new tab) untuk memperbarui variabel status newMessage kita ketika input di bidang teks berubah.

Selain variabel status kita, Anda juga akan melihat bahwa fungsi connectWalletPressed dan onUpdatePressed dipanggil ketika tombol dengan ID publishButton dan walletButton masing-masing diklik.

Terakhir, mari kita bahas di mana komponen HelloWorld.js ini ditambahkan.

Jika Anda pergi ke file App.js, yang merupakan komponen utama di React yang bertindak sebagai wadah untuk semua komponen lainnya, Anda akan melihat bahwa komponen HelloWorld.js kita disuntikkan pada baris 7.

Terakhir namun tidak kalah pentingnya, mari kita periksa satu file lagi yang disediakan untuk Anda, file interact.js.

File interact.js

Karena kita ingin mengikuti paradigma M-V-C (opens in a new tab), kita akan menginginkan file terpisah yang berisi semua fungsi kita untuk mengelola logika, data, dan aturan dapp kita, dan kemudian dapat mengekspor fungsi-fungsi tersebut ke frontend kita (komponen HelloWorld.js kita).

👆🏽Inilah tujuan pasti dari file interact.js kita!

Navigasikan ke folder util di direktori src Anda, dan Anda akan melihat bahwa kita telah menyertakan file bernama interact.js yang akan berisi semua interaksi kontrak pintar dan fungsi serta variabel dompet kita.

1// interact.js // interact.js
2
3//export const helloWorldContract; // export const helloWorldContract;
4
5export const loadCurrentMessage = async () => {}
6
7export const connectWallet = async () => {}
8
9const getCurrentWalletConnected = async () => {}
10
11export const updateMessage = async (message) => {}
Tampilkan semua

Anda akan melihat di bagian atas file bahwa kita telah mengomentari objek helloWorldContract. Nanti dalam tutorial ini, kita akan menghapus komentar objek ini dan membuat instansiasi kontrak pintar kita di variabel ini, yang kemudian akan kita ekspor ke komponen HelloWorld.js kita.

Empat fungsi yang belum diimplementasikan setelah objek helloWorldContract kita melakukan hal berikut:

  • loadCurrentMessage - fungsi ini menangani logika memuat pesan saat ini yang disimpan di kontrak pintar. Ini akan melakukan panggilan baca ke kontrak pintar Hello World menggunakan API Alchemy Web3 (opens in a new tab).
  • connectWallet - fungsi ini akan menghubungkan MetaMask pengguna ke dapp kita.
  • getCurrentWalletConnected - fungsi ini akan memeriksa apakah akun Ethereum sudah terhubung ke dapp kita saat halaman dimuat dan memperbarui UI kita sesuai dengan itu.
  • updateMessage - fungsi ini akan memperbarui pesan yang disimpan di kontrak pintar. Ini akan melakukan panggilan tulis ke kontrak pintar Hello World, sehingga dompet MetaMask pengguna harus menandatangani transaksi Ethereum untuk memperbarui pesan.

Sekarang setelah kita memahami apa yang sedang kita kerjakan, mari kita cari tahu cara membaca dari kontrak pintar kita!

Langkah 3: Membaca dari kontrak pintar Anda

Untuk membaca dari kontrak pintar Anda, Anda harus berhasil menyiapkan:

  • Koneksi API ke rantai Ethereum
  • Instansiasi yang dimuat dari kontrak pintar Anda
  • Fungsi untuk memanggil fungsi kontrak pintar Anda
  • Pendengar untuk mengawasi pembaruan ketika data yang Anda baca dari kontrak pintar berubah

Ini mungkin terdengar seperti banyak langkah, tetapi jangan khawatir! Kami akan memandu Anda tentang cara melakukan masing-masing langkah demi langkah! :)

Buat koneksi API ke rantai Ethereum

Jadi ingat bagaimana di Bagian 2 dari tutorial ini, kita menggunakan kunci Alchemy Web3 kita untuk membaca dari kontrak pintar kita (opens in a new tab)? Anda juga akan memerlukan kunci Alchemy Web3 di dapp Anda untuk membaca dari rantai.

Jika Anda belum memilikinya, pertama-tama instal Alchemy Web3 (opens in a new tab) dengan menavigasi ke direktori root dari starter-files Anda dan menjalankan yang berikut ini di terminal Anda:

1npm install @alch/alchemy-web3

Alchemy Web3 (opens in a new tab) adalah pembungkus di sekitar Web3.js (opens in a new tab), menyediakan metode API yang ditingkatkan dan manfaat penting lainnya untuk membuat hidup Anda sebagai pengembang web3 lebih mudah. Ini dirancang untuk memerlukan konfigurasi minimal sehingga Anda dapat mulai menggunakannya di aplikasi Anda segera!

Kemudian, instal paket dotenv (opens in a new tab) di direktori proyek Anda, sehingga kita memiliki tempat yang aman untuk menyimpan kunci API kita setelah kita mengambilnya.

1npm install dotenv --save

Untuk dapp kita, kita akan menggunakan kunci API Websockets kita alih-alih kunci API HTTP kita, karena ini akan memungkinkan kita untuk menyiapkan pendengar yang mendeteksi ketika pesan yang disimpan di kontrak pintar berubah.

Setelah Anda memiliki kunci API Anda, buat file .env di direktori root Anda dan tambahkan url Alchemy Websockets Anda ke dalamnya. Setelah itu, file .env Anda akan terlihat seperti ini:

1REACT_APP_ALCHEMY_KEY = wss://eth-goerli.ws.alchemyapi.io/v2/<key> // eth-goerli.ws.alchemyapi.io/v2/<key>

Sekarang, kita siap untuk menyiapkan titik akhir Alchemy Web3 kita di dapp kita! Mari kembali ke interact.js kita, yang bersarang di dalam folder util kita dan tambahkan kode berikut di bagian atas file:

1// interact.js // interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8//export const helloWorldContract; // export const helloWorldContract;

Di atas, kita pertama-tama mengimpor kunci Alchemy dari file .env kita dan kemudian meneruskan alchemyKey kita ke createAlchemyWeb3 untuk membuat titik akhir Alchemy Web3 kita.

Dengan titik akhir ini siap, saatnya untuk memuat kontrak pintar kita!

Memuat kontrak pintar Hello World Anda

Untuk memuat kontrak pintar Hello World Anda, Anda akan memerlukan alamat kontrak dan ABI-nya, yang keduanya dapat ditemukan di Etherscan jika Anda menyelesaikan Bagian 3 dari tutorial ini.

Cara mendapatkan ABI kontrak Anda dari Etherscan

Jika Anda melewati Bagian 3 dari tutorial ini, Anda dapat menggunakan kontrak HelloWorld dengan alamat 0x6f3f635A9762B47954229Ea479b4541eAF402A6A (opens in a new tab). ABI-nya dapat ditemukan di sini (opens in a new tab).

ABI kontrak diperlukan untuk menentukan fungsi mana yang akan dipanggil oleh kontrak serta memastikan bahwa fungsi tersebut akan mengembalikan data dalam format yang Anda harapkan. Setelah kita menyalin ABI kontrak kita, mari simpan sebagai file JSON bernama contract-abi.json di direktori src Anda.

contract-abi.json Anda harus disimpan di folder src Anda.

Berbekal alamat kontrak, ABI, dan titik akhir Alchemy Web3 kita, kita dapat menggunakan metode kontrak (opens in a new tab) untuk memuat instansiasi kontrak pintar kita. Impor ABI kontrak Anda ke dalam file interact.js dan tambahkan alamat kontrak Anda.

1// interact.js // interact.js
2
3const contractABI = require("../contract-abi.json")
4const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"

Kita sekarang akhirnya dapat menghapus komentar variabel helloWorldContract kita, dan memuat kontrak pintar menggunakan titik akhir AlchemyWeb3 kita:

1// interact.js // interact.js
2export const helloWorldContract = new web3.eth.Contract(
3 contractABI,
4 contractAddress
5)

Sebagai rekap, 12 baris pertama dari interact.js Anda sekarang akan terlihat seperti ini:

1// interact.js // interact.js
2
3require("dotenv").config()
4const alchemyKey = process.env.REACT_APP_ALCHEMY_KEY
5const { createAlchemyWeb3 } = require("@alch/alchemy-web3")
6const web3 = createAlchemyWeb3(alchemyKey)
7
8const contractABI = require("../contract-abi.json")
9const contractAddress = "0x6f3f635A9762B47954229Ea479b4541eAF402A6A"
10
11export const helloWorldContract = new web3.eth.Contract(
12 contractABI,
13 contractAddress
14)
Tampilkan semua

Sekarang setelah kontrak kita dimuat, kita dapat mengimplementasikan fungsi loadCurrentMessage kita!

Mengimplementasikan loadCurrentMessage di file interact.js Anda

Fungsi ini sangat sederhana. Kita akan melakukan panggilan web3 async sederhana untuk membaca dari kontrak kita. Fungsi kita akan mengembalikan pesan yang disimpan di kontrak pintar:

Perbarui loadCurrentMessage di file interact.js Anda menjadi berikut ini:

1// interact.js // interact.js
2
3export const loadCurrentMessage = async () => {
4 const message = await helloWorldContract.methods.message().call()
5 return message
6}

Karena kita ingin menampilkan kontrak pintar ini di UI kita, mari perbarui fungsi useEffect di komponen HelloWorld.js kita menjadi berikut ini:

1// HelloWorld.js // HelloWorld.js
2
3//called only once // hanya dipanggil sekali
4useEffect(async () => {
5 const message = await loadCurrentMessage()
6 setMessage(message)
7}, [])

Catatan, kita hanya ingin loadCurrentMessage kita dipanggil sekali selama render pertama komponen. Kita akan segera mengimplementasikan addSmartContractListener untuk memperbarui UI secara otomatis setelah pesan di kontrak pintar berubah.

Sebelum kita menyelami pendengar kita, mari kita periksa apa yang kita miliki sejauh ini! Simpan file HelloWorld.js dan interact.js Anda, lalu buka http://localhost:3000/ (opens in a new tab)

Anda akan melihat bahwa pesan saat ini tidak lagi mengatakan "No connection to the network." Sebaliknya, ini mencerminkan pesan yang disimpan di kontrak pintar. Keren!

UI Anda sekarang harus mencerminkan pesan yang disimpan di kontrak pintar

Sekarang berbicara tentang pendengar itu...

Implementasikan addSmartContractListener

Jika Anda mengingat kembali file HelloWorld.sol yang kita tulis di Bagian 1 dari seri tutorial ini (opens in a new tab), Anda akan ingat bahwa ada peristiwa kontrak pintar yang disebut UpdatedMessages yang dipancarkan setelah fungsi update kontrak pintar kita dipanggil (lihat baris 9 dan 27):

1// HelloWorld.sol // HelloWorld.sol
2
3// Specifies the version of Solidity, using semantic versioning. // Menentukan versi Solidity, menggunakan semantic versioning.
4// Learn more: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma // Pelajari lebih lanjut: https://solidity.readthedocs.io/en/v0.5.10/layout-of-source-files.html#pragma
5pragma solidity ^0.7.3;
6
7// Defines a contract named `HelloWorld`. // Mendefinisikan kontrak bernama `HelloWorld`.
8// A contract is a collection of functions and data (its state). Once deployed, a contract resides at a specific address on the Ethereum blockchain. Learn more: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html // Kontrak adalah kumpulan fungsi dan data (state-nya). Setelah disebarkan, kontrak berada di alamat tertentu di blockchain Ethereum. Pelajari lebih lanjut: https://solidity.readthedocs.io/en/v0.5.10/structure-of-a-contract.html
9contract HelloWorld {
10
11 //Emitted when update function is called // Dikeluarkan saat fungsi update dipanggil
12 //Smart contract events are a way for your contract to communicate that something happened on the blockchain to your app front-end, which can be 'listening' for certain events and take action when they happen. // Event kontrak pintar adalah cara bagi kontrak Anda untuk mengomunikasikan bahwa sesuatu telah terjadi di blockchain ke front-end aplikasi Anda, yang dapat 'mendengarkan' event tertentu dan mengambil tindakan saat event tersebut terjadi.
13 event UpdatedMessages(string oldStr, string newStr);
14
15 // Declares a state variable `message` of type `string`. // Mendeklarasikan variabel state `message` dengan tipe `string`.
16 // State variables are variables whose values are permanently stored in contract storage. The keyword `public` makes variables accessible from outside a contract and creates a function that other contracts or clients can call to access the value. // Variabel state adalah variabel yang nilainya disimpan secara permanen di penyimpanan kontrak. Kata kunci `public` membuat variabel dapat diakses dari luar kontrak dan membuat fungsi yang dapat dipanggil oleh kontrak atau klien lain untuk mengakses nilainya.
17 string public message;
18
19 // Similar to many class-based object-oriented languages, a constructor is a special function that is only executed upon contract creation. // Mirip dengan banyak bahasa berorientasi objek berbasis kelas, konstruktor adalah fungsi khusus yang hanya dieksekusi saat pembuatan kontrak.
20 // Constructors are used to initialize the contract's data. Learn more:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors // Konstruktor digunakan untuk menginisialisasi data kontrak. Pelajari lebih lanjut:https://solidity.readthedocs.io/en/v0.5.10/contracts.html#constructors
21 constructor(string memory initMessage) {
22
23 // Accepts a string argument `initMessage` and sets the value into the contract's `message` storage variable). // Menerima argumen string `initMessage` dan menetapkan nilainya ke dalam variabel penyimpanan `message` kontrak).
24 message = initMessage;
25 }
26
27 // A public function that accepts a string argument and updates the `message` storage variable. // Fungsi publik yang menerima argumen string dan memperbarui variabel penyimpanan `message`.
28 function update(string memory newMessage) public {
29 string memory oldMsg = message;
30 message = newMessage;
31 emit UpdatedMessages(oldMsg, newMessage);
32 }
33}
Tampilkan semua

Peristiwa kontrak pintar adalah cara bagi kontrak Anda untuk mengomunikasikan bahwa sesuatu terjadi (yaitu, ada peristiwa) di blockchain ke aplikasi front-end Anda, yang dapat 'mendengarkan' peristiwa tertentu dan mengambil tindakan ketika peristiwa itu terjadi.

Fungsi addSmartContractListener akan secara khusus mendengarkan peristiwa UpdatedMessages dari kontrak pintar Hello World kita, dan memperbarui UI kita untuk menampilkan pesan baru.

Ubah addSmartContractListener menjadi berikut ini:

1// HelloWorld.js // HelloWorld.js
2
3function addSmartContractListener() {
4 helloWorldContract.events.UpdatedMessages({}, (error, data) => {
5 if (error) {
6 setStatus("😥 " + error.message)
7 } else {
8 setMessage(data.returnValues[1])
9 setNewMessage("")
10 setStatus("🎉 Your message has been updated!")
11 }
12 })
13}
Tampilkan semua

Mari kita uraikan apa yang terjadi ketika pendengar mendeteksi suatu peristiwa:

  • Jika terjadi kesalahan saat peristiwa dipancarkan, itu akan tercermin di UI melalui variabel status status kita.
  • Jika tidak, kita akan menggunakan objek data yang dikembalikan. data.returnValues adalah array yang diindeks pada nol di mana elemen pertama dalam array menyimpan pesan sebelumnya dan elemen kedua menyimpan pesan yang diperbarui. Secara keseluruhan, pada peristiwa yang berhasil kita akan mengatur string message kita ke pesan yang diperbarui, menghapus string newMessage, dan memperbarui variabel status status kita untuk mencerminkan bahwa pesan baru telah dipublikasikan di kontrak pintar kita.

Terakhir, mari panggil pendengar kita di fungsi useEffect kita sehingga diinisialisasi pada render pertama komponen HelloWorld.js. Secara keseluruhan, fungsi useEffect Anda akan terlihat seperti ini:

1// HelloWorld.js // HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7}, [])

Sekarang setelah kita dapat membaca dari kontrak pintar kita, akan sangat bagus untuk mencari tahu cara menulis ke dalamnya juga! Namun, untuk menulis ke dapp kita, kita harus terlebih dahulu memiliki dompet Ethereum yang terhubung dengannya.

Jadi, selanjutnya kita akan menangani penyiapan dompet Ethereum kita (MetaMask) dan kemudian menghubungkannya ke dapp kita!

Langkah 4: Siapkan dompet Ethereum Anda

Untuk menulis apa pun ke rantai Ethereum, pengguna harus menandatangani transaksi menggunakan kunci pribadi dompet virtual mereka. Untuk tutorial ini, kita akan menggunakan MetaMask (opens in a new tab), dompet virtual di peramban yang digunakan untuk mengelola alamat akun Ethereum Anda, karena ini membuat penandatanganan transaksi ini sangat mudah bagi pengguna akhir.

Jika Anda ingin memahami lebih lanjut tentang cara kerja transaksi di Ethereum, periksa halaman ini dari yayasan Ethereum.

Unduh MetaMask

Anda dapat mengunduh dan membuat akun MetaMask secara gratis di sini (opens in a new tab). Saat Anda membuat akun, atau jika Anda sudah memiliki akun, pastikan untuk beralih ke “Goerli Test Network” di kanan atas (sehingga kita tidak berurusan dengan uang sungguhan).

Tambahkan ether dari Faucet

Untuk menandatangani transaksi di blockchain Ethereum, kita akan membutuhkan beberapa Eth palsu. Untuk mendapatkan Eth Anda dapat pergi ke FaucETH (opens in a new tab) dan masukkan alamat akun Goerli Anda, klik “Request funds”, lalu pilih “Ethereum Testnet Goerli” di dropdown dan terakhir klik tombol “Request funds” lagi. Anda akan melihat Eth di akun MetaMask Anda segera setelahnya!

Periksa Saldo Anda

Untuk memeriksa ulang saldo kita ada di sana, mari buat permintaan eth_getBalance (opens in a new tab) menggunakan alat komposer Alchemy (opens in a new tab). Ini akan mengembalikan jumlah Eth di dompet kita. Setelah Anda memasukkan alamat akun MetaMask Anda dan mengklik “Send Request”, Anda akan melihat respons seperti ini:

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

CATATAN: Hasil ini dalam wei bukan eth. Wei digunakan sebagai denominasi terkecil dari ether. Konversi dari wei ke eth adalah: 1 eth = 10¹⁸ wei. Jadi jika kita mengonversi 0xde0b6b3a7640000 ke desimal kita mendapatkan 1*10¹⁸ yang sama dengan 1 eth.

Fiuh! Uang palsu kita semuanya ada di sana! 🤑

Langkah 5: Hubungkan MetaMask ke UI Anda

Sekarang setelah dompet MetaMask kita disiapkan, mari hubungkan dapp kita ke sana!

Fungsi connectWallet

Di file interact.js kita, mari implementasikan fungsi connectWallet, yang kemudian dapat kita panggil di komponen HelloWorld.js kita.

Mari ubah connectWallet menjadi berikut ini:

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

Jadi apa sebenarnya yang dilakukan oleh blok kode raksasa ini?

Nah, pertama, ia memeriksa apakah window.ethereum diaktifkan di peramban Anda.

window.ethereum adalah API global yang disuntikkan oleh MetaMask dan penyedia dompet lainnya yang memungkinkan situs web untuk meminta akun Ethereum pengguna. Jika disetujui, ia dapat membaca data dari blockchain yang terhubung dengan pengguna, dan menyarankan agar pengguna menandatangani pesan dan transaksi. Periksa dokumen MetaMask (opens in a new tab) untuk info lebih lanjut!

Jika window.ethereum tidak ada, maka itu berarti MetaMask tidak diinstal. Ini menghasilkan objek JSON yang dikembalikan, di mana address yang dikembalikan adalah string kosong, dan objek JSX status menyampaikan bahwa pengguna harus menginstal MetaMask.

Sekarang jika window.ethereum ada, maka saat itulah hal-hal menjadi menarik.

Menggunakan loop try/catch, kita akan mencoba terhubung ke MetaMask dengan memanggil window.ethereum.request({ method: "eth_requestAccounts" }); (opens in a new tab). Memanggil fungsi ini akan membuka MetaMask di peramban, di mana pengguna akan diminta untuk menghubungkan dompet mereka ke dapp Anda.

  • Jika pengguna memilih untuk terhubung, method: "eth_requestAccounts" akan mengembalikan array yang berisi semua alamat akun pengguna yang terhubung ke dapp. Secara keseluruhan, fungsi connectWallet kita akan mengembalikan objek JSON yang berisi address pertama dalam array ini (lihat baris 9) dan pesan status yang meminta pengguna untuk menulis pesan ke kontrak pintar.
  • Jika pengguna menolak koneksi, maka objek JSON akan berisi string kosong untuk address yang dikembalikan dan pesan status yang mencerminkan bahwa pengguna menolak koneksi.

Sekarang setelah kita menulis fungsi connectWallet ini, langkah selanjutnya adalah memanggilnya ke komponen HelloWorld.js kita.

Tambahkan fungsi connectWallet ke Komponen UI HelloWorld.js Anda

Navigasikan ke fungsi connectWalletPressed di HelloWorld.js, dan perbarui menjadi berikut ini:

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

Perhatikan bagaimana sebagian besar fungsionalitas kita diabstraksikan dari komponen HelloWorld.js kita dari file interact.js? Ini agar kita mematuhi paradigma M-V-C!

Di connectWalletPressed, kita cukup melakukan panggilan await ke fungsi connectWallet yang kita impor, dan menggunakan responsnya, kita memperbarui variabel status dan walletAddress kita melalui hook status mereka.

Sekarang, mari simpan kedua file (HelloWorld.js dan interact.js) dan uji UI kita sejauh ini.

Buka peramban Anda di halaman http://localhost:3000/ (opens in a new tab), dan tekan tombol "Connect Wallet" di kanan atas halaman.

Jika Anda telah menginstal MetaMask, Anda akan diminta untuk menghubungkan dompet Anda ke dapp Anda. Terima undangan untuk terhubung.

Anda akan melihat bahwa tombol dompet sekarang mencerminkan bahwa alamat Anda terhubung! Yasssss 🔥

Selanjutnya, coba segarkan halaman... ini aneh. Tombol dompet kita meminta kita untuk menghubungkan MetaMask, meskipun sudah terhubung...

Namun, jangan takut! Kita dapat dengan mudah mengatasinya dengan mengimplementasikan getCurrentWalletConnected, yang akan memeriksa apakah alamat sudah terhubung ke dapp kita dan memperbarui UI kita sesuai dengan itu!

Fungsi getCurrentWalletConnected

Perbarui fungsi getCurrentWalletConnected Anda di file interact.js menjadi berikut ini:

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

Kode ini sangat mirip dengan fungsi connectWallet yang baru saja kita tulis di langkah sebelumnya.

Perbedaan utamanya adalah alih-alih memanggil metode eth_requestAccounts, yang membuka MetaMask bagi pengguna untuk menghubungkan dompet mereka, di sini kita memanggil metode eth_accounts, yang hanya mengembalikan array yang berisi alamat MetaMask yang saat ini terhubung ke dapp kita.

Untuk melihat fungsi ini beraksi, mari panggil di fungsi useEffect dari komponen HelloWorld.js kita:

1// HelloWorld.js // HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11}, [])
Tampilkan semua

Perhatikan, kita menggunakan respons dari panggilan kita ke getCurrentWalletConnected untuk memperbarui variabel status walletAddress dan status kita.

Sekarang setelah Anda menambahkan kode ini, mari coba segarkan jendela peramban kita.

Bagussss! Tombol tersebut harus mengatakan bahwa Anda terhubung, dan menampilkan pratinjau alamat dompet Anda yang terhubung - bahkan setelah Anda menyegarkan!

Implementasikan addWalletListener

Langkah terakhir dalam penyiapan dompet dapp kita adalah mengimplementasikan pendengar dompet sehingga UI kita diperbarui ketika status dompet kita berubah, seperti ketika pengguna memutuskan sambungan atau beralih akun.

Di file HelloWorld.js Anda, ubah fungsi addWalletListener Anda sebagai berikut:

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

Saya yakin Anda bahkan tidak memerlukan bantuan kami untuk memahami apa yang terjadi di sini pada titik ini, tetapi untuk tujuan ketelitian, mari kita uraikan dengan cepat:

  • Pertama, fungsi kita memeriksa apakah window.ethereum diaktifkan (yaitu, MetaMask diinstal).
    • Jika tidak, kita cukup mengatur variabel status status kita ke string JSX yang meminta pengguna untuk menginstal MetaMask.
    • Jika diaktifkan, kita menyiapkan pendengar window.ethereum.on("accountsChanged") pada baris 3 yang mendengarkan perubahan status di dompet MetaMask, yang mencakup ketika pengguna menghubungkan akun tambahan ke dapp, beralih akun, atau memutuskan sambungan akun. Jika ada setidaknya satu akun yang terhubung, variabel status walletAddress diperbarui sebagai akun pertama dalam array accounts yang dikembalikan oleh pendengar. Jika tidak, walletAddress diatur sebagai string kosong.

Terakhir namun tidak kalah pentingnya, kita harus memanggilnya di fungsi useEffect kita:

1// HelloWorld.js // HelloWorld.js
2
3useEffect(async () => {
4 const message = await loadCurrentMessage()
5 setMessage(message)
6 addSmartContractListener()
7
8 const { address, status } = await getCurrentWalletConnected()
9 setWallet(address)
10 setStatus(status)
11
12 addWalletListener()
13}, [])
Tampilkan semua

Dan itu saja! Kita telah berhasil menyelesaikan pemrograman semua fungsionalitas dompet kita! Sekarang ke tugas terakhir kita: memperbarui pesan yang disimpan di kontrak pintar kita!

Langkah 6: Implementasikan fungsi updateMessage

Baiklah teman-teman, kita telah tiba di tahap akhir! Di updateMessage dari file interact.js Anda, kita akan melakukan hal berikut:

  1. Pastikan pesan yang ingin kita publikasikan di kontrak pintar kita valid
  2. Tandatangani transaksi kita menggunakan MetaMask
  3. Panggil fungsi ini dari komponen frontend HelloWorld.js kita

Ini tidak akan memakan waktu lama; mari selesaikan dapp ini!

Penanganan kesalahan input

Tentu saja, masuk akal untuk memiliki semacam penanganan kesalahan input di awal fungsi.

Kita akan menginginkan fungsi kita untuk kembali lebih awal jika tidak ada ekstensi MetaMask yang diinstal, tidak ada dompet yang terhubung (yaitu, address yang diteruskan adalah string kosong), atau message adalah string kosong. Mari tambahkan penanganan kesalahan berikut ke updateMessage:

1// interact.js // interact.js
2
3export const updateMessage = async (address, message) => {
4 if (!window.ethereum || address === null) {
5 return {
6 status:
7 "💡 Connect your MetaMask wallet to update the message on the blockchain.",
8 }
9 }
10
11 if (message.trim() === "") {
12 return {
13 status: "❌ Your message cannot be an empty string.",
14 }
15 }
16}
Tampilkan semua

Sekarang setelah memiliki penanganan kesalahan input yang tepat, saatnya untuk menandatangani transaksi melalui MetaMask!

Menandatangani transaksi kita

Jika Anda sudah terbiasa dengan transaksi Ethereum web3 tradisional, kode yang kita tulis selanjutnya akan sangat familier. Di bawah kode penanganan kesalahan input Anda, tambahkan yang berikut ini ke updateMessage:

1// interact.js // interact.js
2
3//set up transaction parameters // menyiapkan parameter transaksi
4const transactionParameters = {
5 to: contractAddress, // Required except during contract publications. // Diperlukan kecuali selama publikasi kontrak.
6 from: address, // must match user's active address. // harus cocok dengan alamat aktif pengguna.
7 data: helloWorldContract.methods.update(message).encodeABI(),
8}
9
10//sign the transaction // menandatangani transaksi
11try {
12 const txHash = await window.ethereum.request({
13 method: "eth_sendTransaction",
14 params: [transactionParameters],
15 })
16 return {
17 status: (
18 <span>
19{" "}
20 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
21 View the status of your transaction on Etherscan!
22 </a>
23 <br />
24 ℹ️ Once the transaction is verified by the network, the message will be
25 updated automatically.
26 </span>
27 ),
28 }
29} catch (error) {
30 return {
31 status: "😥 " + error.message,
32 }
33}
Tampilkan semua

Mari kita uraikan apa yang terjadi. Pertama, kita menyiapkan parameter transaksi kita, di mana:

  • to menentukan alamat penerima (kontrak pintar kita)
  • from menentukan penandatangan transaksi, variabel address yang kita teruskan ke fungsi kita
  • data berisi panggilan ke metode update kontrak pintar Hello World kita, menerima variabel string message kita sebagai input

Kemudian, kita melakukan panggilan await, window.ethereum.request, di mana kita meminta MetaMask untuk menandatangani transaksi. Perhatikan, pada baris 11 dan 12, kita menentukan metode eth kita, eth_sendTransaction dan meneruskan transactionParameters kita.

Pada titik ini, MetaMask akan terbuka di peramban, dan meminta pengguna untuk menandatangani atau menolak transaksi.

  • Jika transaksi berhasil, fungsi akan mengembalikan objek JSON di mana string JSX status meminta pengguna untuk memeriksa Etherscan untuk informasi lebih lanjut tentang transaksi mereka.
  • Jika transaksi gagal, fungsi akan mengembalikan objek JSON di mana string status menyampaikan pesan kesalahan.

Secara keseluruhan, fungsi updateMessage kita akan terlihat seperti ini:

1// interact.js // interact.js
2
3export const updateMessage = async (address, message) => {
4 //input error handling // penanganan kesalahan input
5 if (!window.ethereum || address === null) {
6 return {
7 status:
8 "💡 Connect your MetaMask wallet to update the message on the blockchain.",
9 }
10 }
11
12 if (message.trim() === "") {
13 return {
14 status: "❌ Your message cannot be an empty string.",
15 }
16 }
17
18 //set up transaction parameters // menyiapkan parameter transaksi
19 const transactionParameters = {
20 to: contractAddress, // Required except during contract publications. // Diperlukan kecuali selama publikasi kontrak.
21 from: address, // must match user's active address. // harus cocok dengan alamat aktif pengguna.
22 data: helloWorldContract.methods.update(message).encodeABI(),
23 }
24
25 //sign the transaction // menandatangani transaksi
26 try {
27 const txHash = await window.ethereum.request({
28 method: "eth_sendTransaction",
29 params: [transactionParameters],
30 })
31 return {
32 status: (
33 <span>
34{" "}
35 <a target="_blank" href={`https://goerli.etherscan.io/tx/${txHash}`}>
36 View the status of your transaction on Etherscan!
37 </a>
38 <br />
39 ℹ️ Once the transaction is verified by the network, the message will
40 be updated automatically.
41 </span>
42 ),
43 }
44 } catch (error) {
45 return {
46 status: "😥 " + error.message,
47 }
48 }
49}
Tampilkan semua

Terakhir namun tidak kalah pentingnya, kita perlu menghubungkan fungsi updateMessage kita ke komponen HelloWorld.js kita.

Hubungkan updateMessage ke frontend HelloWorld.js

Fungsi onUpdatePressed kita harus melakukan panggilan await ke fungsi updateMessage yang diimpor dan memodifikasi variabel status status untuk mencerminkan apakah transaksi kita berhasil atau gagal:

1// HelloWorld.js // HelloWorld.js
2
3const onUpdatePressed = async () => {
4 const { status } = await updateMessage(walletAddress, newMessage)
5 setStatus(status)
6}

Ini sangat bersih dan sederhana. Dan tebak apa... DAPP ANDA SELESAI!!!

Silakan dan uji tombol Update!

Buat dapp kustom Anda sendiri

Wooooo, Anda berhasil mencapai akhir tutorial! Sebagai rekap, Anda telah mempelajari cara:

  • Menghubungkan dompet MetaMask ke proyek dapp Anda
  • Membaca data dari kontrak pintar Anda menggunakan API Alchemy Web3 (opens in a new tab)
  • Menandatangani transaksi Ethereum menggunakan MetaMask

Sekarang Anda sepenuhnya siap untuk menerapkan keterampilan dari tutorial ini untuk membangun proyek dapp kustom Anda sendiri! Seperti biasa, jika Anda memiliki pertanyaan, jangan ragu untuk menghubungi kami untuk mendapatkan bantuan di Discord Alchemy (opens in a new tab). 🧙‍♂️

Setelah Anda menyelesaikan tutorial ini, beri tahu kami bagaimana pengalaman Anda atau jika Anda memiliki umpan balik dengan menandai kami di Twitter @alchemyplatform (opens in a new tab)!

Pembaruan terakhir halaman: 26 Februari 2026

Apakah tutorial ini membantu?