Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions contracts/predictify-hybrid/src/disputes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ pub struct Dispute {
pub status: DisputeStatus,
}

#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DisputeDecayConfig {
pub half_life_seconds: u64,
pub floor_bps: u32,
}

/// Represents the current lifecycle status of a dispute.
///
/// Disputes progress through various states from creation to final resolution.
Expand Down Expand Up @@ -2723,12 +2730,16 @@ impl DisputeUtils {

// Update voting statistics
voting_data.total_votes += 1;

// Calculate the decayed stake using tally_votes
let decayed_stake = Self::tally_votes(env, vote.stake, vote.timestamp, voting_data.voting_start);

if vote.vote {
voting_data.support_votes += 1;
voting_data.total_support_stake += vote.stake;
voting_data.total_support_stake += decayed_stake;
} else {
voting_data.against_votes += 1;
voting_data.total_against_stake += vote.stake;
voting_data.total_against_stake += decayed_stake;
}

// Store updated voting data
Expand All @@ -2740,6 +2751,46 @@ impl DisputeUtils {
Ok(())
}

/// Calculate the stake weight using exponential decay approximation
/// so late votes count less than early votes.
pub fn tally_votes(env: &Env, raw_stake: i128, vote_time: u64, window_start: u64) -> i128 {
let config_key = symbol_short!("decaycfg");
let config: Option<DisputeDecayConfig> = env.storage().persistent().get(&config_key);

let cfg = match config {
Some(c) => c,
None => return raw_stake,
};

if cfg.half_life_seconds == 0 {
return raw_stake;
}

let elapsed = vote_time.saturating_sub(window_start);
let num_half_lives = elapsed / cfg.half_life_seconds;
let rem = elapsed % cfg.half_life_seconds;

let shift = num_half_lives.min(16) as u32;
let weight_at_n = 10000u32.checked_shr(shift).unwrap_or(0);
let weight_at_n_plus_1 = 10000u32.checked_shr(shift + 1).unwrap_or(0);

let diff = weight_at_n.saturating_sub(weight_at_n_plus_1);
let exact_weight = weight_at_n.saturating_sub((diff as u64 * rem / cfg.half_life_seconds) as u32);

let final_weight = exact_weight.max(cfg.floor_bps);

(raw_stake * final_weight as i128) / 10000
}

pub fn set_dispute_decay_config(env: &Env, admin: Address, config: DisputeDecayConfig) -> Result<(), Error> {
admin.require_auth();
DisputeValidator::validate_admin_permissions(env, &admin)?;
let key = symbol_short!("decaycfg");
env.storage().persistent().set(&key, &config);
env.storage().persistent().extend_ttl(&key, 535680, 535680);
Ok(())
}

/// Get dispute voting data
pub fn get_dispute_voting(env: &Env, dispute_id: &Symbol) -> Result<DisputeVoting, Error> {
let key = (symbol_short!("dispute_v"), dispute_id.clone());
Expand Down
2 changes: 2 additions & 0 deletions docs/contracts/EVENT_ARCHIVE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

Bounded on-chain storage for resolved and cancelled prediction markets, with paginated historical queries.



## Overview

`EventArchive` (in `contracts/predictify-hybrid/src/event_archive.rs`) lets admins mark resolved or cancelled markets as archived and exposes read-only paginated queries for analytics and UI. Only public metadata is returned β€” no votes, individual stakes, or addresses.
Expand Down
1 change: 1 addition & 0 deletions scripts/check_wasm_size.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash
set -e


# Default budget: 768 KiB = 768 * 1024 = 786432 bytes
BUDGET=${WASM_SIZE_BUDGET:-786432}

Expand Down
Loading