Skip to content
edycutjongPublic

About

πŸ•‹ Purpose-bound umrah prepayment on Arbitrum One: each pilgrim's rupiah earmarked onchain, payable only to claim-verified vendors, refundable by anyone. ETHJKT 2026.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

Β 

History

91 Commits

Folders and files

Repository files navigation

Ikon Mabrur

Mabrur πŸ•‹

Bahasa Indonesia Β· English

Dana jamaah adalah amanah, bukan modal kerja agen.
Setiap rupiah uang muka jamaah dikunci onchain per pos, hanya bisa dibayarkan ke vendor terverifikasi, dan bisa dikembalikan oleh siapa pun.

Kasus First Travel: 63.310 calon jamaah jadi korban, kerugian Rp 905 miliar (Kompas, 5 Jan 2023).

Mabrur: empat percobaan agen pada booking Pak Ahmad, tiga ditolak, satu dibayar; dana Ibu Siti dikembalikan

Aplikasi Pitch deck Video pitch Video demo HackQuest DEMO.md Arbitrum One ETHJKT 2026


Solidity OpenZeppelin Foundry Next.js viem CI Release


πŸ“Έ Lihat cara kerjanya

Konsol agen di aplikasi Mabrur: faktur hotel Ibu Siti dicoba pada booking Pak Ahmad, tombol Simulasi saja ditekan, dan cap merah DITOLAK EarmarkMismatch muncul

Rekaman layar asli dari konsol agen: Simulasi saja adalah panggilan nyata ke kontrak tanpa mengirim transaksi. Versi EarmarkMismatch yang benar-benar ditambang ada di baris pertama tabel di bawah.

Satu booking, empat percobaan. Agen mencoba memakai faktur hotel Ibu Siti dengan dana Pak Ahmad (EarmarkMismatch), mencoba membayar direkturnya sendiri (VendorClaimMissing), membayar maskapai (βœ” Rp 14.000.000 ke penanda tangan faktur), lalu mencoba mengambil ujrah sebelum jamaah berangkat (NotDeparted). Batas tiket Ibu Siti lewat tanpa tiket dibeli, dan siapa pun bisa menekan refund: setiap rupiah yang belum terpakai kembali kepadanya.

Setiap percobaan itu adalah transaksi yang benar-benar ditambang di Arbitrum One: tiga penolakan di bawah adalah transaksi gagal yang bisa dibuka di Arbiscan, lengkap dengan nama error-nya. Buku besar lengkap ada di DEMO.md.

Percobaan agen (onchain) Hasil Tx
Faktur hotel Ibu Siti dipakai pada booking Pak Ahmad EarmarkMismatch 0x9ed30e38…
Faktur yang ditandatangani direktur agen VendorClaimMissing 0x887d6b86…
Mengirim ulang faktur yang sudah dibayar InvoiceReplayed 0x952f30ad…
Agen menandatangani "keberangkatan" sendiri untuk mengambil ujrah NotDeparted 0xe4855ebd…
Faktur maskapai, Rp 14.000.000 dibayar ke penanda tangan 0x7bb513e7…
Batas tiket Ibu Siti lewat, pihak ketiga memanggil refund Rp 23.000.000 kembali ke Ibu Siti 0x13b8a133…

πŸ’‘ Masalah & solusinya

Umrah dibayar di muka, sering berbulan-bulan sebelumnya, kepada biro perjalanan berizin (PPIU). Ketika agen memperlakukan dana itu sebagai modal kerja, jamaah baru membiayai keberangkatan jamaah lama sampai semuanya runtuh:

  • First Travel: 63.310 calon jamaah, kerugian Rp 905 miliar (Kompas, 5 Jan 2023); pendaftar baru diduga membiayai keberangkatan jamaah sebelumnya (detik, 24 Jul 2017).
  • Abu Tours: 86.720 jamaah, perkiraan kerugian Rp 1,8 triliun (Kompas, 29 Jan 2019).
  • Skala: sekitar 1,4 juta jamaah berangkat melalui PPIU pada 2024 (data SISKOPATUH sebagaimana dilaporkan HIMPUH, 18 Feb 2025; sumber sekunder).

Mabrur menjadikan uang muka jamaah sebuah aset dunia nyata yang ia pegang sendiri: klaim atas layanan berizin yang tidak bisa dipindahtangankan (mUMRAH), dipecah menjadi pos TIKET Β· HOTEL Β· VISA Β· UJRAH. Agen hanya punya satu jalan untuk memindahkan uang.

Fitur utama (semuanya di MabrurPBM.sol):

  • 🧾 Dana dipisah per jamaah: satu tanda tangan permit EIP-2612 ditambah book() membungkus rupiahnya ke dalam booking miliknya sendiri. _update memblokir setiap transfer, sehingga dana satu jamaah tidak pernah bisa membiayai perjalanan jamaah lain (MabrurPBM.sol:241).
  • ✍️ Tidak ada yang memilih penerima: spend() membayar penanda tangan faktur EIP-712, dan hanya jika penanda tangan itu memegang klaim AIRLINE / HOTEL / VISA yang sah di ClaimRegistry. Tidak ada kolom alamat yang bisa diisi agen dengan alamat direkturnya (MabrurPBM.sol:162).
  • πŸ›« Ujrah setelah berangkat: ujrah agen (maksimal 20 %) baru terbuka dengan tanda tangan Departure dari jamaah, atau dari maskapai berizin yang dibayar dari pos tiketnya (MabrurPBM.sol:197).
  • 🎫 "Tiket lunas atau sisa dana kembali": jika tiket tidak dibayar lunas sebelum ticketBy yang ditandatangani jamaah, atau setelah departBy lewat, siapa pun (tetangga, LSM, regulator) bisa memanggil refund() dan setiap rupiah yang belum terpakai kembali ke jamaah (MabrurPBM.sol:218). book juga menolak tanggal berangkat lebih dari 180 hari ke depan.
  • πŸ›οΈ Pandangan regulator secara langsung: regulatorView(agency) (booking terbuka, kewajiban dari buku besar setoran βˆ’ pembayaran yang independen, dana tersimpan) dan conservation() menunjukkan bahwa seluruh uang muka yang belum terpakai dijamin rupiah yang ada di kontrak.
  • πŸ”‘ Tanpa kunci darurat: tidak ada owner, pause, atau jalur upgrade yang bisa menyentuh saldo. Pemilik registry (kunci regulator, tidak pernah agen) hanya menentukan siapa yang diakui sebagai vendor.

Mengapa bukan rekening penampungan bank atau kontrak escrow bertahap?

Rekening penampungan bank Escrow bertahap (pendekatan umum) Mabrur
Pemisahan per jamaah satu rekening gabungan satu kolam atau per transaksi dana per booking, ditegakkan di token
Siapa memilih penerima agen memberi instruksi ke bank agen menulis alamat tidak ada: penanda tangan faktur yang terverifikasi
Ujrah sebelum layanan agen menarik kapan saja pada tahap yang ditentukan agen terkunci sampai keberangkatan ditandatangani
Agen menghilang proses pengadilan, bertahun-tahun butuh arbiter refund() oleh siapa pun, tanpa kerja sama agen
Pandangan regulator setelah runtuh, lewat audit tidak ada langsung, dari state kontrak

Dibanding solusi RWA yang sudah ada

  • Centrifuge, Ondo, Securitize menokenisasi aset milik investor (kredit, obligasi, dana). Mabrur melindungi uang muka milik konsumen: dana jamaah yang sudah dibayar untuk layanan yang belum diterima.
  • MAS Project Orchid (Singapura) menguji purpose-bound money untuk voucher dan pembayaran (MAS, 31 Okt 2022). Mabrur menerapkan pola uang berbatas-tujuan itu pada uang muka konsumen: dipisah per jamaah, dibayar hanya ke vendor yang klaimnya terverifikasi, dan dikembalikan oleh siapa pun bila tiket tidak lunas.

πŸ—οΈ Arsitektur & teknologi

flowchart LR
  P[Dompet jamaah] -- permit + book --> PBM[MabrurPBM<br/>dana mUMRAH per pos]
  A[Konsol agen] -- spend(faktur, tanda tangan vendor) --> PBM
  V[Halaman vendor] -- tanda tangan Invoice EIP-712 --> A
  PBM -- hasValidClaim --> R[ClaimRegistry<br/>pemilik = regulator]
  I[Penerbit: pengganti Kemenhaj / IATA] -- issueClaim --> R
  PBM -- penerima = penanda tangan --> V
  PBM -- ujrah setelah tanda tangan berangkat --> A
  X[Siapa pun] -- refund setelah ticketBy / departBy --> PBM
  PBM -- sisa pos --> P
  PBM -. regulatorView / conservation .-> G[Panel regulator]
Loading
Lapisan Teknologi
Kontrak Solidity ^0.8.24, dikompilasi & diverifikasi dengan solc 0.8.33; OpenZeppelin 5.6.1 (ERC20Wrapper, ERC20Permit, EIP712, ECDSA, ReentrancyGuard)
Chain Arbitrum One (42161), ketiga kontrak terverifikasi di Arbiscan; salinan di testnet Arbitrum Sepolia (421614) untuk dicoba siapa saja
Tooling Foundry (unit, fuzz, invariant, skrip yang di-broadcast ke mainnet)
Aplikasi Scaffold-ETH 2: Next.js App Router, wagmi + viem, RainbowKit; error kontrak didekode lewat simulateContract
Uang tIDR: rupiah uji ERC20Permit tanpa nilai moneter, decimals = 0
Kontrak Alamat (Arbitrum One)
MabrurPBM 0x36f1d899d9d4411b2DdfB60Dbbe989220336d2D5
ClaimRegistry 0xd5B731CD0f2c91D5D64b59d9E4a2A4E4b6315ADb
TIDR (token uji) 0x66F838be32A624f4C797483a151C7f6209A43448
Kontrak Alamat (Arbitrum Sepolia, testnet)
MabrurPBM 0xa2BC8c2959b1c9d5A90B750aCFfbD52181CE98f1
ClaimRegistry 0xCbf1247e292B8eA0bA151a1b5811B732Fc564480
TIDR (token uji) 0xc56c5561fc65274c55419FC031D90f68AE854B4B

Coba sendiri di testnet. Tombol Mainnet | Testnet di header aplikasi memindahkan aplikasi ke Arbitrum Sepolia, dengan kontrak yang sama (terverifikasi). Ambil ETH uji gratis dari faucet, tekan faucet tIDR di aplikasi, lalu buat booking sendiri dengan batas tiket Demo: 10 menit, dan refund setelah 10 menit itu lewat: mabrur.edycu.dev/app/jamaah?net=testnet.

Biaya per jamaah (dari receipt asli, script/cost.sh): siklus lengkap jamaah yang berangkat (book + 3 pembayaran vendor + pelepasan ujrah) memakai 907.185 gas β‰ˆ Rp 809, dan siklus yang di-refund (book + 1 pembayaran + refund) 582.938 gas β‰ˆ Rp 520, dengan kurs ETH/IDR 44.563.294 (CoinGecko, 9 Okt 2026 04:32 UTC). Sekitar 0,0025 % dari paket Rp 32.000.000.


πŸš€ Mulai

Untuk juri, tanpa instalasi: buka mabrur.edycu.dev (atau langsung /judge). Semua data bisa dibaca tanpa dompet.

  • /app/jamaah: cari booking berdasarkan alamat jamaah atau id, lihat status setiap pos langsung dari chain.
  • /app/agen: tekan Muat contoh faktur (atau tempel / pilih file faktur bertanda tangan), tekan Simulasi saja, dan dapatkan keputusan kontrak dari Arbitrum One tanpa dompet. Contohnya ada di /demo/invoices.json; /app/agen?contoh=1 memuatnya otomatis. Panel regulator ada di sebelah kanan.
  • /app/vendor: tandatangani Invoice dengan kunci burner lalu tempel ke konsol agen.

Jalankan sendiri:

git clone --recursive https://github.com/edycutjong/mabrur.git && cd mabrur
yarn install
cd packages/foundry && forge test          # 87 tes: unit, fuzz, invariant
cd ../.. && yarn chain                     # anvil lokal (terminal 1)
yarn deploy                                # deploy + buat ABI (terminal 2)
NEXT_PUBLIC_LOCAL_CHAIN=true yarn start    # aplikasi di localhost:3000 (terminal 3)

Memutar ulang demo Arbitrum One (script/run.sh SeedDemo, script/proof.sh) membutuhkan kunci peran di ~/.config/mabrur/keys.env; kunci itu tidak pernah ada di repo ini.


πŸ§ͺ Pengujian & bukti

Apa Hasil
forge test 87 tes, 0 gagal, cakupan baris Β· cabang Β· fungsi 100 % pada ketiga kontrak: setiap custom error punya tes; tes regresi dinamai sesuai cacat yang dicegahnya (mis. test_ReAddedIssuerDoesNotResurrectOldClaims)
Suite invariant 7 invariant Γ— 256 run Γ— kedalaman 100: Ξ£ dana per pos == suplai mUMRAH == total tercatat; tIDR di kontrak == suplai + donasi; buku besar per booking dan per agen seimbang; tidak ada pembayaran ke alamat tanpa klaim; tidak ada pembayaran setelah booking bisa di-refund; setelah semua tenggat lewat dan semua di-refund, suplai menjadi 0
Batas T1 test_CaptureIssuer_Bound: penerbit yang dibajak pun tidak bisa mengambil lebih dari pos TIKET+HOTEL+VISA yang belum kedaluwarsa; ujrah hanya pernah dibayar ke agen
Revert yang ditambang 4 percobaan curang ditambang di Arbitrum One, masing-masing diputar ulang oleh script/proof.sh dan wajib terdekode ke error yang diharapkan
Review 3 putaran review adversarial internal atas kontrak: tidak ada temuan High atau Medium; empat temuan Low di ClaimRegistry (penimpaan klaim penerbit lain, penambahan ulang penerbit, penyempitan topik, klaim kedaluwarsa yang memblokir) diperbaiki dengan tes regresi; putaran 3 tanpa temuan baru (log review)
Lapisan Alat
Kontrak forge fmt --check, forge test (unit, fuzz, invariant), forge coverage di CI
Frontend ESLint (0 peringatan), cek tipe tsc, build produksi Next.js, tes unit Vitest dengan cakupan 100 % per file (gerbang CI), E2E Playwright
Keamanan CodeQL, peringatan + pembaruan Dependabot, gitleaks atas seluruh riwayat, secret scanning + push protection GitHub
Rilis versi semantik dari conventional commits (release.yml); deploy otomatis ke Vercel setelah semua gerbang lulus

✈️ Bagaimana tahu tiket dan hotelnya benar-benar ada?

Pertanyaan juri di Demo Day ETHJKT (11 Okt 2026). Jawaban jujurnya: blockchain tidak bisa melihat pesawat atau kamar hotel. Yang dibuktikan kontrak adalah siapa yang dibayar, berapa, dan atas tanda tangan siapa:

Pertanyaan Yang ditegakkan kontrak Bukti
Apakah penerimanya benar maskapai / hotel? Penerima selalu penanda tangan faktur, dan penanda tangan itu harus punya klaim berizin yang sesuai pos: pos tiket hanya ke klaim AIRLINE, pos hotel hanya ke klaim HOTEL (topicOf). Agen tidak bisa mengetik alamat penerima, dan tidak bisa membayar dirinya sendiri (SelfDealing) MabrurPBM.sol#L162, test_TopicOf_FlightReturnsAirline, test_TopicOf_HotelReturnsHotel, test_Demo_VendorClaimMissing_DirectorCannotBePaid, test_Spend_RevertsSelfDealing_LicensedAgencyAsVendor
Apakah tiketnya dibayar sungguhan, bukan "tiket Rp 1"? Faktur tiket harus melunasi seluruh pos tiket, dan hanya satu kali test_Spend_RevertsFlightNotFullyPaid_Rp1TicketIsNotATicket, test_Spend_RevertsFlightAlreadyPurchased
Faktur untuk booking yang mana? Faktur menandatangani bookingId; faktur untuk jamaah lain ditolak (EarmarkMismatch, tertambang di mainnet) 0x9ed30e38…
Bisa dicek jamaah ke vendor? Setiap faktur membawa nomor referensi publik (ref, 32 byte) yang tercatat di event Spent. Di demo isinya nomor faktur (mis. INV-HTL-0001); usulan produksi: PNR maskapai / nomor voucher hotel, yang bisa dicek jamaah langsung ke maskapai atau hotel MabrurPBM.sol#L79
Kapan biro mendapat untung? Ujrah baru cair dengan tanda tangan keberangkatan dari jamaah, atau dari maskapai yang memang dibayar dari pos tiket booking itu dan masih berizin releaseMargin, test_ReleaseMargin_UnpaidAirlineCannotCoSign, test_ReleaseMargin_RevokedAirlineCannotCoSign
Kalau tiket tidak pernah dibeli? Setelah batas tiket lewat tanpa tiket lunas, siapa pun bisa me-refund sisa dana ke jamaah 0x13b8a133…

Yang tidak bisa dibuktikan onchain: apakah layanannya benar-benar diberikan (asumsi T2 di bawah). Vendor berizin yang menagih untuk layanan yang tidak diberikan tetap mungkin; kerugiannya dibatasi oleh pos booking itu, pembayarannya tercatat publik atas nama vendor, dan regulator bisa mencabut klaimnya sehingga pembayaran berikutnya ditolak (test_Spend_RevokedVendorClaimBlocksPayment).

Roadmap (belum dibangun): konfirmasi check-in hotel yang ditandatangani jamaah sebelum pos hotel dibayar penuh, dan klaim vendor yang diterbitkan langsung oleh maskapai / IATA dan regulator, bukan kunci pengganti.

πŸ’° Model bisnis

Rencana, bukan traksi. Belum ada pengguna di luar tim, mitra, atau pendapatan. Harga di bawah adalah usulan untuk versi produksi; kontrak hari ini tidak memungut biaya apa pun.

Tiga angka yang sering tertukar:

Angka Milik siapa Artinya
Ujrah (maks. 20 %) Biro (PPIU) Keuntungan biro dari jamaah, terkunci sampai jamaah berangkat (MAX_MARGIN_BPS = 2000, MabrurPBM.sol#L52). Mabrur tidak mengambil bagian apa pun dari ujrah
Rp 809 Biaya jaringan Gas Arbitrum untuk satu siklus jamaah lengkap (lihat Pengujian & bukti). Ini biaya, bukan pendapatan
Rp 50.000 Mabrur (usulan) Biaya layanan per jamaah yang dibayar biro
  • Margin per jamaah: Rp 50.000 βˆ’ Rp 809 β‰ˆ Rp 49.000. Rp 50.000 setara sekitar 0,16 % dari paket Rp 32.000.000.
  • Skala (ilustrasi): dari sekitar 1,4 juta jamaah per tahun melalui PPIU (lihat Masalah & solusinya), 1 % adopsi = 14.000 jamaah β‰ˆ Rp 700 juta per tahun; 10 % β‰ˆ Rp 7 miliar per tahun.
  • Kenapa biro mau membayar: bukti terbuka bahwa dana jamaah aman, keunggulan kepercayaan terhadap biro promo. Usulan ke regulator (ide, bukan kebijakan yang ada): biro yang memakai earmark mendapat keringanan jaminan bank. Jangka panjang: mandat regulator, sehingga Mabrur menjadi infrastruktur yang dilisensikan ke regulator atau bank penerbit token rupiah.
  • Yang tidak akan pernah dilakukan: mengambil bunga atau imbal hasil dari dana jamaah. Dana jamaah adalah amanah; kontrak tidak punya owner, treasury, atau fungsi yield (MabrurPBM.sol#L19), dan hanya membayar ke tiga pihak: vendor penanda tangan faktur, ujrah biro setelah berangkat, dan refund jamaah.
  • Risiko yang diakui: pihak yang membayar (biro) adalah pihak yang dibatasi. Biro jujur mendapat keunggulan kepercayaan; biro nakal tidak akan memilihnya, itulah sebabnya jalur jangka panjangnya mandat regulator. Hipotesis ini belum diuji dengan biro sungguhan.

βš–οΈ Asumsi kepercayaan & batasan yang jujur

  • T1 Β· integritas registry. Pemilik registry adalah kunci regulator, tidak pernah agen. Pemilik atau penerbit yang dibajak bisa mengakui direktur sebagai vendor; kerusakannya terbatas (test_CaptureIssuer_Bound) dan terlihat onchain.
  • T2 Β· kejujuran vendor. Vendor berizin bisa menandatangani faktur untuk layanan yang lebih sedikit dari tagihan. spend memblokir alamat agen sendiri (SelfDealing) tetapi tidak bisa melihat afiliasi perusahaan; setiap pembayaran dibatasi oleh pos booking tersebut.
  • T3 Β· penanda tangan keberangkatan. Satu maskapai yang dibayar dari pos tiket bisa ikut menandatangani keberangkatan. Faktur tiket harus melunasi seluruh pos tiket, sehingga "tiket Rp 1" tidak bisa mematikan refund batas tiket (test_Spend_RevertsFlightNotFullyPaid_Rp1TicketIsNotATicket).
  • T4 Β· penyimpanan kunci jamaah. Jika agen memegang kunci jamaah, agen bisa menandatangani atas namanya. Jalur produksi: dompet passkey / smart account yang diterbitkan bank atau onramp jamaah, tidak pernah oleh agen.
  • Uangnya adalah token uji. Bank Indonesia tidak mengizinkan kripto sebagai alat pembayaran (ANTARA, 15 Jun 2021). Jalur produksinya adalah token rupiah berizin (deposito tertokenisasi bank atau Rupiah Digital) ditambah mandat regulator untuk dana PPIU; keduanya belum ada hari ini. Wrapper menerima ERC-20 standar apa pun.
  • Tidak menjamin kursi. Kursi bergantung pada maskapai dan kuota visa. Mabrur menjamin tiket lunas, atau setiap rupiah yang belum terpakai kembali sebelum tanggal yang ditandatangani jamaah.
  • Kunci penerbit adalah pengganti Kemenhaj (Kementerian Haji dan Umrah, yang memegang perizinan dan pengawasan PPIU sejak UU 14/2025 dan Permen Haji dan Umrah No. 2/2026; RRI, 6 Agu 2026) / IATA, dan semua nama dalam demo ("PT Amanah Contoh Wisata", "PT Contoh GSA") fiktif.

πŸ“ Struktur proyek

mabrur/
β”œβ”€β”€ packages/foundry/
β”‚   β”œβ”€β”€ contracts/        # MabrurPBM, ClaimRegistry, TIDR
β”‚   β”œβ”€β”€ test/             # unit + fuzz, suite invariant, tes cakupan
β”‚   β”œβ”€β”€ script/           # Deploy, Setup, SeedDemo, DemoRun, DemoRefund, proof.sh, cost.sh, ledger.py
β”‚   └── broadcast/        # receipt Arbitrum One yang di-commit (DEMO.md dibuat dari sini)
β”œβ”€β”€ packages/nextjs/app/app/
β”‚   β”œβ”€β”€ jamaah/           # buku amanah jamaah: booking dengan permit, tanda tangan berangkat, refund
β”‚   β”œβ”€β”€ agen/             # konsol agen: pemuat faktur, cap revert terdekode, panel regulator
β”‚   └── vendor/           # penanda tangan faktur vendor (EIP-712)
β”œβ”€β”€ DEMO.md               # setiap langkah sebagai tautan Arbiscan
β”œβ”€β”€ JUDGE.md              # jalur 30 detik untuk juri
└── .github/              # CI, CodeQL, gitleaks, Dependabot, rilis

πŸ“„ Lisensi

MIT Β© 2026 Edy Cu. Dibangun di atas Scaffold-ETH 2 (MIT, BuidlGuidl).

πŸ™ Terima kasih

Dibuat untuk Ethereum Jakarta Hackathon 2026 (ETHJKT Γ— HackQuest), track Build the Real World Onchain. Terima kasih kepada mentor dan panitia ETHJKT, OpenZeppelin, Foundry, dan Scaffold-ETH 2.

Cara dibangun. Dibangun solo bersama agen AI (Claude Code) dari spesifikasi desain yang ditulis sebelum sprint; semua kode di-commit setelah 10:00 WIB, 9 Okt 2026 (commit pertama 6837308, 10:38 WIB). Riwayat commit lengkap ada di repo ini.

About

πŸ•‹ Purpose-bound umrah prepayment on Arbitrum One: each pilgrim's rupiah earmarked onchain, payable only to claim-verified vendors, refundable by anyone. ETHJKT 2026.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Used by

Contributors

Languages