Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: modify follow contract, update tests #88

Merged
merged 6 commits into from
Jul 18, 2024
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: 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
Loading