Lewati ke konten utama
peringatan

Dokumentasi ini merujuk pada chain Lisk sebelumnya di L1, yang dihentikan saat Lisk bermigrasi ke L2 pada Mei 2024.
Jika Anda ingin mengembangkan di chain Lisk L2 yang saat ini, harap merujuk ke dokumentasi pengembang di bawah kategori Membangun di Lisk.

Panduan Migrasi Lisk L1->L2

Cara memigrasikan aplikasi Lisk L1 ke Lisk L2 dengan lancar.

Persyaratan

Anda membutuhkan:

  • Aplikasi Lisk L1 yang dibangun di Lisk SDK versi 6.0.0 atau lebih baru.
  • Pemahaman dasar tentang Solidity.
  • Kerangka pengembangan smart contract pilihan Anda.
    Dalam panduan ini, kami akan menggunakan kerangka Foundry.

Pengaturan Proyek

Untuk menggambarkan proses migrasi, kami akan menggunakan modul Hello dari Lisk L1, dan memigrasikannya ke Lisk L2.

Untuk memulai migrasi proyek, buat proyek baru dengan Foundry seperti ini:

forge init hello_liskl2

Ini akan membuat folder baru hello_liskl2, yang akan berisi smart contract yang akan kami implementasikan.

cd hello_liskl2

Migrasi Modul

info

Modul di Lisk L1 diimplementasikan ulang sebagai smart contract di Lisk L2.

Untuk membuat smart contract baru, buat file baru Hello.sol di bawah src/ dan tambahkan konten berikut:

hello_liskl2/src/Hello.sol
// SPDX-License-Identifier: MIT
// versi compiler harus lebih besar dari atau sama dengan 0.8.20 dan kurang dari 0.9.0
pragma solidity ^0.8.20;

contract Hello {

}

Di dalam kontrak baru ini, kami akan menempatkan semua logika yang sebelumnya ada di modul Lisk L1 Hello.

Tabel: Perbandingan Lisk L1/L2

DeskripsiLisk L1Lisk L2
Logika bisnis onchainModulSmart contract
Penyimpanan data onchainStores (onchain)Variabel state
Pencatatan di blockchainBlockchain EventsEvents
Logika transisi status yang dipicu oleh transaksiPerintahFungsi
APIEndpointFungsi pandang
API InternalMetodeFungsi (+ modifikator)
Logika yang dipicu per blokLifecycle HooksX1

Penyimpanan

Migrasikan penyimpanan onchain dari sebuah modul dengan mengimplementasikan variabel state yang sesuai dalam kontrak seperti yang ditunjukkan di bawah ini.

hello_liskl2/src/Hello.sol
// SPDX-License-Identifier: MIT  
// versi compiler harus lebih besar dari atau sama dengan 0.8.20 dan kurang dari 0.9.0
pragma solidity ^0.8.20;

contract Hello {
/** Variabel state */
// Variabel state untuk pesan Hello
mapping(address => string) public message;
// Variabel state untuk penghitung pesan
uint32 public counter = 0;
}

Event

Migrasikan event blockchain dari modul dengan mengimplementasikan event yang sesuai dalam kontrak seperti yang ditunjukkan di bawah ini.

hello_liskl2/src/Hello.sol
// SPDX-License-Identifier: MIT
// versi compiler harus lebih besar dari atau sama dengan 0.8.20 dan kurang dari 0.9.0
pragma solidity ^0.8.20;

contract Hello {
/** Variabel state */
// Variabel state untuk pesan Hello
mapping(address => string) public message;
// Variabel state untuk penghitung pesan
uint32 public counter = 0;

/** Event */
// Event untuk pesan Hello baru
event NewHello(address indexed sender, string message);
}

Logika Transisi Status

Migrasi Konfigurasi

Konfigurasi

Konfigurasi spesifik modul, yang sebelumnya berada di config.json pada Lisk L1, sekarang menjadi bagian dari smart contract itu sendiri dan didefinisikan sebagai variabel state.

hello_liskl2/src/Hello.sol
// Daftar kata yang tidak diperbolehkan dalam pesan Hello
string[] public blacklist = ["word1","word2"];
// Panjang maksimum pesan Hello
uint32 public maxLength = 200;
// Panjang minimum pesan Hello
uint32 public minLength = 3;

Untuk mengedit opsi konfigurasi modul Hello, kita implementasikan fungsi-fungsi berikut dalam kontrak Hello:

  • setBlacklist() untuk mengonfigurasi daftar hitam kata-kata yang tidak diperbolehkan dalam pesan Hello.
  • setMinMaxMessageLength() untuk mengonfigurasi panjang minimum dan maksimum pesan Hello.
hello_liskl2/src/Hello.sol
// Fungsi untuk mengonfigurasi daftar hitam
function setBlacklist(string[] memory _newBlackList) public onlyOwner {
blacklist = _newBlackList;
}
// Fungsi untuk mengonfigurasi panjang pesan min/maks
function setMinMaxMessageLength(uint32 _newMinLength,uint32 _newMaxLength) public onlyOwner {
minLength = _newMinLength;
maxLength = _newMaxLength;
}

Seperti yang terlihat pada cuplikan kode di atas, kita menambahkan modifier berikut pada fungsi-fungsi tersebut:

  • public untuk membuat fungsi dapat dipanggil dari luar kontrak. Ini adalah modifier visibilitas default untuk fungsi dalam Solidity.
  • onlyOwner untuk memeriksa apakah pemanggil adalah pemilik kontrak. Ini adalah modifier kustom yang perlu kita implementasikan secara manual dalam kontrak, seperti yang ditunjukkan pada contoh di bawah ini.

Untuk mengatur pemilik kontrak, kita menambahkan variabel state baru owner, dan sebuah konstruktor yang mengatur variabel owner ke alamat akun yang deploy kontrak.

tip

Untuk memperbarui pemilik smart contract, Anda dapat mengimplementasikan fungsi yang sesuai setOwner(), dan menggunakan modifier onlyOwner untuk memastikan hanya pemilik saat ini yang dapat memanggil fungsi ini.

Terakhir, kita dapat memeriksa apakah pengirim pesan adalah pemilik kontrak dalam modifier onlyOwner yang digunakan untuk fungsi setBlacklist() dan setMinMaxMessageLength().

hello_liskl2/src/Hello.sol
// Alamat pemilik kontrak
address public immutable owner;

constructor() {
// Menetapkan pengirim transaksi sebagai pemilik kontrak.
owner = msg.sender;
}

/** Modifiers */
// Modifier untuk memeriksa apakah pemanggil adalah pemilik kontrak.
modifier onlyOwner() {
require(msg.sender == owner, "Bukan pemilik");
_;
}
Verifikasi Migrasi

Verifikasi

Untuk memverifikasi pesan Hello, kita implementasikan modifier kustom dalam kontrak.

Di dalam modifier, kita memeriksa panjang pesan dan apakah pesan tersebut mengandung kata-kata yang ada dalam daftar hitam seperti yang dilakukan dalam metode verify() dari modul Hello Lisk L1.

Untuk memeriksa panjang pesan Hello, kita implementasikan modifier validLength seperti ini:

hello_liskl2/src/Hello.sol
// Validasi panjang pesan
modifier validLength(string memory _message) {
require(bytes(_message).length >= minLength, "Pesan terlalu pendek");
require(bytes(_message).length <= maxLength, "Pesan terlalu panjang");
_;
}

Untuk memeriksa apakah pesan mengandung kata-kata yang ada dalam daftar hitam, kita implementasikan modifier validWords dalam kontrak.

hello_liskl2/src/Hello.sol
// Validasi konten pesan
modifier validWords(string memory _message) {
bytes memory whereBytes = bytes (_message);

for (uint h = 0; h < blacklist.length; h++) {
bool found = false;
bytes memory whatBytes = bytes (blacklist[h]);
for (uint i = 0; i <= whereBytes.length - whatBytes.length; i++) {
bool flag = true;
for (uint j = 0; j < whatBytes.length; j++)
if (whereBytes [i + j] != whatBytes [j]) {
flag = false;
break;
}
if (flag) {
found = true;
break;
}
}
require (!found, "Pesan mengandung kata yang ada dalam daftar hitam");
}
_;
}
Eksekusi Migrasi

Eksekusi

Untuk memigrasikan eksekusi perintah createHello, kita implementasikan fungsi createHello() dalam kontrak.

Di dalam fungsi ini, kita menyimpan pesan pengirim di dalam pemetaan message dengan alamat pengirim sebagai kunci.

tip

Alamat pengirim adalah variabel global dalam Solidity dan dapat diakses dengan msg.sender.

Selain itu, kita meningkatkan penghitung pesan Hello sebanyak 1 dan memancarkan event NewHello, seperti yang dilakukan dalam metode execute() pada modul Lisk L1 Hello sebelumnya.

Modifier validMessage() yang kita definisikan di atas dalam bagian Migrasi Verifikasi digunakan untuk memeriksa apakah pesan valid sebelum fungsi createHello() dijalankan.

hello_liskl2/src/Hello.sol
// Fungsi untuk membuat pesan Hello baru
function createHello(string calldata _message) public validLength(_message) validWords(_message) {
message[msg.sender] = _message;
counter+=1;
emit NewHello(msg.sender, _message);
}
hello_liskl2/src/Hello.sol
// SPDX-License-Identifier: MIT
// versi compiler harus lebih besar dari atau sama dengan 0.8.20 dan kurang dari 0.9.0
pragma solidity ^0.8.20;

contract Hello {
/** Variabel state */
// Variabel state untuk pesan Hello
mapping(address => string) public message;
// Variabel state untuk penghitung pesan
uint32 public counter = 0;
// Alamat pemilik kontrak
address public immutable owner;
// Daftar hitam kata-kata yang tidak diperbolehkan dalam pesan Hello
string[] public blacklist = ["word1","word2"];
// Panjang maksimum pesan Hello
uint32 public maxLength = 200;
// Panjang minimum pesan Hello
uint32 public minLength = 3;

constructor() {
// Menetapkan pengirim transaksi sebagai pemilik kontrak.
owner = msg.sender;
}

/** Modifier */
// Modifier untuk memeriksa bahwa pemanggil adalah pemilik kontrak.
modifier onlyOwner() {
require(msg.sender == owner, "Bukan pemilik");
_;
}
// Validasi panjang pesan
modifier validLength(string memory _message) {
require(bytes(_message).length >= minLength, "Pesan terlalu pendek");
require(bytes(_message).length <= maxLength, "Pesan terlalu panjang");
_;
}
// Validasi isi pesan
modifier validWords(string memory _message) {
bytes memory whereBytes = bytes (_message);

for (uint h = 0; h < blacklist.length; h++) {
bool found = false;
bytes memory whatBytes = bytes (blacklist[h]);
for (uint i = 0; i <= whereBytes.length - whatBytes.length; i++) {
bool flag = true;
for (uint j = 0; j < whatBytes.length; j++)
if (whereBytes [i + j] != whatBytes [j]) {
flag = false;
break;
}
if (flag) {
found = true;
break;
}
}
require (!found, "Pesan mengandung kata yang ada di daftar hitam");
}
_;
}

/** Acara */
// Acara untuk pesan Hello baru
event NewHello(address indexed sender, string message);

/** Fungsi */
// Fungsi untuk mengonfigurasi daftar hitam
function setBlacklist(string[] memory _newBlackList) public onlyOwner {
blacklist = _newBlackList;
}
// Fungsi untuk mengonfigurasi panjang pesan min/maks
function setMinMaxMessageLength(uint32 _newMinLength,uint32 _newMaxLength) public onlyOwner {
minLength = _newMinLength;
maxLength = _newMaxLength;
}
// Fungsi untuk membuat pesan Hello baru
function createHello(string calldata _message) public validLength(_message) validWords(_message) {
message[msg.sender] = _message;
counter+=1;
emit NewHello(msg.sender, _message);
}
}

Endpoint

Migrasikan endpoint modul dengan mengimplementasikan fungsi [view] yang sesuai dalam kontrak seperti yang ditunjukkan di bawah ini.

Untuk getter sederhana, cukup tambahkan modifier visibilitas public pada variabel state (lihat penyimpanan).

Variabel state publik dapat diakses langsung oleh pihak eksternal, tanpa perlu mengimplementasikan fungsi view yang sesuai.

Endpoint

Migrasikan endpoint modul dengan mengimplementasikan fungsi view yang sesuai dalam kontrak seperti yang ditunjukkan di bawah ini.

Untuk getter sederhana, cukup tambahkan visibility modifier public pada variabel state (lihat penyimpanan).

Variabel state publik dapat diakses langsung oleh pihak eksternal, tanpa perlu mengimplementasikan view function yang sesuai.

Langkah Selanjutnya

Sekarang kita telah mengimplementasikan kembali modul Hello dari Lisk L1 sebagai smart contract di Lisk L2, maka kita bisa langsung deploy kontrak Hello ke Lisk L2 dan berinteraksi dengannya.

Sebelum deploy smart contract ke Lisk, disarankan untuk menguji secara lokal dengan menulis uji yang sesuai untuk smart contract yang baru dibuat. Setelah smart contract di-deploy ke Lisk, Anda dapat berinteraksi dengannya dengan memanggil fungsi publiknya.

Terakhir, Anda dapat memigrasikan plugin dan UI dari aplikasi Hello Lisk L1 agar kompatibel dengan API baru, untuk menyelesaikan proses migrasi aplikasi Lisk Anda.

Menguji Smart Contract

Dengan menguji smart contract, Anda dapat memverifikasi bahwa smart contract berfungsi seperti yang diharapkan dan bebas dari bug, sebelum deploynya ke Lisk.

Foundry menyediakan framework pengujian untuk mendukung Anda dalam menulis uji untuk smart contract. Lihat Tests - Foundry Book untuk contoh dan referensi terkait framework pengujian.

Untuk menguji smart contract Hello, buat file baru Hello.t.sol di bawah test/, dan tambahkan konten berikut:

Hello.t.sol
hello_liskl2/test/Hello.t.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Test, console} from "forge-std/Test.sol";
import {Hello} from "../src/Hello.sol";

contract HelloTest is Test {
Hello public hello;
address alice = makeAddr("alice");
event NewHello(address indexed sender, string message);

function setUp() public {
hello = new Hello();
}

function test_CreateHello() public {
string memory message = "Hello World";
// Mengharapkan event NewHello
vm.expectEmit(true,false,false,false);
emit NewHello(address(alice), message);
// Membuat pesan Hello baru
hoax(alice, 100 ether);
hello.createHello(message);
// Memeriksa pesan
assertEq(hello.message(alice),message);
// Memeriksa apakah counter = 1
assertEq(hello.counter(),1);
}

function test_MinLength() public {
vm.expectRevert("Message too short");
hello.createHello("Hi");
}

function test_MaxLength() public {
vm.expectRevert("Message too long");
hello.createHello("Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean porta neque eget elit tristique pharetra. Pellentesque tempus sollicitudin tortor, ut tempus diam. Nulla facilisi. Donec at neque sapien.");
}

function test_Blacklist() public {
vm.expectRevert("Message contains blacklisted word");
hello.createHello("Hello word1");
}

function test_SetBlacklist() public {
// Membuat array dinamis sementara untuk string
string bl[1] = "word3";
bl[2] = "word4";
hello.setBlacklist(bl);
string[] memory getBL = new string[acklist(0);
getBL[1] = hello.blacklist(1);
assertEq(getBL[0], bl[0]);
assertEq(getBL[1], bl[1]);
}

function test_SetBlacklistNotOwner() public {
string[] memory bl = new string[](3) ;
bl[0] = "word1";
bl[2] = "word4";
vm.expectRevert("Not owner");
hoax(alice, 100 ether);
hello.setBlacklist(bl);
}

function test_SetMinMaxMessageLength() public {
uint32 newMin = 1;
uint32 newMax = 500;
hello.setMinMaxMessageLength(newMin,newMax);
assertEq(hello.minLength(), newMin);
assertEq(hello.maxLength(), newMax);
}

function test_SetMinMaxMessageLengthNotOwner() public {
uint32 newMin = 1;
uint32 newMax = 500;
hoax(alice, 100 ether);
vm.expectRevert();
hello.setMinMaxMessageLength(newMin,newMax);
}
}

Untuk menjalankan pengujian, jalankan perintah berikut:

forge test

Output-nya seharusnya terlihat seperti ini:

Running 8 tests for test/Hello.t.sol:HelloTest
[PASS] test_Blacklist() (gas: 23772)
[PASS] test_CreateHello() (gas: 66179)
[PASS] test_MaxLength() (gas: 14179)
[PASS] test_MinLength() (gas: 13929)
[PASS] test_SetBlacklist() (gas: 885276)
[PASS] test_SetBlacklistNotOwner() (gas: 16978)
[PASS] test_SetMinMaxMessageLength() (gas: 853243)
[PASS] test_SetMinMaxMessageLengthNotOwner() (gas: 10889)
Test result: ok. 8 passed; 0 failed; 0 skipped; finished in 3.35ms

Deployment Smart Contract

Sekarang Anda dapat deploy smart contract ke Lisk. Untuk contoh ini, kami akan menggunakan jaringan Lisk Sepolia untuk deploy kontrak Hello. Namun, langkah-langkahnya sama untuk jaringan Lisk juga.

Tambahkan flag --verify ke perintah forge create untuk langsung memverifikasi smart contract di BlockScout.

forge create --rpc-url https://rpc.sepolia-api.lisk.com \
--etherscan-api-key 123 \
--verify \
--verifier blockscout \
--verifier-url https://sepolia-blockscout.lisk.com/api \
--private-key <your-private-key> \
src/Hello.sol:Hello

Jika deployment berhasil, keluaran seharusnya terlihat seperti ini:

[⠢] Compiling...
No files changed, compilation skipped
Deployer: 0x3C46A11471f285E36EE8d089473ce98269D1b081
Deployed to: 0x0a5A1C81F278cAe80d340a4A97E2D7B1c3Ec511a
Transaction hash: 0x52bb6aab8ceeecef674253ecc0ccfe35baeac7db3cc8e889a9da1f7cf1ce0593
Starting contract verification...
Waiting for blockscout to detect contract deployment...
Start verifying contract `0x0a5A1C81F278cAe80d340a4A97E2D7B1c3Ec511a` deployed on 4202

Submitting verification for [src/Hello.sol:Hello] 0x0a5A1C81F278cAe80d340a4A97E2D7B1c3Ec511a.
Submitted contract for verification:
Response: `OK`
GUID: `0a5a1c81f278cae80d340a4a97e2d7b1c3ec511a65cf6f72`
URL: https://sepolia-blockscout.lisk.com/address/0x0a5a1c81f278cae80d340a4a97e2d7b1c3ec511a
Contract verification status:
Response: `OK`
Details: `Pending in queue`
Contract verification status:
Response: `OK`
Details: `Pass - Verified`
Contract successfully verified

Setelah smart contract di-deploy, Anda dapat berinteraksi dengannya dengan memanggil fungsi publiknya.

Dari sini, Anda dapat memigrasi plugin dan UI aplikasi Lisk L1 agar kompatibel dengan API baru, untuk menyelesaikan proses migrasi aplikasi Lisk Anda.

Jika Anda memerlukan bantuan lebih lanjut, jangan ragu untuk menghubungi komunitas Lisk di Lisk.chat.

Footnotes

  1. Tidak ada padanan langsung di Solidity.
    Harap teliti solusi kustom untuk memigrasikan logika yang ada di lifecycle hooks.