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
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:
// 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
Deskripsi | Lisk L1 | Lisk L2 |
---|---|---|
Logika bisnis onchain | Modul | Smart contract |
Penyimpanan data onchain | Stores (onchain) | Variabel state |
Pencatatan di blockchain | Blockchain Events | Events |
Logika transisi status yang dipicu oleh transaksi | Perintah | Fungsi |
API | Endpoint | Fungsi pandang |
API Internal | Metode | Fungsi (+ modifikator) |
Logika yang dipicu per blok | Lifecycle Hooks | X1 |
Penyimpanan
Migrasikan penyimpanan onchain dari sebuah modul dengan mengimplementasikan variabel state yang sesuai dalam kontrak seperti yang ditunjukkan di bawah ini.
- Lisk L1
- Lisk L2
- Message Store
- Counter Store
import { BaseStore } from 'lisk-sdk';
export interface MessageStoreData {
message: string;
}
export const messageStoreSchema = {
$id: '/hello/message',
type: 'object',
required: ['message'],
properties: {
message: {
dataType: 'string',
fieldNumber: 1,
},
},
};
export class MessageStore extends BaseStore<MessageStoreData> {
public schema = messageStoreSchema;
}
import { BaseStore } from 'lisk-sdk';
export interface CounterStoreData {
counter: number;
}
export const counterKey = Buffer.alloc(0);
export const counterStoreSchema = {
$id: '/hello/counter',
type: 'object',
required: ['counter'],
properties: {
counter: {
dataType: 'uint32',
fieldNumber: 1,
},
},
};
export class CounterStore extends BaseStore<CounterStoreData> {
public schema = counterStoreSchema;
}
// 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.
- Lisk L1
- Lisk L2
import { BaseEvent } from 'lisk-sdk';
export const newHelloEventSchema = {
$id: '/hello/events/new_hello',
type: 'object',
required: ['senderAddress', 'message'],
properties: {
senderAddress: {
dataType: 'bytes',
fieldNumber: 1,
},
message: {
dataType: 'string',
fieldNumber: 2,
},
},
};
export interface NewHelloEventData {
senderAddress: Buffer;
message: string;
}
export class NewHelloEvent extends BaseEvent<NewHelloEventData> {
public schema = newHelloEventSchema;
}
// 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.
// 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.
// 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.
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()
.
// 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:
// 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.
// 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.
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.
// 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);
}
- Lisk L1
- Lisk L2
/* eslint-disable class-methods-use-this */
import {
BaseCommand,
CommandVerifyContext,
CommandExecuteContext,
VerificationResult,
VerifyStatus,
} from 'lisk-sdk';
import { createHelloSchema } from '../schema';
import { MessageStore } from '../stores/message';
import { counterKey, CounterStore, CounterStoreData } from '../stores/counter';
import { ModuleConfig } from '../types';
import { NewHelloEvent } from '../events/new_hello';
interface Params {
message: string;
}
export class CreateHelloCommand extends BaseCommand {
public schema = createHelloSchema;
private _blacklist!: string[];
// eslint-disable-next-line @typescript-eslint/require-await
public async init(config: ModuleConfig): Promise<void> {
// Set _blacklist to the value of the blacklist defined in the module config
this._blacklist = config.blacklist;
// Set the max message length to the value defined in the module config
this.schema.properties.message.maxLength = config.maxMessageLength;
// Set the min message length to the value defined in the module config
this.schema.properties.message.minLength = config.minMessageLength;
}
// eslint-disable-next-line @typescript-eslint/require-await
public async verify(context: CommandVerifyContext<Params>): Promise<VerificationResult> {
let validation: VerificationResult;
const wordList = context.params.message.split(" ");
const found = this._blacklist.filter(value => wordList.includes(value));
if (found.length > 0) {
context.logger.info("==== DITEMUKAN: Pesan mengandung kata yang ada di daftar hitam ====");
throw new Error(
`Kata ilegal dalam pesan hello: ${ found.toString()}`
);
} else {
context.logger.info("==== TIDAK DITEMUKAN: Pesan tidak mengandung kata yang ada di daftar hitam ====");
validation = {
status: VerifyStatus.OK
};
}
return validation;
}
public async execute(context: CommandExecuteContext<Params>): Promise<void> {
// 1. Mendapatkan data akun pengirim transaksi Hello.
const { senderAddress } = context.transaction;
// 2. Mendapatkan store pesan dan penghitung.
const messageSubstore = this.stores.get(MessageStore);
const counterSubstore = this.stores.get(CounterStore);
// 3. Menyimpan pesan Hello ke store pesan, menggunakan senderAddress sebagai kunci, dan pesan sebagai nilai.
await messageSubstore.set(context, senderAddress, {
message: context.params.message,
});
// 3. Mendapatkan penghitung Hello dari store penghitung.
let helloCounter: CounterStoreData;
try {
helloCounter = await counterSubstore.get(context, counterKey);
} catch (error) {
helloCounter = {
counter: 0,
}
}
// 5. Menambah penghitung Hello sebanyak +1.
helloCounter.counter+=1;
// 6. Menyimpan penghitung Hello ke store penghitung.
await counterSubstore.set(context, counterKey, helloCounter);
// 7. Memancarkan acara "New Hello"
const newHelloEvent = this.events.get(NewHelloEvent);
newHelloEvent.add(context, {
senderAddress: context.transaction.senderAddress,
message: context.params.message
},[context.transaction.senderAddress]);
}
}
// 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.
- Lisk L1
- Lisk L2
export class HelloEndpoint extends BaseEndpoint {
public async getHelloCounter(ctx: ModuleEndpointContext): Promise<CounterStoreData> {
const counterSubStore = this.stores.get(CounterStore);
const helloCounter = await counterSubStore.get(
ctx,
counterKey,
);
return helloCounter;
}
public async getHello(ctx: ModuleEndpointContext): Promise<MessageStoreData> {
const messageSubStore = this.stores.get(MessageStore);
const { address } = ctx.params;
if (typeof address !== 'string') {
throw new Error('Parameter address must be a string.');
}
cryptography.address.validateLisk32Address(address);
const helloMessage = await messageSubStore.get(
ctx,
cryptography.address.getAddressFromLisk32Address(address),
);
return helloMessage;
}
}
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.
- Lisk L1
- Lisk L2
export class HelloEndpoint extends BaseEndpoint {
public async getHelloCounter(ctx: ModuleEndpointContext): Promise<CounterStoreData> {
const counterSubStore = this.stores.get(CounterStore);
const helloCounter = await counterSubStore.get(
ctx,
counterKey,
);
return helloCounter;
}
public async getHello(ctx: ModuleEndpointContext): Promise<MessageStoreData> {
const messageSubStore = this.stores.get(MessageStore);
const { address } = ctx.params;
if (typeof address !== 'string') {
throw new Error('Parameter address harus berupa string.');
}
cryptography.address.validateLisk32Address(address);
const helloMessage = await messageSubStore.get(
ctx,
cryptography.address.getAddressFromLisk32Address(address),
);
return helloMessage;
}
}
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
// 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.
- Lisk
- Lisk Sepolia
forge create --rpc-url https://rpc.api.lisk.com \
--etherscan-api-key 123 \
--verify \
--verifier blockscout \
--verifier-url https://blockscout.lisk.com/api \
--private-key <your-private-key> \
src/Hello.sol:Hello
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
-
Tidak ada padanan langsung di Solidity.
Harap teliti solusi kustom untuk memigrasikan logika yang ada di lifecycle hooks. ↩