diff --git a/src/base/types.cairo b/src/base/types.cairo index d142524..fba9bb7 100644 --- a/src/base/types.cairo +++ b/src/base/types.cairo @@ -116,7 +116,7 @@ pub struct ReferencePubParams { // * @param pointed_profile_id The profile address to point the mirror to. // * @param pointed_pub_id The publication ID to point the mirror to. // */ -#[derive(Drop, Serde, starknet::Store)] +#[derive(Drop, Serde, starknet::Store, Clone)] pub struct MirrorParams { profile_address: ContractAddress, metadata_URI: ByteArray, diff --git a/src/interfaces/IPublication.cairo b/src/interfaces/IPublication.cairo index e775c29..c7becae 100644 --- a/src/interfaces/IPublication.cairo +++ b/src/interfaces/IPublication.cairo @@ -2,7 +2,9 @@ // INTERFACE of KARST PUBLICATIONS // ************************************************************************* use starknet::ContractAddress; -use karst::base::types::{PostParams, ReferencePubParams, PublicationType, Publication}; +use karst::base::types::{ + PostParams, MirrorParams, ReferencePubParams, PublicationType, Publication +}; #[starknet::interface] pub trait IKarstPublications { @@ -23,6 +25,9 @@ pub trait IKarstPublications { pointed_pub_id: u256, profile_contract_address: ContractAddress, ) -> u256; + fn mirror( + ref self: T, mirrorParams: MirrorParams, profile_contract_address: ContractAddress + ) -> u256; // ************************************************************************* // GETTERS diff --git a/src/publication/publication.cairo b/src/publication/publication.cairo index 05edae9..55aad92 100644 --- a/src/publication/publication.cairo +++ b/src/publication/publication.cairo @@ -5,6 +5,7 @@ use karst::base::types::{ PostParams, PublicationType, CommentParams, ReferencePubParams, Publication, MirrorParams, QuoteParams }; + use karst::interfaces::IProfile::{IKarstProfileDispatcher, IKarstProfileDispatcherTrait}; use core::option::OptionTrait; @@ -31,7 +32,9 @@ pub trait IKarstPublications { pointed_pub_id: u256, profile_contract_address: ContractAddress, ) -> u256; - fn mirror(ref self: T, mirrorParams: MirrorParams) -> u256; + fn mirror( + ref self: T, mirrorParams: MirrorParams, profile_contract_address: ContractAddress + ) -> u256; fn quote(ref self: T, quoteParams: QuoteParams) -> u256; ////// Getters////// fn get_publication(self: @T, user: ContractAddress, pubIdAssigned: u256) -> Publication; @@ -48,7 +51,7 @@ pub mod Publications { // ************************************************************************* // IMPORTS // ************************************************************************* - use starknet::{ContractAddress, get_contract_address, get_caller_address}; + use starknet::{ContractAddress, get_contract_address, get_caller_address, get_block_timestamp}; use karst::base::types::{ PostParams, Publication, PublicationType, ReferencePubParams, CommentParams, QuoteParams, MirrorParams @@ -75,6 +78,7 @@ pub mod Publications { #[derive(Drop, starknet::Event)] pub enum Event { Post: Post, + MirrorCreated: MirrorCreated, } // ************************************************************************* @@ -89,6 +93,14 @@ pub mod Publications { pub block_timestamp: u256, } + #[derive(Drop, starknet::Event)] + pub struct MirrorCreated { + pub mirrorParams: MirrorParams, + pub publication_id: u256, + pub transaction_executor: ContractAddress, + pub block_timestamp: u64, + } + // ************************************************************************* // CONSTRUCTOR @@ -103,7 +115,6 @@ pub mod Publications { // ************************************************************************* // PUBLISHING FUNCTIONS // ************************************************************************* - fn post( ref self: ContractState, contentURI: ByteArray, @@ -159,9 +170,52 @@ pub mod Publications { // * // * @return uint256 The created publication's pubId. // */ - fn mirror(ref self: ContractState, mirrorParams: MirrorParams) -> u256 { + fn mirror( + ref self: ContractState, + mirrorParams: MirrorParams, + profile_contract_address: ContractAddress + ) -> u256 { // logic here - 0 + + assert!( + profile_contract_address.into() != 0, "Contract Profile Address cannot be zero" + ); + + self._validatePointedPub(mirrorParams.profile_address, mirrorParams.pointed_pub_id); + self + .validateNotBlocked( + mirrorParams.profile_address, mirrorParams.pointed_profile_address, false + ); + let ref_mirrorParams = mirrorParams.clone(); + // _processMirrorIfNeeded is not needed + let profileDispatcher = IKarstProfileDispatcher { + contract_address: profile_contract_address + }; + let pub_id_assigned = profileDispatcher + .get_user_publication_count(mirrorParams.profile_address); + let publication = self + .get_publication(mirrorParams.pointed_profile_address, mirrorParams.pointed_pub_id); + + self + ._fillRefeferencePublicationStorage( + mirrorParams.profile_address, + publication.content_URI, + mirrorParams.pointed_profile_address, + mirrorParams.pointed_pub_id, + PublicationType::Mirror, + profile_contract_address, + ); + self + .emit( + MirrorCreated { + mirrorParams: ref_mirrorParams, + publication_id: pub_id_assigned, + transaction_executor: mirrorParams.profile_address, + block_timestamp: get_block_timestamp(), + } + ); + + pub_id_assigned } fn quote(ref self: ContractState, quoteParams: QuoteParams) -> u256 { diff --git a/tests/test_publication.cairo b/tests/test_publication.cairo index d4caf3f..78728f2 100644 --- a/tests/test_publication.cairo +++ b/tests/test_publication.cairo @@ -5,7 +5,7 @@ use core::option::OptionTrait; use core::starknet::SyscallResultTrait; use core::result::ResultTrait; use core::traits::{TryInto, Into}; -use starknet::{ContractAddress, class_hash::ClassHash}; +use starknet::{ContractAddress, class_hash::ClassHash, contract_address_const}; use snforge_std::{declare, ContractClassTrait, CheatTarget, start_prank, stop_prank}; use token_bound_accounts::interfaces::IAccount::{IAccountDispatcher, IAccountDispatcherTrait}; @@ -19,11 +19,12 @@ use karst::publication::publication::Publications; use karst::interfaces::IPublication::{ IKarstPublicationsDispatcher, IKarstPublicationsDispatcherTrait }; -use karst::base::types::{PostParams, ReferencePubParams, PublicationType}; +use karst::base::types::{PostParams, MirrorParams, ReferencePubParams, PublicationType}; const HUB_ADDRESS: felt252 = 'HUB'; const USER_ONE: felt252 = 'BOB'; const USER_TWO: felt252 = 'ALICE'; +const USER_THREE: felt252 = 'ROB'; // ************************************************************************* // SETUP @@ -37,6 +38,7 @@ fn __setup__() -> ( felt252, ContractAddress, ContractAddress, + ContractAddress, u256, ) { // deploy NFT @@ -123,6 +125,29 @@ fn __setup__() -> ( CheatTarget::Multiple(array![publication_contract_address, profile_contract_address]), ); + start_prank( + CheatTarget::Multiple(array![publication_contract_address, profile_contract_address]), + USER_THREE.try_into().unwrap() + ); + let user_three_profile_address = profile_dispatcher + .create_profile( + nft_contract_address, + registry_class_hash.class_hash.into(), + account_class_hash.class_hash.into(), + 2480, + USER_THREE.try_into().unwrap() + ); + profile_dispatcher + .set_profile_metadata_uri( + user_three_profile_address.try_into().unwrap(), + "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZyjaYXr4ga/" + ); + let contentURI: ByteArray = "ipfs://helloworld"; + publication_dispatcher.post(contentURI, user_three_profile_address, profile_contract_address); + stop_prank( + CheatTarget::Multiple(array![publication_contract_address, profile_contract_address]), + ); + return ( nft_contract_address, registry_contract_address, @@ -132,6 +157,7 @@ fn __setup__() -> ( account_class_hash.class_hash.into(), user_one_profile_address, user_two_profile_address, + user_three_profile_address, user_one_first_post_pointed_pub_id, ); } @@ -151,6 +177,7 @@ fn test_post() { _, user_one_profile_address, _, + _, user_one_first_post_pointed_pub_id, ) = __setup__(); @@ -182,6 +209,7 @@ fn test_comment() { _, user_one_profile_address, user_two_profile_address, + _, user_one_first_post_pointed_pub_id, ) = __setup__(); @@ -232,6 +260,189 @@ fn test_comment() { ); } +#[test] +fn test_publish_mirror() { + let ( + _, + _, + profile_contract_address, + publication_contract_address, + _, + _, + user_one_profile_address, + user_two_profile_address, + _, + user_one_first_post_pointed_pub_id, + ) = + __setup__(); + let publication_dispatcher = IKarstPublicationsDispatcher { + contract_address: publication_contract_address + }; + + let metadata_URI = "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZysddewga/"; + + let mirror_params = MirrorParams { + profile_address: user_one_profile_address, + metadata_URI: metadata_URI, + pointed_profile_address: user_two_profile_address, + pointed_pub_id: user_one_first_post_pointed_pub_id, + }; + + start_prank(CheatTarget::One(publication_contract_address), USER_ONE.try_into().unwrap()); + let profileDispatcher = IKarstProfileDispatcher { contract_address: profile_contract_address }; + + let pub_id_assigned = profileDispatcher.get_user_publication_count(user_one_profile_address); + let pub_assign_id = publication_dispatcher.mirror(mirror_params, profile_contract_address); + + assert(pub_id_assigned == pub_assign_id, 'Invalid publication id assign'); + + stop_prank(CheatTarget::One(publication_contract_address),); +} + +#[test] +fn test_two_publish_mirror() { + let ( + _, + _, + profile_contract_address, + publication_contract_address, + _, + _, + user_one_profile_address, + user_two_profile_address, + user_three_profile_address, + user_one_first_post_pointed_pub_id, + ) = + __setup__(); + + let publication_dispatcher = IKarstPublicationsDispatcher { + contract_address: publication_contract_address + }; + + let profile_dispatcher = IKarstProfileDispatcher { contract_address: profile_contract_address }; + + let metadata_URI = "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZysddewga/"; + + let metadata_URI_1 = "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZysddewga/"; + + start_prank(CheatTarget::One(publication_contract_address), USER_TWO.try_into().unwrap()); + let mirror_params = MirrorParams { + profile_address: user_one_profile_address, + metadata_URI: metadata_URI, + pointed_profile_address: user_two_profile_address, + pointed_pub_id: user_one_first_post_pointed_pub_id, + }; + + publication_dispatcher.mirror(mirror_params, profile_contract_address); + + stop_prank(CheatTarget::One(publication_contract_address),); + + start_prank(CheatTarget::One(publication_contract_address), USER_THREE.try_into().unwrap()); + + let mirror_params_1 = MirrorParams { + profile_address: user_one_profile_address, + metadata_URI: metadata_URI_1, + pointed_profile_address: user_three_profile_address, + pointed_pub_id: user_one_first_post_pointed_pub_id, + }; + + publication_dispatcher.mirror(mirror_params_1, profile_contract_address); + + let pointed_profile_three = profile_dispatcher.get_profile(user_three_profile_address); + + assert( + pointed_profile_three.profile_address == user_three_profile_address, + 'Invalid publication id assign' + ); + + stop_prank(CheatTarget::One(publication_contract_address),); +} + +#[test] +fn test_mirror_pointed_profile_address() { + let ( + _, + _, + profile_contract_address, + publication_contract_address, + _, + _, + user_one_profile_address, + user_two_profile_address, + _, + user_one_first_post_pointed_pub_id, + ) = + __setup__(); + let publication_dispatcher = IKarstPublicationsDispatcher { + contract_address: publication_contract_address + }; + + let metadata_URI = "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZysddewga/"; + + let mirror_params = MirrorParams { + profile_address: user_one_profile_address, + metadata_URI: metadata_URI, + pointed_profile_address: user_two_profile_address, + pointed_pub_id: user_one_first_post_pointed_pub_id, + }; + + start_prank(CheatTarget::One(publication_contract_address), USER_ONE.try_into().unwrap()); + let profileDispatcher = IKarstProfileDispatcher { contract_address: profile_contract_address }; + + publication_dispatcher.mirror(mirror_params, profile_contract_address); + + let pointed_profile = profileDispatcher.get_profile(user_one_profile_address); + + assert( + pointed_profile.profile_address == user_one_profile_address, + 'Invalid Pointed Profile Address' + ); + + stop_prank(CheatTarget::One(publication_contract_address),); +} + +#[test] +fn test_mirror_root_profile_address() { + let ( + _, + _, + profile_contract_address, + publication_contract_address, + _, + _, + user_one_profile_address, + user_two_profile_address, + _, + user_one_first_post_pointed_pub_id, + ) = + __setup__(); + let publication_dispatcher = IKarstPublicationsDispatcher { + contract_address: publication_contract_address + }; + + let metadata_URI = "ipfs://QmSkDCsS32eLpcymxtn1cEn7Rc5hfefLBgfvZysddewga/"; + + let mirror_params = MirrorParams { + profile_address: user_one_profile_address, + metadata_URI: metadata_URI, + pointed_profile_address: user_two_profile_address, + pointed_pub_id: user_one_first_post_pointed_pub_id, + }; + + start_prank(CheatTarget::One(publication_contract_address), USER_ONE.try_into().unwrap()); + let profileDispatcher = IKarstProfileDispatcher { contract_address: profile_contract_address }; + + publication_dispatcher.mirror(mirror_params, profile_contract_address); + + let pointed_profile = profileDispatcher.get_profile(user_two_profile_address); + + assert( + pointed_profile.profile_address == user_two_profile_address, 'Invalid Root Profile Address' + ); + + stop_prank(CheatTarget::One(publication_contract_address),); +} + fn to_address(name: felt252) -> ContractAddress { name.try_into().unwrap()