Skip to content

newt0/ONE-KATAKURI

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ONE KATAKURI: Prediction Market Module

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.


Table of Contents


Overview

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.

Target Hackathon

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).


Design Philosophy

Hybrid On/Off-chain Design

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

Three Design Principles

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.


Architecture

┌─────────────────────────────────────────────────────────────┐
│                      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     │
                              └────────────────────────┘

Module Structure

This repository contains all types, constants, functions, and events in a single module: katakuri::prediction_market.

Types (Structs)

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

Market Status

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)

Public Entry Functions

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)

Events

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

API Reference

1. create_market

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 LMSR b parameter etc. Actual calculation is off-chain
  • fee_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.

2. close_market

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.

3. resolve_market

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 winners
  • winners_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.

4. cancel_market

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.


Off-chain Responsibilities

This Move contract operates as a complete prediction market in conjunction with an off-chain server. Off-chain responsibilities are as follows.

1. User Authentication and Point Management

  • Social login processing via zkLogin
  • User point balance database
  • Recording point-earning activities (U-NEXT viewing, sponsor ad viewing, etc.)

2. Prediction Intake

  • 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

3. Market Close Processing

Processing flow triggered at close time:

  1. Fetch all predictions for the market from the off-chain database
  2. Aggregate the final total stakes per option
  3. Build a Merkle tree from the prediction list and compute the Merkle root
  4. Call close_market from the AdminCap-holding account to post the snapshot

4. Result Settlement Processing

Processing flow after the match ends:

  1. Retrieve match results from the ONE Championship official API, Sherdog, Tapology, etc.
  2. Extract the winner list and calculate each winner's payout via LMSR
  3. Build a Merkle tree from the winner list and compute the Merkle root
  4. Call resolve_market from the OracleCap-holding account to post the result

5. Batch Payout

Processing after receiving the MarketResolved event:

  1. Retrieve the winner list from the off-chain database
  2. Credit each winner's point balance with their payout amount
  3. Send payout completion notifications to users

6. Merkle Proof Provision

When a user requests to verify that their prediction was included:

  1. Retrieve the relevant prediction from the off-chain database
  2. Reconstruct the Merkle tree and generate the Merkle proof for that prediction
  3. Return the proof to the user
  4. The user (or a third-party verifier) compares against the on-chain Merkle root

Capability Separation Design

This module separates AdminCap and OracleCap as an important design decision for enhancing prediction market trustworthiness.

Role Separation

AdminCap (Operator Capability):

  • Market creation
  • Ending prediction intake and posting snapshots
  • Market cancellation in exceptional cases

OracleCap (Oracle Capability):

  • Match result finalization only

Future Delegation

At initial deployment, both capabilities are held by the same account, but future separation is possible:

  • AdminCap: Held by the ONE KATAKURI operations team
  • OracleCap: 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.


Verifiability via Merkle Root

Why Use a Merkle Root

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

Two Types of Merkle Roots

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.

Verification Flow (Off-chain)

// 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
}

Lifecycle

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

Build and Deploy

Prerequisites

  • Sui CLI 1.x or higher
  • Move 2024 Edition support
  • A Sui testnet or devnet wallet for testing

Move.toml

[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"

Build

sui move build

Test

sui move test

Deploy to Testnet

sui client publish --gas-budget 100000000

After successful deployment, the following are output:

  • Package ID
  • AdminCap object ID (transferred to deployer)
  • OracleCap object ID (transferred to deployer)

Set these IDs as environment variables in the off-chain server.

Market Creation Example (Sui CLI)

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 100000000

Future Extensions

During the hackathon period, focus remains on this module. The following are planned for Sui Overflow or later phases.

katakuri_sbt (Prediction Track Record SBT)

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 achieved
  • TAKERU_ERA_FAN: Prediction history on Takeru-related markets
  • ONE_SAMURAI_1_WITNESS: Proof of participation in ONE SAMURAI 1
  • JAPANESE_FIGHTER_LOYALIST: Ongoing prediction history on Japanese fighters
  • UPSET_CALLER: Record of correctly predicting low-odds outcomes
  • METHOD_MASTER: High accuracy rate on method-of-victory predictions

katakuri_lottery (Verifiable Lottery)

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.

katakuri_collectible (Commemorative NFT)

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.

Seal Integration

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.

Nautilus Integration

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.

Releases

No releases published

Packages

 
 
 

Contributors