This is the Move contract module for ONE KATAKURI, Japan's first legal combat sports–focused prediction market platform.
It implements the katakuri::prediction_market module, the core of a hybrid on/off-chain design.
- Overview
- Design Philosophy
- Architecture
- Module Structure
- API Reference
- Off-chain Responsibilities
- Capability Separation Design
- Verifiability via Merkle Root
- Lifecycle
- Build and Deploy
- Future Extensions
- Project Information
ONE KATAKURI is a prediction market platform where users participate using free points earned through reward activities (point programs) rather than wagering cash — structurally avoiding Japan's criminal gambling laws.
This repository provides the Move contract portion of ONE KATAKURI's prediction market functionality, deployed on Sui. Specifically, it handles the lifecycle of market creation, closure, and result settlement on-chain, and guarantees the immutability of the full prediction snapshot and winner list at close time.
Point balances, individual prediction intake, and real-time odds calculation (LMSR) remain off-chain and combine with this contract to form a complete prediction market.
This contract is designed for submission to "Sui x ONE Samurai - Tokyo Builders' Arena (Apr 2026)". The demo target event is ONE SAMURAI 1, held on April 29, 2026 (including Takeru's retirement bout — a Japan-focused ONE Championship event streaming on U-NEXT).
ONE KATAKURI occupies a middle ground between purely on-chain prediction markets (e.g., Polymarket) and purely off-chain point-reward apps (e.g., POYP), adopting a hybrid design.
| Feature | Layer | Reason |
|---|---|---|
| Point balance management | Off-chain | Central management of free points required by legal framework |
| LMSR odds calculation | Off-chain | High-frequency updates and cost efficiency |
| Individual prediction intake | Off-chain | Minimize UX friction; users are Web3-agnostic |
| Market specification declaration | On-chain | Immutability |
| Snapshot at close | On-chain | Trustworthiness of sentiment data |
| Result settlement | On-chain | Immutability and operational transparency |
| Winner list finalization | On-chain | Immutability and user-verifiability |
1. Users never send on-chain transactions
By using zkLogin with social login, users are unaware they're interacting with a blockchain. All public functions in this module are called by the operator or oracle.
2. On-chain gas costs are limited to 3–4 transactions per market
The core flow uses create_market, close_market, and resolve_market — with cancel_market added only in exceptional cases, keeping the ceiling at 4. Even with a high volume of predictions, on-chain costs do not scale linearly.
3. Individual predictions are aggregated into a Merkle root and recorded on-chain
Rather than recording each prediction on-chain, the Merkle root of all predictions is posted at market close. This balances scalability with verifiability.
┌─────────────────────────────────────────────────────────────┐
│ ONE KATAKURI App │
│ (Frontend, Mobile-first) │
│ zkLogin Auth │
└────────────┬────────────────────────────────┬───────────────┘
│ │
↓ ↓
┌────────────────────────┐ ┌─────────────────────────────┐
│ Off-chain Server │ │ Sui Blockchain │
│ │ │ │
│ • Point Balance DB │◄─────┤ katakuri::prediction_market │
│ • Predictions DB │ Event│ │
│ • LMSR Calculation │ Sub │ • create_market │
│ • Sponsor / Reward │ │ • close_market │
│ • U-NEXT Integration │ │ • resolve_market │
│ • Merkle Tree Builder │─────►│ • cancel_market │
└────────────────────────┘ Tx └──────────────────────────────┘
▲
│ (Sui Overflow extensions)
│
┌───────────┴────────────┐
│ • katakuri_sbt │
│ • katakuri_lottery │
│ • katakuri_collectible│
│ • Seal / Walrus │
│ • Nautilus Oracle │
└────────────────────────┘
This repository contains all types, constants, functions, and events in a single module: katakuri::prediction_market.
| Type | Role |
|---|---|
Market |
The market object itself. Deployed as a Shared object |
AdminCap |
Operator capability. Used for market creation, closure, and cancellation |
OracleCap |
Oracle capability. Used for result settlement |
| Value | Status | Meaning |
|---|---|---|
0 |
STATUS_OPEN |
Accepting predictions |
1 |
STATUS_CLOSED |
Prediction intake ended; awaiting result |
2 |
STATUS_RESOLVED |
Result finalized |
3 |
STATUS_CANCELLED |
Cancelled (all stakes eligible for refund) |
| Function | Required Capability | Role |
|---|---|---|
create_market |
AdminCap | Create a market |
close_market |
AdminCap | End prediction intake and post Merkle root |
resolve_market |
OracleCap | Finalize result and post winner Merkle root |
cancel_market |
AdminCap | Cancel market (exception use only) |
| Event | Emitted When | Purpose |
|---|---|---|
MarketCreated |
Market creation | Initialize off-chain UI |
MarketClosed |
Market closure | Persist final sentiment values |
MarketResolved |
Result finalization | Trigger off-chain batch payout |
MarketCancelled |
Cancellation | Trigger off-chain batch refund |
Creates a new prediction market.
public entry fun create_market(
_admin_cap: &AdminCap,
title: String,
description: String,
event_id: String,
category: String,
metadata_uri: String,
options: vector<String>,
opens_at: u64,
closes_at: u64,
amm_type: String,
amm_params_hash: vector<u8>,
fee_bps: u16,
clock: &Clock,
ctx: &mut TxContext,
)Parameter descriptions:
title: Market title (e.g.,"TAKERU vs RODTANG · WINNER")event_id: Event identifier (e.g.,"ONE_SAMURAI_1")category: Market category ("WINNER","METHOD","ROUND","PROPS", etc.)metadata_uri: URI for detailed description or images (Walrus anticipated in the future)options: Vector of prediction option strings (e.g.,["TAKERU", "RODTANG"])opens_at,closes_at: Start/end timestamps for prediction intake (milliseconds)amm_type: AMM type declaration (currently"LMSR")amm_params_hash: Hash of LMSRbparameter etc. Actual calculation is off-chainfee_bps: Fee rate in basis points (500 = 5%)
Post-condition: Market is shared as a Shared object in STATUS_OPEN state, and a MarketCreated event is emitted.
Ends prediction intake and posts the final stake amounts and Merkle root of all predictions on-chain.
public entry fun close_market(
market: &mut Market,
_admin_cap: &AdminCap,
final_stakes: vector<u64>,
total_predictions: u64,
predictions_merkle_root: vector<u8>,
clock: &Clock,
)Parameter descriptions:
final_stakes: Final total stakes per option (e.g.,[2_400_000, 1_800_000])total_predictions: Total number of predictions (e.g.,18420)predictions_merkle_root: Merkle root of the full prediction list
Pre-condition: closes_at must have passed; market must be in STATUS_OPEN.
Post-condition: Market transitions to STATUS_CLOSED and the snapshot is persisted.
Finalizes the match result and posts the winner list Merkle root and total payout amount.
public entry fun resolve_market(
market: &mut Market,
_oracle_cap: &OracleCap,
winning_option: u8,
total_winners: u64,
winners_merkle_root: vector<u8>,
total_payout: u64,
clock: &Clock,
)Parameter descriptions:
winning_option: Index of the winning option (0-based)total_winners: Total number of winnerswinners_merkle_root: Merkle root of the winner list (each entry is expected to include(user_address, payout_amount), etc.)total_payout: Total points to be paid out across all winners
Pre-condition: Market must be in STATUS_CLOSED and result must not yet be finalized.
Post-condition: Market transitions to STATUS_RESOLVED and a MarketResolved event is emitted. The off-chain server receives this event and batch-credits all winners' point balances.
Cancels a market under exceptional circumstances — match cancellation, discovered fraud, or other operator decisions.
public entry fun cancel_market(
market: &mut Market,
_admin_cap: &AdminCap,
clock: &Clock,
)Pre-condition: Market must be in STATUS_OPEN or STATUS_CLOSED.
Post-condition: Market transitions to STATUS_CANCELLED. The off-chain server receives the event and refunds all predictors' stakes.
This Move contract operates as a complete prediction market in conjunction with an off-chain server. Off-chain responsibilities are as follows.
- Social login processing via zkLogin
- User point balance database
- Recording point-earning activities (U-NEXT viewing, sponsor ad viewing, etc.)
- Receiving prediction requests from users
- Validating and locking point balances
- Calculating odds via LMSR and presenting them to users
- Recording predictions in the off-chain database
Processing flow triggered at close time:
- Fetch all predictions for the market from the off-chain database
- Aggregate the final total stakes per option
- Build a Merkle tree from the prediction list and compute the Merkle root
- Call
close_marketfrom the AdminCap-holding account to post the snapshot
Processing flow after the match ends:
- Retrieve match results from the ONE Championship official API, Sherdog, Tapology, etc.
- Extract the winner list and calculate each winner's payout via LMSR
- Build a Merkle tree from the winner list and compute the Merkle root
- Call
resolve_marketfrom the OracleCap-holding account to post the result
Processing after receiving the MarketResolved event:
- Retrieve the winner list from the off-chain database
- Credit each winner's point balance with their payout amount
- Send payout completion notifications to users
When a user requests to verify that their prediction was included:
- Retrieve the relevant prediction from the off-chain database
- Reconstruct the Merkle tree and generate the Merkle proof for that prediction
- Return the proof to the user
- The user (or a third-party verifier) compares against the on-chain Merkle root
This module separates AdminCap and OracleCap as an important design decision for enhancing prediction market trustworthiness.
AdminCap (Operator Capability):
- Market creation
- Ending prediction intake and posting snapshots
- Market cancellation in exceptional cases
OracleCap (Oracle Capability):
- Match result finalization only
At initial deployment, both capabilities are held by the same account, but future separation is possible:
AdminCap: Held by the ONE KATAKURI operations teamOracleCap: Delegated to ONE Championship officially or an independent third-party oracle
This enables a public-private separation where "result finalization authority rests with ONE, operational authority with KATAKURI" — structurally eliminating concerns about operator-manipulated results. This is a strong selling point when pitching to ONE Championship.
Recording every prediction on-chain causes gas costs to scale linearly with prediction volume. Conversely, recording only the result and winners on-chain fails to guarantee the trustworthiness of live sentiment data.
Merkle roots resolve this tradeoff:
- On-chain Tx per market is kept to 3–4
- The total prediction volume and distribution at close time is made tamper-proof
- Users can verify their prediction was included via a Merkle proof
predictions_merkle_root (at close_market time)
A hash of the full prediction list at close time. Each leaf is expected to contain:
leaf = hash(user_address || option_index || stake_weight || timestamp)
Users can prove "I made a prediction" via Merkle proof, without relying on the operator. This makes the final snapshot of real-time sentiment data distributed by the brand amplification engine permanently verifiable.
winners_merkle_root (at resolve_market time)
A hash of the winner list. Each leaf is expected to contain:
leaf = hash(user_address || payout_amount)
Users can prove "I am on the winner list" and "the correct payout amount is recorded" via Merkle proof — structurally eliminating concerns about arbitrary payouts by the operator.
// Pseudocode
const onChainRoot = await suiClient.getMarketWinnersRoot(marketId);
const myProof = await katakuriApi.getMyMerkleProof(marketId, userAddress);
const myLeaf = computeLeaf(userAddress, myPayoutAmount);
const computed = verifyMerkleProof(myLeaf, myProof, onChainRoot);
if (computed === onChainRoot) {
// On-chain verification complete: I am on the winner list
}Each market follows this lifecycle:
create_market
[Initial] ──────────────────────────► STATUS_OPEN
│
│ close_market
│ (after closes_at)
▼
STATUS_CLOSED ────┐
│ │
│ resolve_ │ cancel_market
│ market │ (exceptional)
▼ ▼
STATUS_RESOLVED STATUS_CANCELLED
│ │
▼ ▼
Off-chain Off-chain
Batch Payout Batch Refund
- Sui CLI 1.x or higher
- Move 2024 Edition support
- A Sui testnet or devnet wallet for testing
[package]
name = "katakuri"
version = "0.1.0"
edition = "2024.beta"
[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }
[addresses]
katakuri = "0x0"sui move buildsui move testsui client publish --gas-budget 100000000After successful deployment, the following are output:
- Package ID
AdminCapobject ID (transferred to deployer)OracleCapobject ID (transferred to deployer)
Set these IDs as environment variables in the off-chain server.
sui client call \
--package $PACKAGE_ID \
--module prediction_market \
--function create_market \
--args \
$ADMIN_CAP_ID \
"TAKERU vs RODTANG · WINNER" \
"Interim Flyweight Kickboxing World Title at ONE SAMURAI 1" \
"ONE_SAMURAI_1" \
"WINNER" \
"https://walrus.example/markets/takeru-rodtang.json" \
'["TAKERU","RODTANG"]' \
1745859300000 \
1745895300000 \
"LMSR" \
"0x..." \
500 \
$CLOCK_OBJ \
--gas-budget 100000000During the hackathon period, focus remains on this module. The following are planned for Sui Overflow or later phases.
Records users' prediction track records as non-transferable Soulbound Tokens. Derived from MarketResolved events, the following SBTs can be issued:
FIRST_10_WINS: 10 cumulative wins achievedTAKERU_ERA_FAN: Prediction history on Takeru-related marketsONE_SAMURAI_1_WITNESS: Proof of participation in ONE SAMURAI 1JAPANESE_FIGHTER_LOYALIST: Ongoing prediction history on Japanese fightersUPSET_CALLER: Record of correctly predicting low-odds outcomesMETHOD_MASTER: High accuracy rate on method-of-victory predictions
Conducts on-chain lotteries for high-value prizes (wBTC, VIP experiences, etc.) using Sui's VRF. Also contributes to trustworthiness compliance under Japan's Prize Notation Act.
Issues limited on-chain commemorative NFTs for historic moments like Takeru's retirement bout at ONE SAMURAI 1. Combined with Walrus for decentralized image/video storage.
Implements access control for limited rewards for correct predictors (exclusive videos, player digital signatures, etc.) using Seal. Conditions such as "decryptable only by holders of a specific SBT" can be expressed in smart contracts.
Implements off-chain verification of complex combat sports stats and multi-source result consensus using Nautilus's verification layer, writing only the validity proof on-chain.