Skip to content

Commit

Permalink
community nft features
Browse files Browse the repository at this point in the history
  • Loading branch information
mubarak23 committed Oct 3, 2024
1 parent fd452a7 commit 491b016
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 84 deletions.
2 changes: 2 additions & 0 deletions src/base/constants/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ pub mod Errors {
pub const COMMUNITY_DOES_NOT_EXIST: felt252 = 'Karst: Community does not exist';
pub const NOT_COMMUNITY_OWNER: felt252 = 'Karst: Not Community owner';
pub const NOT_MEMBER: felt252 = 'Karst: Not a Community Member';
pub const NOT_TOKEN_OWNER: felt252 = 'Karst: Not a Token Owner';
pub const TOKEN_DOES_NOT_EXIST: felt252 = 'Karst: Token does not exist';
}
4 changes: 2 additions & 2 deletions src/base/constants/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ pub struct CommunityGateKeepDetails {
// permissioned_addresses: Vec<ContractAddress>,

#[derive(Debug, Drop, Serde, starknet::Store, Clone, PartialEq)]
enum GateKeepType {
pub enum GateKeepType {
None,
NFTGating,
PermissionedGating,
Expand All @@ -238,7 +238,7 @@ enum GateKeepType {


#[derive(Debug, Drop, Serde, starknet::Store, Clone, PartialEq)]
enum CommunityType {
pub enum CommunityType {
Free,
Standard,
Business
Expand Down
45 changes: 24 additions & 21 deletions src/community/community.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ mod CommunityComponent {
// update community member count
community.community_total_members = community.community_total_members - 1;
self.communities.write(community_id, community);
// this function will also burn the nft on leaving
// this function will also burn the nft on leaving
// call the burn function from the community nft contract

}
Expand All @@ -233,7 +233,7 @@ mod CommunityComponent {
fn add_community_mods(
ref self: ComponentState<TContractState>, community_id: u256, moderator: ContractAddress
) {
// only community owner should be able to set a mod
// only community owner should be able to set a mod

let community_owner = self.community_owner.read(community_id);
assert(community_owner == get_caller_address, NOT_COMMUNITY_OWNER);
Expand Down Expand Up @@ -268,16 +268,15 @@ mod CommunityComponent {
let community_mods = self.community_mod.read(community_id);

let mut index = 0;
while index < community_mods
.len() {
let mod_address = community_mods.at(index);
if mod_address == moderator {
community_mod.append().write(0x0);
self.community_mod.write(community_id, community_mods);
break ();
}
index = index + 1;
while index < community_mods.len() {
let mod_address = community_mods.at(index);
if mod_address == moderator {
community_mod.append().write(0x0);
self.community_mod.write(community_id, community_mods);
break ();
}
index = index + 1;
}

self.community_mod.write(community_id, community_mod);
self
Expand All @@ -303,18 +302,17 @@ mod CommunityComponent {
let community_mods = self.community_mod.read(community_id);

let mut index = 0;
while index < community_mods
.len() {
let mod_address = community_mods.at(index);
if mod_address == caller {
caller_is_mod = true;
break ();
}
index = index + 1;
while index < community_mods.len() {
let mod_address = community_mods.at(index);
if mod_address == caller {
caller_is_mod = true;
break ();
}
index = index + 1;
}

// if call is not mod,
// check for community onwer
// if call is not mod,
// check for community onwer
let community_owner_adddress = self.community_owner.read(community_id);

let caller_is_owner = caller == community_owner_address;
Expand Down Expand Up @@ -506,4 +504,9 @@ mod CommunityComponent {
(true, gate_keep.gate_keep_type)
}
}

// *************************************************************************
// PRIVATE FUNCTIONS
// *************************************************************************

}
56 changes: 0 additions & 56 deletions src/community/community_nft.cairo

This file was deleted.

133 changes: 133 additions & 0 deletions src/communitynft/community_nft.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#[starknet::contract]
pub mod CommunityNft {
use starknet::{ContractAddress, get_block_timestamp, get_caller_address};
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc721::{ERC721Component, ERC721HooksEmptyImpl};

use karst::interfaces::ICommunityNft::ICommunityNft;
use karst::interfaces::IHub::{IHubDispatcher, IHubDispatcherTrait};
use karst::base::{
constants::errors::Errors::{ALREADY_MINTED, NOT_TOKEN_OWNER, TOKEN_DOES_NOT_EXIST},
utils::base64_extended::convert_into_byteArray
};
use starknet::storage::{
Map, StoragePointerWriteAccess, StoragePointerReadAccess, StorageMapReadAccess,
StorageMapWriteAccess
};
component!(path: ERC721Component, storage: erc721, event: ERC721Event);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// ERC721 Mixin
#[abi(embed_v0)]
impl ERC721MixinImpl = ERC721Component::ERC721MixinImpl<ContractState>;
impl ERC721InternalImpl = ERC721Component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
erc721: ERC721Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
karst_hub: ContractAddress,
last_minted_id: u256,
mint_timestamp: Map<u256, u64>,
user_token_id: Map<ContractAddress, u256>,
profile_address: ContractAddress,
community_id: u256
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC721Event: ERC721Component::Event,
#[flat]
SRC5Event: SRC5Component::Event
}

#[constructor]
fn constructor(
ref self: ContractState,
karst_hub: ContractAddress,
profile_address: ContractAddress,
community_id: u256
) {
self.karst_hub.write(karst_hub);
self.profile_address.write(profile_address);
self.community_id.write(community_id);
}

#[abi(embed_v0)]
impl CommunityNft of ICommunityNft<ContractState> {
// *************************************************************************
// EXTERNAL
// *************************************************************************

/// @notice mints the user community NFT
/// @param address address of user trying to mint the community NFT token
fn mint(ref self: ContractState, user_address: ContractAddress) -> u256 {
let balance = self.erc721.balance_of(user_address);
assert(balance.is_zero(), ALREADY_MINTED);

let mut token_id = self.last_minted_id.read() + 1;
self.erc721.mint(user_address, token_id);
let timestamp: u64 = get_block_timestamp();
self.user_token_id.write(user_address, token_id);
self.last_minted_id.write(token_id);
self.mint_timestamp.write(token_id, timestamp);
self.last_minted_id.read()
}

/// @notice burn the user community NFT
/// @param address address of user trying to burn the community NFT token
fn burn(ref self: ContractState, user_address: ContractAddress, token_id: u256) {
let user_token_id = self.user_token_id.read();
assert(user_token_id == token_id, NOT_TOKEN_OWNER);
// check the token exist in erc721
assert(self.erc721.exists(token_id), TOKEN_DOES_NOT_EXIST);
self.erc721.burn(token_id);
self.user_token_id.write(user_address, 0);
}

// *************************************************************************
// GETTERS
// *************************************************************************
/// @notice gets the token ID for a user address
/// @param user address of user to retrieve token ID for
fn get_user_token_id(self: @ContractState, user_address: ContractAddress) -> u256 {
self.user_token_id.read(user_address)
}

// *************************************************************************
// METADATA
// *************************************************************************
/// @notice returns the community name
fn name(self: @ContractState) -> ByteArray {
let mut collection_name = ArrayTrait::<felt252>::new();
let profile_address_felt252: felt252 = self.profile_address.read().into();
let community_id_felt252: felt252 = self.community_id.read().try_into().unwrap();
collection_name.append('Karst Community | Profile #');
collection_name.append(profile_address_felt252);
collection_name.append('- Community #');
collection_name.append(community_id_felt252);
let collection_name_byte = convert_into_byteArray(ref collection_name);
collection_name_byte
}

/// @notice returns the collection symbol
fn symbol(self: @ContractState) -> ByteArray {
return "KARST:COMMUNITY";
}

/// @notice returns the token_uri for a particular token_id
fn token_uri(self: @ContractState, token_id: u256) -> ByteArray {
assert(self.erc721.exists(token_id), TOKEN_DOES_NOT_EXIST);
let profile_address = self.profile_address.read();
let community_id = self.community_id.read();
let karst_hub = self.karst_hub.read();
let token_uri = IHubDispatcher { contract_address: karst_hub }
.get_publication_content_uri(profile_address, community_id);
token_uri
}
}
}
4 changes: 1 addition & 3 deletions src/interfaces/ICommunity.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use starknet::ContractAddress;
use karst::base::constants::types::{
GateKeepType, CommunityType, CommunityDetails, CommunityGateKeepDetails
};
use karst::base::constants::types::{GateKeepType, CommunityType, CommunityDetails};

// *************************************************************************
// INTERFACE of ICommunity
Expand Down
21 changes: 19 additions & 2 deletions src/interfaces/ICommunityNft.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,23 @@ use starknet::ContractAddress;
// *************************************************************************
#[starknet::interface]
pub trait ICommunityNft<TContractState> {
fn mint(ref self: TContractState, to: ContractAddress, token_id: u256);
fn burn(ref self: TContractState, token_id: u256);
// *************************************************************************
// EXTERNALS
// *************************************************************************

fn mint_nft(ref self: TContractState, user_address: ContractAddress) -> u256;
fn burn_nft(ref self: TContractState, user_address: ContractAddress, token_id: u256);

// *************************************************************************
// GETTERS
// *************************************************************************
fn get_user_token_id(self: @ContractState, user_address: ContractAddress) -> u256;


// *************************************************************************
// METADATA
// *************************************************************************
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn token_uri(self: @TState, token_id: u256) -> ByteArray;
}

0 comments on commit 491b016

Please sign in to comment.