diff --git a/src/base/constants/errors.cairo b/src/base/constants/errors.cairo index 504fbe4..2e65a80 100644 --- a/src/base/constants/errors.cairo +++ b/src/base/constants/errors.cairo @@ -36,4 +36,5 @@ pub mod Errors { pub const AUTO_RENEW_DURATION_ENDED: felt252 = 'Karst: auto renew ended!'; pub const INVALID_JOLT: felt252 = 'Karst: invalid jolt!'; pub const INVALID_JOLT_RECIPIENT: felt252 = 'Karst: not request recipient!'; + pub const INVALID_LENGTH: felt252 = 'Karst: Invalid Length'; } diff --git a/src/community/community.cairo b/src/community/community.cairo index 01b9347..9c6421a 100644 --- a/src/community/community.cairo +++ b/src/community/community.cairo @@ -19,7 +19,7 @@ pub mod CommunityComponent { }; use karst::base::constants::errors::Errors::{ ALREADY_MEMBER, NOT_COMMUNITY_OWNER, NOT_MEMBER, BANNED_MEMBER, UNAUTHORIZED, - ONLY_PREMIUM_COMMUNITIES + ONLY_PREMIUM_COMMUNITIES, INVALID_LENGTH }; @@ -355,8 +355,8 @@ pub mod CommunityComponent { fn set_ban_status( ref self: ComponentState, community_id: u256, - profile: ContractAddress, - ban_status: bool + profiles: Array, + ban_statuses: Array ) { let caller = get_caller_address(); let is_community_mod = self.community_mod.read((community_id, caller)); @@ -365,26 +365,8 @@ pub mod CommunityComponent { // check caller is mod or owner assert(is_community_mod == true || community_owner == caller, UNAUTHORIZED); - // check profile is a community member - let is_community_member = self.is_community_member(profile, community_id); - assert(is_community_member == true, NOT_MEMBER); - - // update storage - let community_member = self.community_member.read((community_id, profile)); - let updated_member = CommunityMember { ban_status: ban_status, ..community_member }; - self.community_member.write((community_id, profile), updated_member); - - // emit event - self - .emit( - CommunityBanStatusUpdated { - community_id: community_id, - transaction_executor: caller, - profile: profile, - ban_status: ban_status, - block_timestamp: get_block_timestamp() - } - ); + // _set_ban_statu + self._set_ban_status(community_id, profiles, ban_statuses); } /// @notice upgrades a community @@ -699,7 +681,7 @@ pub mod CommunityComponent { /// @notice internal function for remove community mod /// @param community_id id of community - // @param moderator to remove + // @param moderators to remove fn _remove_community_mods( ref self: ComponentState, community_id: u256, @@ -730,6 +712,48 @@ pub mod CommunityComponent { }; } + /// @notice internal function for set ban status for members + /// @param community_id id of community to be banned or unbanned + /// @param profiles addresses + /// @param ban_statuses bool values + fn _set_ban_status( + ref self: ComponentState, + community_id: u256, + profiles: Array, + ban_statuses: Array + ) { + assert(profiles.len() == ban_statuses.len(), INVALID_LENGTH); + // for permissioned gating update array of addresses + let length = profiles.len(); + let mut index: u32 = 0; + + while index < length { + let profile = *profiles[index]; + let ban_status = *ban_statuses[index]; + // check profile is a community member + let is_community_member = self.is_community_member(profile, community_id); + assert(is_community_member == true, NOT_MEMBER); + + // update storage + let community_member = self.community_member.read((community_id, profile)); + let updated_member = CommunityMember { ban_status: ban_status, ..community_member }; + self.community_member.write((community_id, profile), updated_member); + + // emit event + self + .emit( + CommunityBanStatusUpdated { + community_id: community_id, + transaction_executor: get_caller_address(), + profile: profile, + ban_status: ban_status, + block_timestamp: get_block_timestamp() + } + ); + index += 1; + }; + } + /// @notice internal function to deploy a community nft /// @param community_id id of community /// @param salt for randomization diff --git a/src/interfaces/ICommunity.cairo b/src/interfaces/ICommunity.cairo index 6096ff1..e9e7d30 100644 --- a/src/interfaces/ICommunity.cairo +++ b/src/interfaces/ICommunity.cairo @@ -21,7 +21,10 @@ pub trait ICommunity { ref self: TState, community_id: u256, moderators: Array ); fn set_ban_status( - ref self: TState, community_id: u256, profile: ContractAddress, ban_status: bool + ref self: TState, + community_id: u256, + profiles: Array, + ban_statuses: Array ); fn upgrade_community(ref self: TState, community_id: u256, upgrade_type: CommunityType); fn gatekeep( diff --git a/tests/test_community.cairo b/tests/test_community.cairo index 9ca058d..b687228 100644 --- a/tests/test_community.cairo +++ b/tests/test_community.cairo @@ -490,148 +490,188 @@ fn should_panic_if_caller_removing_mod_is_not_owner() { communityDispatcher.remove_community_mods(community_id, moderators); } -// #[test] -// fn test_set_ban_status_by_owner() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// // let salt: felt252 = 'djkngkylu349586'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(CommunityType::Free); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); - -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); - -// assert(is_ban == true, 'Community Member is not banned'); - -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_set_ban_status_by_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; - -// start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); - -// let community_id = communityDispatcher.create_comminuty(CommunityType::Free); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_SIX.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); - -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_FOUR.try_into().unwrap()); - -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); - -// assert(is_ban == true, 'Community Member is not banned'); - -// stop_cheat_caller_address(community_contract_address); -// } - -// #[test] -// fn test_set_ban_status_emit_event() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// // let salt: felt252 = '495ksjdhfgjrkf86'; -// // start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// // //create the community -// // let community_id = communityDispatcher.create_comminuty(CommunityType::Free); -// // // join the community -// // communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// // communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// // communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// // communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// // // add a community mod -// // communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); -// // stop_cheat_caller_address(community_contract_address); - -// // spy on emitted events -// let mut spy = spy_events(); -// start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); -// let community_id = communityDispatcher.create_comminuty(CommunityType::Free); -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_SIX.try_into().unwrap(), community_id); -// // add a community mod -// communityDispatcher.add_community_mods(community_id, USER_SIX.try_into().unwrap()); - -// // set ban -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); - -// spy -// .assert_emitted( -// @array![ -// ( -// community_contract_address, -// CommunityComponent::Event::CommunityBanStatusUpdated( -// CommunityComponent::CommunityBanStatusUpdated { -// community_id: community_id, -// transaction_executor: USER_SIX.try_into().unwrap(), -// profile: USER_TWO.try_into().unwrap(), -// ban_status: true, -// block_timestamp: get_block_timestamp() -// } -// ) -// ) -// ] -// ); -// } - -// #[test] -// #[should_panic(expected: ('Karst: user unauthorized!',))] -// fn test_should_panic_if_caller_to_set_ban_status_is_not_owner_or_mod() { -// let community_contract_address = __setup__(); - -// let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address -// }; -// // let salt: felt252 = 'djkrtyhjejfg6'; -// start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); -// //create the community -// let community_id = communityDispatcher.create_comminuty(CommunityType::Free); -// // join the community -// communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); -// communityDispatcher.join_community(USER_SIX.try_into().unwrap(), community_id); - -// // mode array -// let mut moderators = ArrayTrait::new(); -// moderators.append(USER_SIX.try_into().unwrap()); -// moderators.append(USER_FIVE.try_into().unwrap()); - -// // add a community mod -// communityDispatcher.add_community_mods(community_id, moderators); - -// stop_cheat_caller_address(community_contract_address); - -// start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); -// communityDispatcher.set_ban_status(community_id, USER_TWO.try_into().unwrap(), true); -// } +#[test] +fn test_set_ban_status_by_owner() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_SIX.try_into().unwrap(), community_id); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_SIX.try_into().unwrap()); + profiles.append(USER_FOUR.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + let is_ban = communityDispatcher.get_ban_status(USER_FOUR.try_into().unwrap(), community_id); + + assert(is_ban == true, 'Community Member is not banned'); + + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_set_ban_status_by_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); + + // add a community mod + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_FOUR.try_into().unwrap()); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_THREE.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + let is_ban = communityDispatcher.get_ban_status(USER_TWO.try_into().unwrap(), community_id); + + assert(is_ban == true, 'Community Member is not banned'); + + stop_cheat_caller_address(community_contract_address); +} + +#[test] +fn test_set_ban_status_emit_event() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + // + + // spy on emitted events + let mut spy = spy_events(); + start_cheat_caller_address(community_contract_address, USER_SIX.try_into().unwrap()); + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + communityDispatcher.join_community(USER_ONE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); + // add a community mod + // add mod array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_FOUR.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_THREE.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + // set ban + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); + + spy + .assert_emitted( + @array![ + ( + community_contract_address, + CommunityComponent::Event::CommunityBanStatusUpdated( + CommunityComponent::CommunityBanStatusUpdated { + community_id: community_id, + transaction_executor: USER_SIX.try_into().unwrap(), + profile: USER_TWO.try_into().unwrap(), + ban_status: true, + block_timestamp: get_block_timestamp() + } + ) + ) + ] + ); +} + +#[test] +#[should_panic(expected: ('Karst: user unauthorized!',))] +fn test_should_panic_if_caller_to_set_ban_status_is_not_owner_or_mod() { + let community_contract_address = __setup__(); + + let communityDispatcher = ICommunityDispatcher { contract_address: community_contract_address }; + + start_cheat_caller_address(community_contract_address, USER_ONE.try_into().unwrap()); + //create the community + let community_id = communityDispatcher.create_comminuty(CommunityType::Free); + // join the community + communityDispatcher.join_community(USER_TWO.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_THREE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_FOUR.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_FIVE.try_into().unwrap(), community_id); + communityDispatcher.join_community(USER_SIX.try_into().unwrap(), community_id); + + // mode array + let mut moderators = ArrayTrait::new(); + moderators.append(USER_SIX.try_into().unwrap()); + moderators.append(USER_FIVE.try_into().unwrap()); + + // add a community mod + communityDispatcher.add_community_mods(community_id, moderators); + + stop_cheat_caller_address(community_contract_address); + + start_cheat_caller_address(community_contract_address, USER_THREE.try_into().unwrap()); + // ban status list + let mut ban_statuses = ArrayTrait::new(); + ban_statuses.append(true); + ban_statuses.append(true); + + // ban profile list + let mut profiles = ArrayTrait::new(); + profiles.append(USER_FOUR.try_into().unwrap()); + profiles.append(USER_TWO.try_into().unwrap()); + // set ban + communityDispatcher.set_ban_status(community_id, profiles, ban_statuses); +} // TEST TODO: create a test fn called `test_can_only_set_ban_status_for_members` to check that you // can only ban existing members +// TEST TODO: TEST To make sure lenght of ban status and profiles are same +// TEST TODO: panic test + #[test] fn test_community_upgrade() { let community_contract_address = __setup__();