Kontrak Sederhana

Bab ini akan memperkenalkan dasar-dasar kontrak Starknet dengan contoh kontrak dasar. Anda akan belajar bagaimana menulis kontrak sederhana yang menyimpan sebuah angka tunggal di blockchain.

Anatomi dari Kontrak Starknet Sederhana

Mari pertimbangkan kontrak berikut untuk menjelaskan dasar-dasar kontrak Starknet. Mungkin tidak mudah untuk memahaminya sekaligus, tetapi kita akan menjelaskannya langkah demi langkah:

{{#include ../listings/ch99-starknet-smart-contracts/listing_99_01/src/lib.cairo:all}}

Listing 99-1: Kontrak penyimpanan sederhana

Catatan: Kontrak Starknet didefinisikan dalam modul.

Apa maksudnya kontrak ini?

Pada contoh ini, Storage struct mendeklarasikan variabel penyimpanan yang disebut stored_data dengan tipe u128 (bilangan bulat tak bertanda 128 bit). Anda dapat memandangnya sebagai sebuah slot tunggal dalam sebuah database yang dapat Anda panggil dan ubah dengan memanggil fungsi dari kode yang mengelola database tersebut. Kontrak ini mendefinisikan dan mengekspos secara publik fungsi-fungsi set dan get yang dapat digunakan untuk memodifikasi atau mengambil nilai dari variabel tersebut.

Antarmuka: desain kontrak

{{#include ../listings/ch99-starknet-smart-contracts/listing_99_01/src/lib.cairo:interface}}

Antarmuka kontrak mewakili fungsi-fungsi yang kontrak ini tawarkan kepada dunia luar. Di sini, antarmuka mengekspos dua fungsi: set dan get. Dengan memanfaatkan mekanisme traits & impls dari Cairo, kita dapat memastikan bahwa implementasi sebenarnya dari kontrak sesuai dengan antarmukanya. Bahkan, Anda akan mendapatkan kesalahan kompilasi jika kontrak Anda tidak sesuai dengan antarmuka yang dinyatakan.

{{#include ../listings/ch99-starknet-smart-contracts/listing_99_01_bis_wrong_impl/src/lib.cairo:impl}}

Listing 99-1-bis: Implementasi yang salah dari antarmuka kontrak. Ini tidak dapat dikompilasi.

Pada antarmuka, perhatikan tipe generik TContractState dari argumen self yang dilewatkan secara referensi ke fungsi set. Parameter self mewakili status kontrak. Melihat argumen self yang dilewatkan ke set memberi tahu kita bahwa fungsi ini mungkin mengakses status kontrak, karena itulah yang memberi kita akses ke penyimpanan kontrak. Modifikator ref mengimplikasikan bahwa self dapat dimodifikasi, yang berarti variabel penyimpanan kontrak dapat dimodifikasi di dalam fungsi set.

Di sisi lain, get mengambil snapshot dari TContractState, yang segera memberi tahu kita bahwa ini tidak mengubah status (dan memang, kompiler akan mengeluh jika kita mencoba mengubah penyimpanan di dalam fungsi get).

Fungsi publik didefinisikan dalam blok implementasi

Sebelum kita menjelajahi lebih jauh, mari tentukan beberapa terminologi.

  • Dalam konteks Starknet, fungsi publik adalah fungsi yang diakses oleh dunia luar. Dalam contoh di atas, set dan get adalah fungsi publik. Fungsi publik dapat dipanggil oleh siapa pun, dan dapat dipanggil dari luar kontrak atau dari dalam kontrak. Dalam contoh di atas, set dan get adalah fungsi publik.

  • Apa yang kita sebut sebagai fungsi eksternal adalah fungsi publik yang dipanggil melalui transaksi dan dapat mengubah status kontrak. set adalah fungsi eksternal.

  • Fungsi view adalah fungsi publik yang dapat dipanggil dari luar kontrak, tetapi tidak dapat mengubah status kontrak. get adalah fungsi view.

{{#include ../listings/ch99-starknet-smart-contracts/listing_99_01/src/lib.cairo:impl}}

Karena antarmuka kontrak didefinisikan sebagai trait ISimpleStorage, untuk sesuai dengan antarmuka, fungsi eksternal kontrak harus didefinisikan dalam implementasi dari trait ini — yang memungkinkan kita memastikan bahwa implementasi kontrak sesuai dengan antarmukanya.

Namun, hanya mendefinisikan fungsi-fungsi dalam implementasi belum cukup. Blok implementasi harus dianotasi dengan atribut #[external(v0)]. Atribut ini mengekspos fungsi-fungsi yang didefinisikan dalam implementasi ini ke dunia luar — lupakan untuk menambahkannya dan fungsi-fungsi Anda tidak dapat dipanggil dari luar. Semua fungsi yang didefinisikan dalam blok yang ditandai dengan #[external(v0)] secara konsekuensial adalah fungsi publik.

Saat menulis implementasi antarmuka, parameter generik yang sesuai dengan argumen self dalam trait harus ContractState. Tipe ContractState dihasilkan oleh kompiler, dan memberikan akses ke variabel penyimpanan yang didefinisikan dalam struktur Storage. Selain itu, ContractState memberikan kita kemampuan untuk menghasilkan acara (events). Nama ContractState tidak mengherankan, karena ini adalah representasi dari status kontrak, yang merupakan apa yang kita pikirkan sebagai self dalam trait antarmuka kontrak.

Memodifikasi Status Kontrak

Seperti yang dapat Anda perhatikan, semua fungsi yang perlu mengakses status kontrak didefinisikan dalam implementasi dari suatu trait yang memiliki parameter generik TContractState, dan mengambil parameter self: ContractState. Ini memungkinkan kita secara eksplisit melewatkan parameter self: ContractState ke fungsi, memungkinkan akses ke variabel penyimpanan kontrak. Untuk mengakses variabel penyimpanan dari kontrak saat ini, Anda menambahkan awalan self ke nama variabel penyimpanan, yang memungkinkan Anda menggunakan metode read dan write untuk membaca atau menulis nilai variabel penyimpanan.

{{#include ../listings/ch99-starknet-smart-contracts/listing_99_01/src/lib.cairo:write_state}}

Menggunakan self dan metode write untuk memodifikasi nilai variabel penyimpanan

Catatan: jika status kontrak dilewatkan sebagai snapshot daripada ref, upaya untuk memodifikasinya akan menghasilkan kesalahan kompilasi.

Kontrak ini belum melakukan banyak hal selain memungkinkan siapa pun menyimpan satu angka yang dapat diakses oleh siapa pun di dunia. Siapa pun bisa memanggil set lagi dengan nilai yang berbeda dan menimpa angka Anda, tetapi angka tersebut masih tersimpan dalam riwayat blockchain. Nanti, Anda akan melihat bagaimana Anda dapat memberlakukan pembatasan akses agar hanya Anda yang dapat mengubah angka tersebut.