Skip to content

Commit

Permalink
Merge pull request #88 from Darlington02/main
Browse files Browse the repository at this point in the history
chore: modify follow contract, update tests
  • Loading branch information
Darlington02 authored Jul 18, 2024
2 parents ec1b299 + fb4ec98 commit 6ad45eb
Show file tree
Hide file tree
Showing 10 changed files with 237 additions and 88 deletions.
8 changes: 6 additions & 2 deletions src/base/constants/types.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ use starknet::ContractAddress;
// /**
// * @notice A struct containing token follow-related data.
// *
// * @param follower_profile_address The ID of the profile using the token to follow.
// * @param followed_profile_address The ID of the profile being followed.
// * @param follower_profile_address The ID of the profile following.
// * @param followTimestamp The timestamp of the current follow, if a profile is using the token to follow.
// * @param block_status true if follower is blocked, false otherwise
// */
#[derive(Drop, Serde, starknet::Store)]
pub struct FollowData {
followed_profile_address: ContractAddress,
follower_profile_address: ContractAddress,
follow_timestamp: u64
follow_timestamp: u64,
block_status: bool,
}

// * @notice A struct containing profile data.
Expand Down
165 changes: 151 additions & 14 deletions src/follownft/follownft.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,36 @@
// *************************************************************************
// OZ ERC721
// *************************************************************************
use openzeppelin::{
token::erc721::{ERC721Component::{ERC721Metadata, ERC721Mixin, HasComponent}},
introspection::src5::SRC5Component,
};


#[starknet::interface]
trait IERC721Metadata<TState> {
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
}

#[starknet::embeddable]
impl IERC721MetadataImpl<
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+Drop<TContractState>
> of IERC721Metadata<TContractState> {
fn name(self: @TContractState) -> ByteArray {
let component = HasComponent::get_component(self);
ERC721Metadata::name(component)
}

fn symbol(self: @TContractState) -> ByteArray {
let component = HasComponent::get_component(self);
ERC721Metadata::symbol(component)
}
}

#[starknet::contract]
mod Follow {
// *************************************************************************
Expand All @@ -12,16 +45,49 @@ mod Follow {
utils::hubrestricted::HubRestricted::hub_only, token_uris::follow_token_uri::FollowTokenUri,
};

use openzeppelin::{
account, access::ownable::OwnableComponent,
token::erc721::{
ERC721Component, erc721::ERC721Component::InternalTrait as ERC721InternalTrait
},
introspection::{src5::SRC5Component}
};

component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);
component!(path: ERC721Component, storage: erc721, event: ERC721Event);

// allow to check what interface is supported
#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;
impl SRC5InternalImpl = SRC5Component::InternalImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721Impl = ERC721Component::ERC721Impl<ContractState>;
#[abi(embed_v0)]
impl ERC721CamelOnlyImpl = ERC721Component::ERC721CamelOnlyImpl<ContractState>;

// add an owner
#[abi(embed_v0)]
impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;

// *************************************************************************
// STORAGE
// *************************************************************************
#[storage]
struct Storage {
#[substorage(v0)]
erc721: ERC721Component::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage,
#[substorage(v0)]
ownable: OwnableComponent::Storage,
admin: ContractAddress,
followed_profile_address: ContractAddress,
follower_count: u256,
follow_id_by_follower_profile_address: LegacyMap<ContractAddress, u256>,
follow_data_by_follow_id: LegacyMap<u256, FollowData>,
initialized: bool,
karst_hub: ContractAddress,
}

Expand All @@ -31,9 +97,16 @@ mod Follow {
#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
ERC721Event: ERC721Component::Event,
#[flat]
SRC5Event: SRC5Component::Event,
#[flat]
OwnableEvent: OwnableComponent::Event,
Followed: Followed,
Unfollowed: Unfollowed,
FollowerBlocked: FollowerBlocked,
FollowerUnblocked: FollowerUnblocked
}

#[derive(Drop, starknet::Event)]
Expand All @@ -60,27 +133,35 @@ mod Follow {
timestamp: u64,
}

#[derive(Drop, starknet::Event)]
struct FollowerUnblocked {
followed_address: ContractAddress,
unblocked_follower: ContractAddress,
follow_id: u256,
timestamp: u64,
}

// *************************************************************************
// CONSTRUCTOR
// *************************************************************************
#[constructor]
fn constructor(ref self: ContractState, hub: ContractAddress) {
fn constructor(
ref self: ContractState,
hub: ContractAddress,
profile_address: ContractAddress,
admin: ContractAddress
) {
self.admin.write(admin);
self.erc721.initializer("KARST:FOLLOWER", "KFL", "");
self.karst_hub.write(hub);
self.followed_profile_address.write(profile_address);
}

// *************************************************************************
// EXTERNAL FUNCTIONS
// *************************************************************************
#[abi(embed_v0)]
impl FollowImpl of IFollowNFT<ContractState> {
/// @notice initialize follow contract
/// @param profile_address address of profile to initialize contract for
fn initialize(ref self: ContractState, profile_address: ContractAddress) {
assert(!self.initialized.read(), Errors::INITIALIZED);
self.initialized.write(true);
self.followed_profile_address.write(profile_address);
}

/// @notice performs the follow action
/// @param follower_profile_address address of the user trying to perform the follow action
fn follow(ref self: ContractState, follower_profile_address: ContractAddress) -> u256 {
Expand All @@ -103,7 +184,7 @@ mod Follow {
self._unfollow(unfollower_profile_address, follow_id);
}

/// @notice performs the block action
/// @notice performs the blocking action
/// @param follower_profile_address address of the user to be blocked
fn process_block(
ref self: ContractState, follower_profile_address: ContractAddress
Expand All @@ -113,7 +194,18 @@ mod Follow {
.follow_id_by_follower_profile_address
.read(follower_profile_address);
assert(follow_id.is_non_zero(), Errors::NOT_FOLLOWING);
self._unfollow(follower_profile_address, follow_id);
let follow_data = self.follow_data_by_follow_id.read(follow_id);
self
.follow_data_by_follow_id
.write(
follow_id,
FollowData {
followed_profile_address: follow_data.followed_profile_address,
follower_profile_address: follow_data.follower_profile_address,
follow_timestamp: follow_data.follow_timestamp,
block_status: true,
}
);
self
.emit(
FollowerBlocked {
Expand All @@ -126,6 +218,40 @@ mod Follow {
return true;
}

/// @notice performs the unblocking action
/// @param follower_profile_address address of the user to be blocked
fn process_unblock(
ref self: ContractState, follower_profile_address: ContractAddress
) -> bool {
hub_only(self.karst_hub.read());
let follow_id = self
.follow_id_by_follower_profile_address
.read(follower_profile_address);
assert(follow_id.is_non_zero(), Errors::NOT_FOLLOWING);
let follow_data = self.follow_data_by_follow_id.read(follow_id);
self
.follow_data_by_follow_id
.write(
follow_id,
FollowData {
followed_profile_address: follow_data.followed_profile_address,
follower_profile_address: follow_data.follower_profile_address,
follow_timestamp: follow_data.follow_timestamp,
block_status: false,
}
);
self
.emit(
FollowerUnblocked {
followed_address: self.followed_profile_address.read(),
unblocked_follower: follower_profile_address,
follow_id: follow_id,
timestamp: get_block_timestamp()
}
);
return true;
}

// *************************************************************************
// GETTERS
// *************************************************************************
Expand Down Expand Up @@ -170,12 +296,18 @@ mod Follow {
// *************************************************************************
// METADATA
// *************************************************************************
/// @notice returns the collection name
fn name(self: @ContractState) -> ByteArray {
return "KARST:FOLLOWER";
}

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

/// @notice returns the token URI of a particular follow NFT
/// @param follow_id ID of NFT to be queried
fn token_uri(self: @ContractState, follow_id: u256) -> ByteArray {
let follow_data = self.follow_data_by_follow_id.read(follow_id);
let timestamp = follow_data.follow_timestamp;
Expand All @@ -195,8 +327,10 @@ mod Follow {
let new_follower_id = self.follower_count.read() + 1;
let follow_timestamp: u64 = get_block_timestamp();
let follow_data = FollowData {
followed_profile_address: self.followed_profile_address.read(),
follower_profile_address: follower_profile_address,
follow_timestamp: follow_timestamp
follow_timestamp: follow_timestamp,
block_status: false,
};

self
Expand Down Expand Up @@ -226,7 +360,10 @@ mod Follow {
.write(
follow_id,
FollowData {
follower_profile_address: 0.try_into().unwrap(), follow_timestamp: 0
followed_profile_address: 0.try_into().unwrap(),
follower_profile_address: 0.try_into().unwrap(),
follow_timestamp: 0,
block_status: false,
}
);
self.follower_count.write(self.follower_count.read() - 1);
Expand Down
2 changes: 1 addition & 1 deletion src/interfaces/IFollowNFT.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ pub trait IFollowNFT<TState> {
// *************************************************************************
// EXTERNALS
// *************************************************************************
fn initialize(ref self: TState, profile_address: ContractAddress);
fn follow(ref self: TState, follower_profile_address: ContractAddress) -> u256;
fn unfollow(ref self: TState, unfollower_profile_address: ContractAddress);
fn process_block(ref self: TState, follower_profile_address: ContractAddress) -> bool;
fn process_unblock(ref self: TState, follower_profile_address: ContractAddress) -> bool;
// *************************************************************************
// GETTERS
// *************************************************************************
Expand Down
7 changes: 6 additions & 1 deletion src/interfaces/IHandle.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ pub trait IHandle<TState> {
fn exists(self: @TState, token_id: u256) -> bool;
fn total_supply(self: @TState) -> u256;
fn get_token_id(self: @TState, local_name: felt252) -> u256;
fn get_handle_token_uri(self: @TState, token_id: u256, local_name: felt252) -> ByteArray;
// *************************************************************************
// METADATA
// *************************************************************************
fn name(self: @TState) -> ByteArray;
fn symbol(self: @TState) -> ByteArray;
fn token_uri(self: @TState, token_id: u256, local_name: felt252) -> ByteArray;
}
30 changes: 27 additions & 3 deletions src/interfaces/IHub.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,31 @@ pub trait IHub<TState> {
fn get_publication_content_uri(
self: @TState, profile_address: ContractAddress, pub_id: u256
) -> ByteArray;
// *************************************************************************
// PROFILE INTERACTIONS
// *************************************************************************

// *************************************************************************
// FOLLOW INTERACTIONS
// *************************************************************************
fn follow(
ref self: TState, follower_profile_address: u256, address_of_profiles_to_follow: Array<u256>
);

fn unfollow(ref self: TState, address_of_profiles_to_unfollow: Array<u256>);

fn set_block_status(
ref self: TState,
blocker_profile_address: ContractAddress,
address_of_profiles_to_block: Array<u256>,
block_status: bool
);

fn is_following(
self: @TState, followed_profile_address: ContractAddress, follower_address: ContractAddress
) -> bool;

// *************************************************************************
// HANDLES
// *************************************************************************
fn get_handle_id(self: @TState, profile_address: ContractAddress) -> u256;

fn get_handle(self: @TState, handle_id: u256) -> ByteArray;
}
9 changes: 2 additions & 7 deletions src/namespaces/handle_registry.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,14 @@ mod HandleRegistry {
};
use karst::interfaces::IHandleRegistry::IHandleRegistry;
use karst::interfaces::IERC721::{IERC721Dispatcher, IERC721DispatcherTrait};
use karst::base::{utils::hubrestricted::HubRestricted::hub_only, constants::errors::Errors};
use karst::base::{constants::errors::Errors};
use karst::interfaces::IHandle::{IHandleDispatcher, IHandleDispatcherTrait};

// *************************************************************************
// STORAGE
// *************************************************************************
#[storage]
struct Storage {
karst_hub: ContractAddress,
handle_address: ContractAddress,
handle_to_profile_address: LegacyMap::<u256, ContractAddress>,
profile_address_to_handle: LegacyMap::<ContractAddress, u256>,
Expand Down Expand Up @@ -54,10 +53,7 @@ mod HandleRegistry {
// CONSTRUCTOR
// *************************************************************************
#[constructor]
fn constructor(
ref self: ContractState, hub_address: ContractAddress, handle_address: ContractAddress
) {
self.karst_hub.write(hub_address);
fn constructor(ref self: ContractState, handle_address: ContractAddress) {
self.handle_address.write(handle_address);
}

Expand All @@ -70,7 +66,6 @@ mod HandleRegistry {
/// @param handle_id ID of handle to be linked
/// @param profile_address address of profile to be linked
fn link(ref self: ContractState, handle_id: u256, profile_address: ContractAddress) {
hub_only(self.karst_hub.read());
self._link(handle_id, profile_address);
}

Expand Down
Loading

0 comments on commit 6ad45eb

Please sign in to comment.