Praktik: Membuat Escrow Smart Contract
Selamat datang di sesi praktik. Kita akan membuat Escrow Smart Contract sederhana di Blockchain Sui.
Apa yang Akan Dipelajari
- Konsep escrow dan cara kerjanya di blockchain
- Menyiapkan proyek Move dari awal
- Memahami struktur dan fungsi smart contract escrow
- Membuat dan menggunakan mock coin untuk testing
- Langkah-langkah build, publish, dan testing smart contract
- Tips troubleshooting dan debugging
1. Konsep Escrow
1.1. Apa itu Escrow?
Escrow adalah sistem perantara yang aman untuk transaksi antara dua pihak. Bayangkan kamu ingin swap token A dengan token B:
- Penjual memiliki token A dan ingin menukarnya dengan token B
- Pembeli memiliki token B dan ingin menukarnya dengan token A
- Kedua pihak ingin memastikan transaksi aman tanpa risiko rug pull
Escrow menyediakan solusi netral:
- Penjual menaruh token A (deposit) ke kotak aman (escrow)
- Pembeli menyerahkan token B (pembayaran) ke kotak aman
- Setelah semuanya cocok, pembeli menerima token A dan penjual mengambil token B
- Jika ada masalah, penjual bisa membatalkan dan menarik deposit kembali
Di blockchain, alur ini dijalankan otomatis oleh smart contract tanpa campur tangan manusia.
Contoh Real-World:
- Swap TBTC dengan zSUI
- Swap USDC dengan SUI
- Swap token A dengan token B (cross-chain atau same-chain)
1.2. Alur Escrow di Sui
-
Penjual membuat escrow:
- Menaruh token A (DepositCoinType) sebagai deposit
- Menentukan jumlah token B (PaymentCoinType) yang diminta
- Escrow object disimpan di blockchain
-
Pembeli menerima escrow:
- Mengirim token B (PaymentCoinType) sesuai jumlah yang diminta
- Langsung menerima token A (DepositCoinType) dari escrow
-
Penjual menarik pembayaran:
- Setelah pembeli membayar, penjual bisa menarik token B (PaymentCoinType) kapan saja
-
Pembatalan (opsional):
- Penjual dapat membatalkan escrow jika belum ada pembayaran
- Deposit token A dikembalikan ke penjual
2. Persiapan Proyek
2.1. Membuat Proyek Baru
Buat folder proyek baru dengan perintah berikut:
sui move new escrow_contract
Masuk ke folder proyek:
cd escrow_contract
2.2. Konfigurasi Move.toml
Buka file Move.toml dan isi dengan konfigurasi berikut:
[package]
name = "escrow"
version = "0.0.1"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
[addresses]
simple_escrow = "0x0"
mock_tbtc = "0x0"
mock_zsui = "0x0"
2.3. Membuat File Source
Buat tiga file berikut di folder sources/:
sources/simple_escrow.movesources/mock_tbtc.movesources/mock_zsui.move
Salin kode dari bagian berikutnya ke file-file tersebut.
2.4. Build Proyek
Jalankan build untuk memastikan proyek valid:
sui move build
Jika build berhasil, Move compiler akan menampilkan pesan sukses.
3. Kode Smart Contract
3.1. Modul simple_escrow
Buat file sources/simple_escrow.move dan salin kode berikut:
module escrow::simple_escrow {
use sui::balance::{Self, Balance};
use sui::coin::{Self, Coin};
use sui::object::{Self, UID};
use sui::transfer;
use sui::tx_context::TxContext;
/// Escrow untuk swap antara dua tipe koin berbeda.
public struct Escrow<phantom DepositCoinType, phantom PaymentCoinType> has key, store {
id: UID,
deposit: Balance<DepositCoinType>,
requested_amount: u64,
receive: Balance<PaymentCoinType>,
creator: address,
}
/// Seller deposit koin dan tentukan jumlah pembayaran yang diminta.
public entry fun create_escrow<DepositCoinType, PaymentCoinType>(
deposit_coin: Coin<DepositCoinType>,
request_amount: u64,
ctx: &mut TxContext,
) {
let escrow = Escrow<DepositCoinType, PaymentCoinType> {
id: object::new(ctx),
deposit: coin::into_balance(deposit_coin),
requested_amount: request_amount,
receive: balance::zero(),
creator: ctx.sender(),
};
transfer::public_transfer(escrow, ctx.sender());
}
/// Buyer bayar dan terima deposit.
/// Buyer bisa kirim coin >= requested_amount, sisa akan di-refund otomatis.
public entry fun accept_escrow<DepositCoinType, PaymentCoinType>(
escrow: &mut Escrow<DepositCoinType, PaymentCoinType>,
mut payment: Coin<PaymentCoinType>,
ctx: &mut TxContext,
) {
let payment_value = coin::value(&payment);
assert!(payment_value >= escrow.requested_amount, 0);
// Split exact amount, refund sisanya
let exact_payment = coin::split(&mut payment, escrow.requested_amount, ctx);
if (coin::value(&payment) > 0) {
transfer::public_transfer(payment, ctx.sender()); // refund sisa
} else {
coin::destroy_zero(payment); // tidak ada sisa
};
// Simpan pembayaran di escrow
balance::join(&mut escrow.receive, coin::into_balance(exact_payment));
// Transfer deposit ke buyer
transfer::public_transfer(coin::from_balance(balance::withdraw_all(&mut escrow.deposit), ctx), ctx.sender());
}
/// Seller tarik pembayaran yang diterima.
public entry fun complete_escrow<DepositCoinType, PaymentCoinType>(
escrow: &mut Escrow<DepositCoinType, PaymentCoinType>,
ctx: &mut TxContext,
) {
assert!(ctx.sender() == escrow.creator, 1);
transfer::public_transfer(coin::from_balance(balance::withdraw_all(&mut escrow.receive), ctx), ctx.sender());
}
/// Seller batalkan escrow (hanya jika belum ada pembayaran).
public entry fun cancel_escrow<DepositCoinType, PaymentCoinType>(
escrow: Escrow<DepositCoinType, PaymentCoinType>,
ctx: &mut TxContext,
) {
assert!(ctx.sender() == escrow.creator, 2);
let Escrow { id, deposit, requested_amount: _, receive, creator: _ } = escrow;
balance::destroy_zero(receive); // Fails if buyer already paid
object::delete(id);
transfer::public_transfer(coin::from_balance(deposit, ctx), ctx.sender());
}
}
Penjelasan Struct Escrow
Escrow ini mendukung swap antara dua tipe koin berbeda, tidak hanya koin yang sama. Ini membuat escrow lebih fleksibel untuk berbagai use case.
id: Tanda unik untuk object escrow (UID)deposit: Saldo penjual (tipeDepositCoinType) yang disimpan aman di escrowrequested_amount: JumlahPaymentCoinTypeyang harus dibayar pembelireceive: Saldo pembayaran (tipePaymentCoinType) yang sudah masuk dari pembelicreator: Alamat penjual sebagai pemilik escrow
Catatan Penting:
Balance<T>adalah kantong internal untuk menyimpan koin tipe tertentu. Kita memakaiBalancekarena bisa diletakkan di dalam object.Coin<T>adalah objek yang siap ditransfer. Kita ubahCoinmenjadiBalancesaat masuk ke escrow dan mengubahnya kembali saat keluar.- Menggunakan phantom type parameters (
phantom) karena tipe coin hanya digunakan untuk type checking, tidak disimpan sebagai data.
Penjelasan Fungsi
-
create_escrow<DepositCoinType, PaymentCoinType>:- Penjual memasukkan koin
DepositCoinTypesebagai deposit - Menentukan jumlah
PaymentCoinTypeyang diminta dari pembeli - Escrow object langsung dikembalikan ke alamat penjual supaya bisa diteruskan ke pembeli
- Contoh: Seller deposit TBTC, minta 100 zSUI sebagai pembayaran
- Penjual memasukkan koin
-
accept_escrow<DepositCoinType, PaymentCoinType>:- Pembeli mengirim koin
PaymentCoinTypedengan nilai >=requested_amount - Jika pembayaran lebih besar, sisa akan di-refund otomatis ke pembeli
- Deposit
DepositCoinTypepenjual langsung dikirim ke pembeli - Pembayaran
PaymentCoinTypepembeli disimpan di fieldreceive - Contoh: Buyer bayar 120 zSUI untuk request 100 zSUI, terima TBTC dan refund 20 zSUI
- Pembeli mengirim koin
-
complete_escrow<DepositCoinType, PaymentCoinType>:- Penjual menarik pembayaran
PaymentCoinTypeyang tersimpan di fieldreceive - Hanya pembuat escrow (creator) yang boleh memanggil fungsi ini
- Contoh: Seller tarik 100 zSUI yang sudah dibayar buyer
- Penjual menarik pembayaran
-
cancel_escrow<DepositCoinType, PaymentCoinType>:- Penjual menarik deposit
DepositCoinTypedan menghapus object escrow - Hanya bisa dilakukan jika tidak ada pembayaran yang tertahan
- Hanya pembuat escrow yang diizinkan
- Contoh: Seller batalkan escrow dan kembalikan TBTC
- Penjual menarik deposit
3.2. Modul mock_tbtc dan mock_zsui
Buat file sources/mock_tbtc.move dan salin kode berikut:
module escrow::mock_tbtc {
use sui::coin::{Self, TreasuryCap};
use sui::coin_registry;
use sui::tx_context::TxContext;
use sui::transfer;
/// One-time witness untuk mock TBTC token
public struct MOCK_TBTC has drop {}
/// Initializer otomatis saat publish
fun init(witness: MOCK_TBTC, ctx: &mut TxContext) {
let (builder, cap) = coin_registry::new_currency_with_otw(
witness,
8, // 8 desimal seperti Bitcoin
b"TBTC".to_string(),
b"Mock Bitcoin".to_string(),
b"Mock Bitcoin for escrow demo".to_string(),
b"".to_string(),
ctx,
);
let metadata_cap = coin_registry::finalize(builder, ctx);
transfer::public_transfer(cap, ctx.sender());
transfer::public_transfer(metadata_cap, ctx.sender());
}
/// Mint mock TBTC ke sender
public entry fun mint_mock_tbtc(
cap: &mut TreasuryCap<MOCK_TBTC>,
amount: u64,
ctx: &mut TxContext,
) {
let minted = coin::mint(cap, amount, ctx);
transfer::public_transfer(minted, ctx.sender());
}
}
Buat file sources/mock_zsui.move dan salin kode berikut:
module escrow::mock_zsui {
use sui::coin::{Self, TreasuryCap};
use sui::coin_registry;
use sui::tx_context::TxContext;
use sui::transfer;
/// One-time witness untuk mock zSUI token
public struct MOCK_ZSUI has drop {}
/// Initializer otomatis saat publish
fun init(witness: MOCK_ZSUI, ctx: &mut TxContext) {
let (builder, cap) = coin_registry::new_currency_with_otw(
witness,
9, // 9 desimal seperti SUI
b"zSUI".to_string(),
b"Mock zSUI".to_string(),
b"Mock zSUI for escrow demo".to_string(),
b"".to_string(),
ctx,
);
let metadata_cap = coin_registry::finalize(builder, ctx);
transfer::public_transfer(cap, ctx.sender());
transfer::public_transfer(metadata_cap, ctx.sender());
}
/// Mint mock zSUI ke sender
public entry fun mint_mock_zsui(
cap: &mut TreasuryCap<MOCK_ZSUI>,
amount: u64,
ctx: &mut TxContext,
) {
let minted = coin::mint(cap, amount, ctx);
transfer::public_transfer(minted, ctx.sender());
}
}
Penjelasan
Modul mock_tbtc:
MOCK_TBTC: One-time witness untuk membuat tipe koin TBTC (8 desimal seperti Bitcoin)init: Otomatis dijalankan saat paket dipublish untuk menyiapkan metadata dan kapabilitas mintmint_mock_tbtc: Mencetak TBTC sesuai jumlah yang diminta
Modul mock_zsui:
MOCK_ZSUI: One-time witness untuk membuat tipe koin zSUI (9 desimal seperti SUI)init: Otomatis dijalankan saat paket dipublish untuk menyiapkan metadata dan kapabilitas mintmint_mock_zsui: Mencetak zSUI sesuai jumlah yang diminta
Catatan: Setelah publish, simpan TreasuryCap<MOCK_TBTC> dan TreasuryCap<MOCK_ZSUI> supaya bisa mencetak koin kapan saja.
3.3. One-Time Witness (OTW) - Penjelasan Lengkap
Apa itu One-Time Witness?
One-Time Witness (OTW) adalah pattern keamanan di Sui Move yang memastikan suatu struct hanya bisa dibuat sekali saat package dipublish. Ini sangat penting untuk membuat tipe koin baru karena mencegah duplikasi atau pembuatan koin palsu.
Karakteristik OTW
- Struct dengan
dropability: OTW harus memiliki abilitydropsaja - Nama sama dengan module: Nama struct harus sama persis dengan nama module (case-sensitive)
- Hanya dibuat saat publish: Compiler Sui secara otomatis membuat instance OTW saat package pertama kali dipublish
- Tidak bisa dibuat manual: Tidak ada cara untuk membuat instance OTW secara manual setelah publish
Contoh di Kode Kita
module escrow::mock_tbtc {
// Nama struct MOCK_TBTC sama dengan nama module (mock_tbtc dalam uppercase)
public struct MOCK_TBTC has drop {}
fun init(witness: MOCK_TBTC, ctx: &mut TxContext) {
// witness adalah instance MOCK_TBTC yang dibuat otomatis saat publish
let (builder, cap) = coin_registry::new_currency_with_otw(
witness, // OTW digunakan di sini
8, // 8 desimal seperti Bitcoin
// ... parameter lainnya
);
}
}
Mengapa OTW Penting?
- Keamanan: Mencegah pembuatan koin duplikat atau palsu
- Unik: Setiap package hanya bisa membuat satu tipe koin dengan nama yang sama
- Trustless: Tidak perlu mempercayai pihak ketiga untuk membuat koin
- Immutable: Setelah dibuat, tipe koin tidak bisa diubah
Alur Kerja OTW
-
Saat publish package:
sui client publish- Compiler Sui melihat struct
MOCK_TBTCdanMOCK_ZSUIdengandropability - Compiler memeriksa apakah nama struct sama dengan nama module
- Jika ya, compiler membuat instance OTW secara otomatis
- Instance ini dikirim ke fungsi
initsebagai parameter
- Compiler Sui melihat struct
-
Fungsi
initdipanggil:- Menerima instance OTW sebagai
witness - Menggunakan
witnessuntuk membuat currency baru viacoin_registry::new_currency_with_otw - Setelah digunakan,
witnessdi-drop (tidak bisa digunakan lagi)
- Menerima instance OTW sebagai
-
Setelah publish:
- Tidak ada cara untuk membuat instance OTW lagi
- Tipe koin sudah terdaftar di sistem
- Hanya bisa mint coin menggunakan
TreasuryCap
Tips Penting
- ✅ Nama struct harus sama persis dengan nama module (case-sensitive)
- ✅ Harus memiliki
dropability saja - ✅ Hanya bisa digunakan sekali di fungsi
init - ❌ Tidak bisa membuat instance manual setelah publish
- ❌ Tidak bisa membuat koin dengan nama yang sama di package lain
4. Publish dan Testing
4.1. Publish Paket
Jalankan perintah publish:
sui client publish --gas-budget 200000000
Penting: Catat ID-ID berikut dari hasil publish:
PackageID: ID paket yang akan digunakan untuk memanggil fungsiTreasuryCap<MOCK_TBTC>: ID untuk proses mint TBTCTreasuryCap<MOCK_ZSUI>: ID untuk proses mint zSUI
Cara mengambil ID dari hasil publish:
# Output publish akan terlihat seperti ini:
# ┌───┬───────────────────────────────────────────────────────────────┐
# │ objectId │ │
# ├───┼───────────────────────────────────────────────────────────────┤
# │ 0x1234... │ PackageID (simpan ini!) │
# │ 0x5678... │ TreasuryCap<MOCK_TBTC> (simpan ini!) │
# │ 0x9abc... │ TreasuryCap<MOCK_ZSUI> (simpan ini!) │
# └───┴───────────────────────────────────────────────────────────────┘
4.2. Mint Mock Coins
Mint TBTC untuk testing. Gunakan angka contoh 10000000 (0.1 TBTC dengan 8 desimal):
sui client call \
--package <PACKAGE_ID> \
--module mock_tbtc \
--function mint_mock_tbtc \
--args <TBTC_TREASURY_CAP_ID> 10000000 \
--gas-budget 100000000
Mint zSUI untuk testing. Gunakan angka contoh 100000000 (0.1 zSUI dengan 9 desimal):
sui client call \
--package <PACKAGE_ID> \
--module mock_zsui \
--function mint_mock_zsui \
--args <ZSUI_TREASURY_CAP_ID> 100000000 \
--gas-budget 100000000
Tips:
- Cek coin yang tersedia dengan
sui client gas - Catat
ObjectIDdari coin yang baru dibuat (akan muncul di output transaksi) - Cara mengambil Coin ID: Lihat di bagian "created objects" pada output transaksi mint
4.3. Membagi Coin untuk Testing
Satu object coin tidak bisa dipakai sekaligus sebagai gas dan argumen. Gunakan sui client pay-sui untuk memecah coin:
sui client pay-sui ^
--input-coins <COIN_ID_BESAR> ^
--recipients $(sui client active-address) ^
--amounts 10000000 ^
--gas-budget 5000000
Penjelasan:
COIN_ID_BESAR: Coin utama yang nilainya besarrecipients: Alamat tujuan (gunakan alamat aktif sendiri)amounts: Nilai coin baru dalam MIST (1 SUI = 1.000.000.000 MIST)gas-budget: Biaya transaksi
Jalankan perintah ini dua kali untuk menyiapkan coin deposit dan coin pembayaran.
4.4. Cara Mengambil Object ID dengan Mudah
PENTING: Ini adalah bagian yang paling sering membuat bingung! Berikut cara mudah mengambil ID:
Setelah Publish:
sui client publish --gas-budget 200000000
# Lihat di bagian "Created Objects" - ambil 3 ID ini:
# 1. PackageID (untuk --package)
# 2. TreasuryCap<MOCK_TBTC> (untuk mint TBTC)
# 3. TreasuryCap<MOCK_ZSUI> (untuk mint zSUI)
Setelah Mint Coin:
sui client call --package <PACKAGE_ID> --module mock_tbtc --function mint_mock_tbtc ...
# Lihat di bagian "Created Objects" - ambil Coin ID TBTC
Setelah Create Escrow:
sui client call --package <PACKAGE_ID> --module simple_escrow --function create_escrow ...
# Lihat di bagian "Created Objects" - ambil Escrow ID
Setelah Accept Escrow:
sui client call --package <PACKAGE_ID> --module simple_escrow --function accept_escrow ...
# Lihat di bagian "Created Objects" - ambil TBTC Coin ID (yang diterima buyer)
Setelah Complete Escrow:
sui client call --package <PACKAGE_ID> --module simple_escrow --function complete_escrow ...
# Lihat di bagian "Created Objects" - ambil zSUI Coin ID (yang diterima seller)
Tips Pro:
- Selalu lihat bagian "Created Objects" di setiap output transaksi
- Copy ID langsung dari output, jangan ketik manual
- Gunakan
sui client objectsuntuk melihat semua object yang kamu punya
5. Testing Alur Escrow
Gunakan angka contoh 10000000 (0.01 token dengan 9 desimal). Ganti semua <ID> dengan ID yang sesuai milikmu.
5.1. Create Escrow (Penjual)
Penjual membuat escrow dengan deposit TBTC dan meminta pembayaran zSUI.
sui client call \
--package <PACKAGE_ID> \
--module simple_escrow \
--function create_escrow \
--type-args \
"$PACKAGE_ID::mock_tbtc::MOCK_TBTC" \
"$PACKAGE_ID::mock_zsui::MOCK_ZSUI" \
--args \
$TBTC_COIN_ID \
$RAW_AMOUNT \
--gas-budget 100000000
Contoh dengan nilai spesifik:
sui client call \
--package <PACKAGE_ID> \
--module simple_escrow \
--function create_escrow \
--type-args \
"<PACKAGE_ID>::mock_tbtc::MOCK_TBTC" \
"<PACKAGE_ID>::mock_zsui::MOCK_ZSUI" \
--args \
<TBTC_COIN_ID> \
10000000 \
--gas-budget 100000000
Catatan:
- Type-args pertama:
DepositCoinType(TBTC yang di-deposit penjual) - Type-args kedua:
PaymentCoinType(zSUI yang harus dibayar pembeli) $RAW_AMOUNT: Jumlah zSUI yang diminta (contoh: 10000000 = 0.01 zSUI)- ESCROW_ID: Akan muncul di output transaksi di bagian "created objects" - simpan ID ini!
Cara mengambil ESCROW_ID:
# Output transaksi akan terlihat seperti ini:
# ┌───┬───────────────────────────────────────────────────────────────┐
# │ objectId │ │
# ├───┼───────────────────────────────────────────────────────────────┤
# │ 0xdef0... │ Escrow object (simpan sebagai ESCROW_ID!) │
# └───┴───────────────────────────────────────────────────────────────┘
5.2. Transfer Escrow ke Pembeli (Opsional)
Jika menggunakan akun pembeli berbeda:
sui client transfer-object ^
--object-id <ESCROW_ID> ^
--to <ALAMAT_PEMBELI> ^
--gas-budget 50000000
Jika menggunakan satu alamat untuk latihan, langkah ini bisa dilewati.
5.3. Accept Escrow (Pembeli)
Pembeli mengirim pembayaran sesuai jumlah yang diminta. Buyer bisa mengirim lebih dari yang diminta, sisa akan di-refund otomatis.
sui client call \
--package <PACKAGE_ID> \
--module simple_escrow \
--function accept_escrow \
--type-args \
"$PACKAGE_ID::mock_tbtc::MOCK_TBTC" \
"$PACKAGE_ID::mock_zsui::MOCK_ZSUI" \
--args \
$ESCROW_ID \
$ZSUI_COIN_ID \
--gas-budget 100000000
Penting:
- Type-args harus sama dengan saat create escrow (TBTC, zSUI)
- Pembayaran bisa >=
requested_amount, sisa akan di-refund otomatis - Setelah transaksi, pembeli langsung menerima TBTC dari escrow (lihat di "created objects")
- Pembayaran zSUI pembeli tersimpan di escrow untuk diambil penjual
- TBTC Coin ID: Akan muncul di output transaksi sebagai "created objects" - pembeli menerima ini otomatis
- Refund: Jika pembayaran lebih dari requested, sisa akan dikembalikan otomatis
5.4. Complete Escrow (Penjual)
Penjual menarik pembayaran zSUI yang sudah diterima dari buyer.
sui client call \
--package <PACKAGE_ID> \
--module simple_escrow \
--function complete_escrow \
--type-args \
"$PACKAGE_ID::mock_tbtc::MOCK_TBTC" \
"$PACKAGE_ID::mock_zsui::MOCK_ZSUI" \
--args \
$ESCROW_ID \
--gas-budget 100000000
Catatan:
- Type-args harus sama dengan saat create escrow
- Pastikan object escrow kembali ke penjual (transfer lagi jika diperlukan)
- Penjual akan menerima pembayaran zSUI dari buyer (lihat di "created objects")
- zSUI Coin ID: Akan muncul di output transaksi sebagai "created objects" - penjual menerima ini otomatis
Setelah ini, penjual memegang pembayaran dari pembeli. Flow utama selesai!
5.5. Cancel Escrow (Opsional)
Jika penjual ingin membatalkan sebelum pembeli bayar:
sui client call \
--package <PACKAGE_ID> \
--module simple_escrow \
--function cancel_escrow \
--type-args \
"$PACKAGE_ID::mock_tbtc::MOCK_TBTC" \
"$PACKAGE_ID::mock_zsui::MOCK_ZSUI" \
--args \
$ESCROW_ID \
--gas-budget 100000000
Catatan:
- Type-args harus sama dengan saat create escrow
- Escrow akan dihapus dan deposit TBTC kembali ke penjual
- Hanya pembuat escrow yang bisa memanggil fungsi ini
- Hanya bisa dibatalkan jika belum ada pembayaran dari buyer
6. Pemeriksaan dan Debug
6.1. Perintah Pemeriksaan
-
Cek object escrow:
sui client object <ESCROW_ID>Menampilkan isi object escrow (deposit, receive, owner)
-
Cek semua object:
sui client objectsMenampilkan semua object milik alamat aktif
-
Cek detail transaksi:
sui client transaction <DIGEST>Membantu membaca detail transaksi terakhir
-
Cek coin yang tersedia:
sui client gasMenampilkan daftar coin untuk gas
6.2. Troubleshooting
Insufficient gas
- Isi faucet lagi atau kurangi
gas-budget
Object not found
- Cek kembali penulisan
ObjectID - Object mungkin sudah digunakan lalu terhapus
Assertion failure
- Pastikan nilai pembayaran >=
requested_amount(bisa lebih, sisa di-refund) - Pastikan pemanggil fungsi sesuai aturan (hanya creator yang boleh complete atau cancel)
- Error code:
0= payment kurang dari requested,1= bukan creator (complete),2= bukan creator (cancel)
Coin value mismatch
- Gunakan
sui client object <COIN_ID>untuk mengecek nilai coin sebelum dipakai
Type mismatch
- Pastikan menggunakan dua type-args yang benar:
<DEPOSIT_COIN_TYPE> <PAYMENT_COIN_TYPE> - Format:
<PACKAGE_ID>::mock_tbtc::MOCK_TBTCatau<PACKAGE_ID>::mock_zsui::MOCK_ZSUI - Type-args harus konsisten di semua fungsi (create, accept, complete, cancel)
7. Ringkasan
7.1. Konsep Utama
- Escrow menyimpan deposit penjual sampai pembeli membayar sesuai jumlah yang disepakati
- Mendukung swap dua tipe coin berbeda:
DepositCoinTypedanPaymentCoinType Balance<T>dipakai untuk menyimpan koin di dalam objectCoin<T>dipakai saat transfer keluarsimple_escrowmendukung empat aksi utama: buat, terima, selesaikan, batalkan- Automatic refund: Buyer bisa bayar lebih dari requested, sisa otomatis di-refund
- One-Time Witness (OTW) memastikan tipe koin hanya dibuat sekali saat publish
7.2. Best Practices
- Catat semua ID penting: Package ID, Treasury Cap TBTC & zSUI, Coin IDs, Escrow ID
- Gunakan mock coin untuk testing agar tidak memakai dana sebenarnya
- Pecah coin sebelum testing untuk memisahkan gas dan argumen
- Verifikasi object dengan
sui client objectsebelum menggunakan - Perhatikan desimal: TBTC menggunakan 8 desimal, zSUI menggunakan 9 desimal
7.3. Langkah Selanjutnya
Setelah memahami contoh ini, kamu bisa:
- Menambahkan status escrow (pending, completed, cancelled)
- Menambahkan batas waktu (deadline)
- Menambahkan event untuk mencatat setiap aksi escrow
- Menambahkan fee untuk escrow service
Selamat berlatih! 🚀