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
64 changes: 64 additions & 0 deletions contracts/geev-core/src/governance.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
use soroban_sdk::{contract, contractevent, contractimpl, Address, Env};

use crate::types::{DataKey, Error};

#[contract]
pub struct GovernanceContract;

#[contractevent]
pub struct ContentFlagged {
#[topic]
target_id: u64,
user: Address,
count: u32,
}

#[contractimpl]
impl GovernanceContract {
/// Flag a piece of content (Giveaway or HelpRequest) by its ID.
/// Each user may only flag a given ID once.
pub fn flag_content(env: Env, user: Address, target_id: u64) -> Result<(), Error> {
// 1. Verify caller signature
user.require_auth();

// 2. Prevent duplicate flags from the same user
let flag_key = DataKey::FlagRecord(target_id, user.clone());
if env.storage().persistent().has(&flag_key) {
return Err(Error::AlreadyFlagged);
}

// 3. Record that this user has flagged this ID
env.storage().persistent().set(&flag_key, &true);

// 4. Increment the total flag count for this ID
let count_key = DataKey::FlagCount(target_id);
let current: u32 = env.storage().persistent().get(&count_key).unwrap_or(0);
let new_count = current.checked_add(1).ok_or(Error::ArithmeticOverflow)?;
env.storage().persistent().set(&count_key, &new_count);

// 5. Emit "ContentFlagged" event: topics = (name, target_id), data = (user, total_flags)
ContentFlagged {
target_id,
user,
count: new_count,
}
.publish(&env);

Ok(())
}

/// Returns the total number of flags for a given content ID.
pub fn get_flag_count(env: Env, target_id: u64) -> u32 {
env.storage()
.persistent()
.get(&DataKey::FlagCount(target_id))
.unwrap_or(0)
}

/// Returns whether a specific user has already flagged a given content ID.
pub fn has_flagged(env: Env, user: Address, target_id: u64) -> bool {
env.storage()
.persistent()
.has(&DataKey::FlagRecord(target_id, user))
}
}
2 changes: 2 additions & 0 deletions contracts/geev-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod access;
pub mod admin;
pub mod giveaway;
pub mod governance;
pub mod mutual_aid;
pub mod profile;
pub mod types;
Expand All @@ -15,6 +16,7 @@ pub use crate::admin::AdminContract;
pub use crate::admin::AdminContractClient;
pub use crate::giveaway::GiveawayContract;
pub use crate::giveaway::GiveawayContractClient;
pub use crate::governance::{GovernanceContract, GovernanceContractClient};
pub use crate::mutual_aid::MutualAidContract;
pub use crate::mutual_aid::MutualAidContractClient;
pub use crate::profile::{ProfileContract, ProfileContractClient};
86 changes: 86 additions & 0 deletions contracts/geev-core/src/test.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::access::check_admin;
use crate::admin::{AdminContract, AdminContractClient};
use crate::giveaway::{GiveawayContract, GiveawayContractClient};
use crate::governance::{GovernanceContract, GovernanceContractClient};
use crate::mutual_aid::{MutualAidContract, MutualAidContractClient};
use crate::profile::{ProfileContract, ProfileContractClient};
use crate::types::{DataKey, HelpRequest, HelpRequestStatus};
Expand Down Expand Up @@ -1312,3 +1313,88 @@ fn test_donate_event_emits_exact_amount_and_total() {
"DonationReceived event did not contain the exact amount_donated and new_total_raised"
);
}

// ── Governance / flag_content tests ──────────────────────────────────────────
#[test]
fn test_flag_content_increments_count() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(GovernanceContract, ());
let client = GovernanceContractClient::new(&env, &contract_id);

let user = Address::generate(&env);
let target_id: u64 = 42;

assert_eq!(client.get_flag_count(&target_id), 0);

client.flag_content(&user, &target_id);

assert_eq!(client.get_flag_count(&target_id), 1);
}

#[test]
fn test_flag_content_multiple_users() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(GovernanceContract, ());
let client = GovernanceContractClient::new(&env, &contract_id);

let user_a = Address::generate(&env);
let user_b = Address::generate(&env);
let target_id: u64 = 7;

client.flag_content(&user_a, &target_id);
client.flag_content(&user_b, &target_id);

assert_eq!(client.get_flag_count(&target_id), 2);
assert!(client.has_flagged(&user_a, &target_id));
assert!(client.has_flagged(&user_b, &target_id));
}

#[test]
#[should_panic]
fn test_flag_content_duplicate_panics() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(GovernanceContract, ());
let client = GovernanceContractClient::new(&env, &contract_id);

let user = Address::generate(&env);
let target_id: u64 = 1;

client.flag_content(&user, &target_id);
// Second flag from the same user must panic with AlreadyFlagged
client.flag_content(&user, &target_id);
}

#[test]
fn test_has_flagged_returns_false_before_flag() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(GovernanceContract, ());
let client = GovernanceContractClient::new(&env, &contract_id);

let user = Address::generate(&env);
assert!(!client.has_flagged(&user, &99u64));
}

#[test]
fn test_flag_counts_are_independent_per_id() {
let env = Env::default();
env.mock_all_auths();

let contract_id = env.register(GovernanceContract, ());
let client = GovernanceContractClient::new(&env, &contract_id);

let user = Address::generate(&env);

client.flag_content(&user, &1u64);

// ID 2 should still be at 0
assert_eq!(client.get_flag_count(&2u64), 0);
assert_eq!(client.get_flag_count(&1u64), 1);
}
3 changes: 3 additions & 0 deletions contracts/geev-core/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub enum Error {
HelpRequestAlreadyExists = 16,
TokenNotSupported = 17,
UsernameTaken = 18,
AlreadyFlagged = 19,
}

#[derive(Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -83,6 +84,8 @@ pub enum DataKey {
AllowedToken(Address),
Profile(Address),
Username(String),
FlagRecord(u64, Address),
FlagCount(u64),
}

#[derive(Clone, Debug, Eq, PartialEq)]
Expand Down
Loading
Loading