Skip to content
Merged
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
8 changes: 4 additions & 4 deletions contract/contracts/predifi-contract/src/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use super::*;
use crate::test_utils::TokenTestContext;
use soroban_sdk::{
testutils::{Address as _, Ledger},
Address, Env,
Address, Env, String,
};

mod dummy_access_control {
Expand Down Expand Up @@ -74,7 +74,7 @@ fn test_full_market_lifecycle() {

// 1. Create Pool
let end_time = 1000u64;
let pool_id = client.create_pool(&end_time, &token_ctx.token_address);
let pool_id = client.create_pool(&end_time, &token_ctx.token_address, &String::from_str(&env, "Test Pool"), &String::from_str(&env, "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"));

// 2. Place Predictions
client.place_prediction(&user1, &pool_id, &100, &1); // User 1 bets 100 on Outcome 1
Expand Down Expand Up @@ -131,7 +131,7 @@ fn test_multi_user_betting_and_balance_verification() {
token_ctx.mint(&user, 5000);
}

let pool_id = client.create_pool(&2000u64, &token_ctx.token_address);
let pool_id = client.create_pool(&2000u64, &token_ctx.token_address, &String::from_str(&env, "Test Pool"), &String::from_str(&env, "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"));

// Bets:
// U0: 500 on 1
Expand Down Expand Up @@ -182,7 +182,7 @@ fn test_market_resolution_multiple_winners() {
token_ctx.mint(&user2, 1000);
token_ctx.mint(&user3, 1000);

let pool_id = client.create_pool(&1500u64, &token_ctx.token_address);
let pool_id = client.create_pool(&1500u64, &token_ctx.token_address, &String::from_str(&env, "Test Pool"), &String::from_str(&env, "ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi"));

// Bets:
// U1: 200 on 1
Expand Down
27 changes: 25 additions & 2 deletions contract/contracts/predifi-contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

use soroban_sdk::{
contract, contracterror, contractevent, contractimpl, contracttype, token, Address, Env,
IntoVal, Symbol, Vec,
IntoVal, String, Symbol, Vec,
};

const DAY_IN_LEDGERS: u32 = 17280;
Expand All @@ -25,6 +25,10 @@ pub struct Pool {
pub outcome: u32,
pub token: Address,
pub total_stake: i128,
/// A short human-readable description of the event being predicted.
pub description: String,
/// A URL (e.g. IPFS CIDv1) pointing to extended metadata for this pool.
pub metadata_url: String,
}

#[contracttype]
Expand Down Expand Up @@ -109,6 +113,8 @@ pub struct PoolCreatedEvent {
pub pool_id: u64,
pub end_time: u64,
pub token: Address,
/// Metadata URL included so off-chain indexers can immediately fetch context.
pub metadata_url: String,
}

#[contractevent(topics = ["pool_resolved"])]
Expand Down Expand Up @@ -274,12 +280,26 @@ impl PredifiContract {
}

/// Create a new prediction pool. Returns the new pool ID.
pub fn create_pool(env: Env, end_time: u64, token: Address) -> u64 {
///
/// # Arguments
/// * `end_time` - Unix timestamp after which no more predictions are accepted.
/// * `token` - The Stellar token contract address used for staking.
/// * `description` - Short human-readable description of the event (max 256 bytes).
/// * `metadata_url` - URL pointing to extended metadata, e.g. an IPFS link (max 512 bytes).
pub fn create_pool(
env: Env,
end_time: u64,
token: Address,
description: String,
metadata_url: String,
) -> u64 {
Self::require_not_paused(&env);
assert!(
end_time > env.ledger().timestamp(),
"end_time must be in the future"
);
assert!(description.len() <= 256, "description exceeds 256 bytes");
assert!(metadata_url.len() <= 512, "metadata_url exceeds 512 bytes");

let pool_id: u64 = env
.storage()
Expand All @@ -294,6 +314,8 @@ impl PredifiContract {
outcome: 0,
token: token.clone(),
total_stake: 0,
description,
metadata_url: metadata_url.clone(),
};

let pool_key = DataKey::Pool(pool_id);
Expand All @@ -309,6 +331,7 @@ impl PredifiContract {
pool_id,
end_time,
token,
metadata_url,
}
.publish(&env);

Expand Down
112 changes: 100 additions & 12 deletions contract/contracts/predifi-contract/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
use super::*;
use soroban_sdk::{
testutils::{Address as _, Ledger},
token, Address, Env,
token, Address, Env, String,
};

mod dummy_access_control {
Expand Down Expand Up @@ -85,7 +85,15 @@ fn test_claim_winnings() {
token_admin_client.mint(&user1, &1000);
token_admin_client.mint(&user2, &1000);

let pool_id = client.create_pool(&100u64, &token_address);
let pool_id = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
client.place_prediction(&user1, &pool_id, &100, &1);
client.place_prediction(&user2, &pool_id, &100, &2);

Expand Down Expand Up @@ -115,7 +123,15 @@ fn test_double_claim() {
let user1 = Address::generate(&env);
token_admin_client.mint(&user1, &1000);

let pool_id = client.create_pool(&100u64, &token_address);
let pool_id = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
client.place_prediction(&user1, &pool_id, &100, &1);

env.ledger().with_mut(|li| li.timestamp = 101);
Expand All @@ -137,7 +153,15 @@ fn test_claim_unresolved() {
let user1 = Address::generate(&env);
token_admin_client.mint(&user1, &1000);

let pool_id = client.create_pool(&100u64, &token_address);
let pool_id = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
client.place_prediction(&user1, &pool_id, &100, &1);

client.claim_winnings(&user1, &pool_id);
Expand All @@ -155,8 +179,24 @@ fn test_multiple_pools_independent() {
token_admin_client.mint(&user1, &1000);
token_admin_client.mint(&user2, &1000);

let pool_a = client.create_pool(&100u64, &token_address);
let pool_b = client.create_pool(&200u64, &token_address);
let pool_a = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
let pool_b = client.create_pool(
&200u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);

client.place_prediction(&user1, &pool_a, &100, &1);
client.place_prediction(&user2, &pool_b, &100, &1);
Expand Down Expand Up @@ -203,7 +243,15 @@ fn test_unauthorized_resolve_pool() {
env.mock_all_auths();

let (_, client, token_address, _, _, _, _) = setup(&env);
let pool_id = client.create_pool(&100u64, &token_address);
let pool_id = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
let not_operator = Address::generate(&env);
client.resolve_pool(&not_operator, &pool_id, &1u32);
}
Expand Down Expand Up @@ -341,7 +389,15 @@ fn test_paused_blocks_create_pool() {
client.init(&ac_id, &treasury, &0u32);

client.pause(&admin);
client.create_pool(&100u64, &token);
client.create_pool(
&100u64,
&token,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
}

#[test]
Expand Down Expand Up @@ -432,7 +488,15 @@ fn test_unpause_restores_functionality() {
client.pause(&admin);
client.unpause(&admin);

let pool_id = client.create_pool(&100u64, &token_contract);
let pool_id = client.create_pool(
&100u64,
&token_contract,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
client.place_prediction(&user, &pool_id, &10, &1);
}

Expand All @@ -448,9 +512,33 @@ fn test_get_user_predictions() {
let user = Address::generate(&env);
token_admin_client.mint(&user, &1000);

let pool0 = client.create_pool(&100u64, &token_address);
let pool1 = client.create_pool(&200u64, &token_address);
let pool2 = client.create_pool(&300u64, &token_address);
let pool0 = client.create_pool(
&100u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
let pool1 = client.create_pool(
&200u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);
let pool2 = client.create_pool(
&300u64,
&token_address,
&String::from_str(&env, "Test Pool"),
&String::from_str(
&env,
"ipfs://bafybeigdyrzt5sfp7udm7hu76uh7y26nf3efuylqabf3oclgtqy55fbzdi",
),
);

client.place_prediction(&user, &pool0, &10, &1);
client.place_prediction(&user, &pool1, &20, &2);
Expand Down