From b62323588c496260640d249ff184aa9f763134aa Mon Sep 17 00:00:00 2001 From: hoossayn Date: Mon, 28 Apr 2025 21:19:26 +0100 Subject: [PATCH 1/6] feat: implement content purchase functionality --- src/base/types.cairo | 19 +++ src/chainlib/ChainLib.cairo | 209 ++++++++++++++++++++++++++++++++- src/interfaces/IChainLib.cairo | 14 ++- 3 files changed, 238 insertions(+), 4 deletions(-) diff --git a/src/base/types.cairo b/src/base/types.cairo index 163d40f..9ec173a 100644 --- a/src/base/types.cairo +++ b/src/base/types.cairo @@ -38,3 +38,22 @@ pub enum Rank { EXPERT, } +#[derive(Copy, Drop, Serde, starknet::Store, Clone, PartialEq, Debug)] +pub enum PurchaseStatus { + #[default] + Pending, + Completed, + Failed, + Refunded +} + +#[derive(Drop, Serde, starknet::Store, Debug)] +pub struct Purchase { + pub id: u256, + pub content_id: felt252, + pub buyer: ContractAddress, + pub price: u256, + pub status: PurchaseStatus, + pub timestamp: u64, + pub transaction_hash: felt252, +} diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index a84186f..0b25e23 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -6,7 +6,9 @@ pub mod ChainLib { }; use starknet::{ContractAddress, get_block_timestamp, get_caller_address}; use crate::interfaces::IChainLib::IChainLib; - use crate::base::types::{TokenBoundAccount, User, Role, Rank}; + use crate::base::types::{TokenBoundAccount, User, Role, Rank, Purchase, PurchaseStatus}; + use core::array::ArrayTrait; + use core::traits::Into; #[derive(Copy, Drop, Serde, starknet::Store, PartialEq, Debug)] pub enum ContentType { @@ -48,7 +50,16 @@ pub mod ChainLib { users: Map, creators_content: Map::, content: Map::, - content_tags: Map::> + content_tags: Map::>, + + // Purchase related storage + content_prices: Map::, // Maps content_id to price + next_purchase_id: u256, // Tracking the next available purchase ID + purchases: Map::, // Store purchases by ID + user_purchase_count: Map::, // Count of purchases per user + user_purchase_ids: Map::<(ContractAddress, u32), u256>, // Map of (user, index) to purchase ID + content_purchase_count: Map::, // Count of purchases per content + content_purchase_ids: Map::<(felt252, u32), u256>, // Map of (content_id, index) to purchase ID } @@ -56,6 +67,8 @@ pub mod ChainLib { fn constructor(ref self: ContractState, admin: ContractAddress) { // Store the values in contract state self.admin.write(admin); + // Initialize purchase ID counter + self.next_purchase_id.write(1_u256); } #[event] @@ -63,6 +76,8 @@ pub mod ChainLib { pub enum Event { TokenBoundAccountCreated: TokenBoundAccountCreated, UserCreated: UserCreated, + ContentPurchased: ContentPurchased, + PurchaseStatusUpdated: PurchaseStatusUpdated, } #[derive(Drop, starknet::Event)] @@ -74,6 +89,22 @@ pub mod ChainLib { pub struct UserCreated { pub id: u256, } + + #[derive(Drop, starknet::Event)] + pub struct ContentPurchased { + pub purchase_id: u256, + pub content_id: felt252, + pub buyer: ContractAddress, + pub price: u256, + pub timestamp: u64, + } + + #[derive(Drop, starknet::Event)] + pub struct PurchaseStatusUpdated { + pub purchase_id: u256, + pub new_status: u8, // Using u8 for status code instead of PurchaseStatus enum + pub timestamp: u64, + } #[abi(embed_v0)] impl ChainLibNetImpl of IChainLib { @@ -214,5 +245,179 @@ pub mod ChainLib { let admin = self.admin.read(); admin } + + /// @notice Processes a content purchase transaction. + /// @dev Creates a purchase record, verifies payment, and emits an event. + /// @param self The contract state reference. + /// @param content_id The unique identifier of the content being purchased. + /// @param transaction_hash The hash of the transaction for verification purposes. + /// @return purchase_id The unique identifier assigned to the purchase. + fn purchase_content( + ref self: ContractState, content_id: felt252, transaction_hash: felt252 + ) -> u256 { + // Validate input parameters + assert!(content_id != 0, "Content ID cannot be empty"); + assert!(transaction_hash != 0, "Transaction hash cannot be empty"); + + // Get the price for the content + let price = self.content_prices.read(content_id); + assert!(price > 0, "Content either doesn't exist or is not for sale"); + + // Get the buyer's address + let buyer = get_caller_address(); + + // Get the next purchase ID and increment for future use + let purchase_id = self.next_purchase_id.read(); + self.next_purchase_id.write(purchase_id + 1); + + // Create the purchase record + let purchase = Purchase { + id: purchase_id, + content_id: content_id, + buyer: buyer, + price: price, + status: PurchaseStatus::Pending, // Initial status is pending until payment is verified + timestamp: get_block_timestamp(), + transaction_hash: transaction_hash, + }; + + // Store the purchase in the purchases mapping + self.purchases.write(purchase_id, purchase); + + // Add the purchase ID to the user's purchase list + let user_purchase_count = self.user_purchase_count.read(buyer); + self.user_purchase_ids.write((buyer, user_purchase_count), purchase_id); + self.user_purchase_count.write(buyer, user_purchase_count + 1); + + // Add the purchase ID to the content's purchase list + let content_purchase_count = self.content_purchase_count.read(content_id); + self.content_purchase_ids.write((content_id, content_purchase_count), purchase_id); + self.content_purchase_count.write(content_id, content_purchase_count + 1); + + // Emit event for the purchase + let timestamp = get_block_timestamp(); + self.emit( + ContentPurchased { + purchase_id, + content_id, + buyer, + price, + timestamp, + } + ); + + // Return the purchase ID + purchase_id + } + + /// @notice Retrieves details of a specific purchase. + /// @dev Fetches purchase information by its ID. + /// @param self The contract state reference. + /// @param purchase_id The unique identifier of the purchase. + /// @return Purchase The purchase details. + fn get_purchase_details(ref self: ContractState, purchase_id: u256) -> Purchase { + // Fetch and return the purchase details from storage + let purchase = self.purchases.read(purchase_id); + purchase + } + + /// @notice Retrieves all purchases made by a specific user. + /// @dev Returns an array of purchase records for the user. + /// @param self The contract state reference. + /// @param user_address The address of the user whose purchases are being retrieved. + /// @return Array An array of purchase records for the user. + fn get_user_purchases( + ref self: ContractState, user_address: ContractAddress + ) -> Array { + // Initialize an empty array to hold the purchases + let mut purchases: Array = ArrayTrait::new(); + + // Get the number of purchases for this user + let purchase_count = self.user_purchase_count.read(user_address); + + // Iterate through the purchase IDs and fetch each purchase + let mut i: u32 = 0; + + while i < purchase_count { + // Get the purchase ID at the current index + let purchase_id = self.user_purchase_ids.read((user_address, i)); + + // Fetch the purchase details using the ID + let purchase = self.purchases.read(purchase_id); + + // Add the purchase to the array + purchases.append(purchase); + + // Move to the next index + i += 1; + }; + + // Return the array of purchases + purchases + } + + /// @notice Verifies if a purchase is valid and completed. + /// @dev Checks the status of a purchase to confirm it has been completed successfully. + /// @param self The contract state reference. + /// @param purchase_id The unique identifier of the purchase to verify. + /// @return bool True if the purchase is valid and completed, false otherwise. + fn verify_purchase(ref self: ContractState, purchase_id: u256) -> bool { + // Get the purchase details + let purchase = self.purchases.read(purchase_id); + + // A purchase is valid if its status is Completed + if purchase.status == PurchaseStatus::Completed { + return true; + } else { + return false; + } + } + + /// @notice Updates the status of a purchase. + /// @dev Only admin can update the status to prevent unauthorized changes. + /// @param self The contract state reference. + /// @param purchase_id The unique identifier of the purchase. + /// @param status The new status to set for the purchase. + /// @return bool True if the status was updated successfully. + fn update_purchase_status( + ref self: ContractState, purchase_id: u256, status: PurchaseStatus + ) -> bool { + // Only admin can update purchase status + let caller = get_caller_address(); + assert(self.admin.read() == caller, 'Only admin can update status'); + + // Get the current purchase + let mut purchase = self.purchases.read(purchase_id); + + // Validate that we're not trying to update a purchase that doesn't exist + assert(purchase.id == purchase_id, 'Purchase does not exist'); + + // Update the status + purchase.status = status; + + // Save the updated purchase + self.purchases.write(purchase_id, purchase); + + // Emit event for the status update + let timestamp = get_block_timestamp(); + + // Convert PurchaseStatus to u8 + let status_code: u8 = match status { + PurchaseStatus::Pending => 0_u8, + PurchaseStatus::Completed => 1_u8, + PurchaseStatus::Failed => 2_u8, + PurchaseStatus::Refunded => 3_u8, + }; + + self.emit( + PurchaseStatusUpdated { + purchase_id, + new_status: status_code, + timestamp, + } + ); + + true + } } } diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index 4f90038..1ea81b6 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -1,5 +1,5 @@ use starknet::ContractAddress; -use crate::base::types::{TokenBoundAccount, User, Role, Rank}; +use crate::base::types::{TokenBoundAccount, User, Role, Rank, Purchase, PurchaseStatus}; #[starknet::interface] pub trait IChainLib { @@ -20,5 +20,15 @@ pub trait IChainLib { fn retrieve_user_profile(ref self: TContractState, user_id: u256) -> User; fn getAdmin(self: @TContractState) -> ContractAddress; fn is_verified(ref self: TContractState, user_id: u256) -> bool; + + // Content Purchase Management + fn purchase_content( + ref self: TContractState, content_id: felt252, transaction_hash: felt252 + ) -> u256; + fn get_purchase_details(ref self: TContractState, purchase_id: u256) -> Purchase; + fn get_user_purchases(ref self: TContractState, user_address: ContractAddress) -> Array; + fn verify_purchase(ref self: TContractState, purchase_id: u256) -> bool; + fn update_purchase_status( + ref self: TContractState, purchase_id: u256, status: PurchaseStatus + ) -> bool; } - From f0148d74de75e945c89eea9fda54f472db4a1d77 Mon Sep 17 00:00:00 2001 From: hoossayn Date: Mon, 28 Apr 2025 21:35:07 +0100 Subject: [PATCH 2/6] Add content purchase functionality with price setting and status management --- src/chainlib/ChainLib.cairo | 38 +++-- src/interfaces/IChainLib.cairo | 2 +- tests/test_ChainLib.cairo | 247 +++++++++++++++++++++++++++++++-- 3 files changed, 268 insertions(+), 19 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 0b25e23..7d64c20 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -236,22 +236,42 @@ pub mod ChainLib { } fn is_verified(ref self: ContractState, user_id: u256) -> bool { - let mut user = self.users.read(user_id); + // Get the user from storage + let user = self.users.read(user_id); + + // Return the verification status user.verified } - - + + /// @notice Gets the admin address of the contract + /// @dev Returns the address of the contract admin + /// @param self The contract state reference. + /// @return The admin address fn getAdmin(self: @ContractState) -> ContractAddress { let admin = self.admin.read(); admin } + + /// @notice Sets the price for a content item (admin only) + /// @dev This function allows the admin to set or update the price of a content item. + /// @param self The contract state reference. + /// @param content_id The unique identifier of the content. + /// @param price The price to set for the content. + fn set_content_price(ref self: ContractState, content_id: felt252, price: u256) { + // Only admin can set content prices + let caller = get_caller_address(); + assert(self.admin.read() == caller, 'Admin only'); + + // Set the price for the content + self.content_prices.write(content_id, price); + } - /// @notice Processes a content purchase transaction. - /// @dev Creates a purchase record, verifies payment, and emits an event. + /// @notice Initiates a purchase for a specific content. + /// @dev Creates a purchase record with a pending status and emits an event. /// @param self The contract state reference. /// @param content_id The unique identifier of the content being purchased. - /// @param transaction_hash The hash of the transaction for verification purposes. - /// @return purchase_id The unique identifier assigned to the purchase. + /// @param transaction_hash The hash of the transaction being used for payment. + /// @return The unique ID of the newly created purchase. fn purchase_content( ref self: ContractState, content_id: felt252, transaction_hash: felt252 ) -> u256 { @@ -261,7 +281,7 @@ pub mod ChainLib { // Get the price for the content let price = self.content_prices.read(content_id); - assert!(price > 0, "Content either doesn't exist or is not for sale"); + assert!(price > 0, "Content either doesn't exist"); // Get the buyer's address let buyer = get_caller_address(); @@ -276,7 +296,7 @@ pub mod ChainLib { content_id: content_id, buyer: buyer, price: price, - status: PurchaseStatus::Pending, // Initial status is pending until payment is verified + status: PurchaseStatus::Pending, timestamp: get_block_timestamp(), transaction_hash: transaction_hash, }; diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index 1ea81b6..d27377c 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -21,7 +21,7 @@ pub trait IChainLib { fn getAdmin(self: @TContractState) -> ContractAddress; fn is_verified(ref self: TContractState, user_id: u256) -> bool; - // Content Purchase Management + fn set_content_price(ref self: TContractState, content_id: felt252, price: u256); fn purchase_content( ref self: TContractState, content_id: felt252, transaction_hash: felt252 ) -> u256; diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 5fbbb1f..f5b7eb9 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -1,14 +1,22 @@ // Import the contract modules -use chain_lib::chainlib::ChainLib; - -use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use chain_lib::interfaces::IChainLib::{IChainLibDispatcher, IChainLibDispatcherTrait}; use snforge_std::{CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare}; use starknet::ContractAddress; -use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; -use starknet::testing::{set_caller_address, set_contract_address}; -use chain_lib::base::types::{Role, Rank}; - +use chain_lib::base::types::{Role, Rank, PurchaseStatus}; + +/// Helper function to create a content item with a price +/// We'll use the set_content_price function implemented in the contract +fn setup_content_with_price( + dispatcher: IChainLibDispatcher, admin_address: ContractAddress, + contract_address: ContractAddress, content_id: felt252, price: u256 +) { + // Set admin as caller for setting content price + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Use the new set_content_price function to set the price + dispatcher.set_content_price(content_id, price); +} fn setup() -> (ContractAddress, ContractAddress) { let declare_result = declare("ChainLib"); @@ -38,7 +46,6 @@ fn test_initial_data() { assert(admin == admin_address, 'deployment failed'); } - #[test] fn test_create_token_bount_account() { let (contract_address, _) = setup(); @@ -62,7 +69,6 @@ fn test_create_token_bount_account() { assert(token_bound_account.init_param2 == init_param2, 'init_param2 mismatch'); } - #[test] fn test_create_user() { let (contract_address, _) = setup(); @@ -138,3 +144,226 @@ fn test_verify_user_not_admin() { let verified_user = dispatcher.retrieve_user_profile(account_id); assert(verified_user.verified, 'Not Verified'); } + +#[test] +fn test_purchase_content() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set up test data + let content_id: felt252 = 'content1'; + let price: u256 = 1000_u256; + let transaction_hash: felt252 = 'tx1'; + + // Set up content with price + setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); + + // We set user as the caller for the purchase + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Call the purchase function + let purchase_id = dispatcher.purchase_content(content_id, transaction_hash); + + // Verify the purchase details + let purchase = dispatcher.get_purchase_details(purchase_id); + assert(purchase.id == purchase_id, 'ID mismatch'); + assert(purchase.content_id == content_id, 'Content ID mismatch'); + assert(purchase.buyer == user_address, 'Buyer mismatch'); + assert(purchase.price == price, 'Price mismatch'); + assert(purchase.status == PurchaseStatus::Pending, 'Status mismatch'); + assert(purchase.transaction_hash == transaction_hash, 'Transaction hash mismatch'); +} + +#[test] +#[should_panic(expected: "Content either doesn't exist")] +fn test_purchase_nonexistent_content() { + let (contract_address, _) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set user as caller + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Attempt to purchase nonexistent content + let content_id: felt252 = 'none'; + let transaction_hash: felt252 = 'tx1'; + + // This should fail because the content doesn't exist (no price set) + let _ = dispatcher.purchase_content(content_id, transaction_hash); +} + +#[test] +fn test_get_user_purchases() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set up multiple content items + let content_id_1: felt252 = 'content1'; + let content_id_2: felt252 = 'content2'; + let price: u256 = 1000_u256; + + // Set admin as caller to prepare the contract state + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Set up content with price + setup_content_with_price(dispatcher, admin_address, contract_address, content_id_1, price); + setup_content_with_price(dispatcher, admin_address, contract_address, content_id_2, price * 2); + + // Set user as caller for purchases + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Purchase multiple content items + let purchase_id_1 = dispatcher.purchase_content(content_id_1, 'tx1'); + let purchase_id_2 = dispatcher.purchase_content(content_id_2, 'tx2'); + + // Get user purchases + let _user_purchases = dispatcher.get_user_purchases(user_address); + + // Verify the purchases array contains the expected items + assert(_user_purchases.len() == 2, 'Wrong number of purchases'); + + // Due to Copy trait constraints, we need to modify how we access array elements + // Instead of dereferencing, we'll check directly using the array + let purchase_1 = dispatcher.get_purchase_details(purchase_id_1); + let purchase_2 = dispatcher.get_purchase_details(purchase_id_2); + + assert(purchase_1.id == purchase_id_1, 'Purchase 1 ID mismatch'); + assert(purchase_1.content_id == content_id_1, 'Purchase 1 content mismatch'); + + assert(purchase_2.id == purchase_id_2, 'Purchase 2 ID mismatch'); + assert(purchase_2.content_id == content_id_2, 'Purchase 2 content mismatch'); +} + +#[test] +fn test_verify_purchase() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set up test data + let content_id: felt252 = 'content1'; + let price: u256 = 1000_u256; + + // Set admin as caller to set up content price + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Set up content with price + setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); + + // Set user as caller + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Purchase the content + let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); + + // Initially, purchase should not be verified (status is Pending) + let is_verified = dispatcher.verify_purchase(purchase_id); + assert(!is_verified, 'Purchase should not be verified'); + + // Set admin as caller to update the purchase status + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Update purchase status to Completed + let update_result = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); + assert(update_result, 'Failed to update status'); + + // Now the purchase should be verified + let is_now_verified = dispatcher.verify_purchase(purchase_id); + assert(is_now_verified, 'Purchase should be verified'); +} + +#[test] +fn test_update_purchase_status() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set up test data + let content_id: felt252 = 'content1'; + let price: u256 = 1000_u256; + + // Set admin as caller to set up content price + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Set up content with price + setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); + + // Set user as caller for purchase + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Purchase the content + let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); + + // Get initial purchase + let initial_purchase = dispatcher.get_purchase_details(purchase_id); + assert(initial_purchase.status == PurchaseStatus::Pending, 'Status should be Pending'); + + // Set admin as caller to update the purchase status + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Test updating to different statuses + // 1. Update to Completed + let update_to_completed = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); + assert(update_to_completed, 'Failed to update to Completed'); + + let completed_purchase = dispatcher.get_purchase_details(purchase_id); + assert(completed_purchase.status == PurchaseStatus::Completed, 'Status should be Completed'); + + // 2. Update to Failed + let update_to_failed = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Failed); + assert(update_to_failed, 'Failed to update to Failed'); + + let failed_purchase = dispatcher.get_purchase_details(purchase_id); + assert(failed_purchase.status == PurchaseStatus::Failed, 'Status should be Failed'); + + // 3. Update to Refunded + let update_to_refunded = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Refunded); + assert(update_to_refunded, 'Failed to update to Refunded'); + + let refunded_purchase = dispatcher.get_purchase_details(purchase_id); + assert(refunded_purchase.status == PurchaseStatus::Refunded, 'Status should be Refunded'); +} + +#[test] +#[should_panic(expected: 'Only admin can update status')] +fn test_update_purchase_status_not_admin() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + // Set up test data + let content_id: felt252 = 'content1'; + let price: u256 = 1000_u256; + + // Set admin as caller to set up content price + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Set up content with price + setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); + + // Set user as caller for purchase + cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); + + // Purchase the content + let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); + + // Attempt to update purchase status as non-admin user + // This should fail with the "Only admin can update status" error + let _ = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); +} + +#[test] +#[should_panic(expected: 'Purchase does not exist')] +fn test_update_nonexistent_purchase() { + let (contract_address, admin_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + + // Set admin as caller + cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); + + // Attempt to update a purchase that doesn't exist + let nonexistent_purchase_id = 42_u256; + let _ = dispatcher.update_purchase_status(nonexistent_purchase_id, PurchaseStatus::Completed); +} From a55298b130c2d4e572f8f54b4e17928cddd0c6ed Mon Sep 17 00:00:00 2001 From: hoossayn Date: Mon, 28 Apr 2025 21:35:34 +0100 Subject: [PATCH 3/6] Format code --- src/chainlib/ChainLib.cairo | 99 +++++++++++++---------------- src/interfaces/IChainLib.cairo | 6 +- tests/test_ChainLib.cairo | 113 +++++++++++++++++---------------- 3 files changed, 107 insertions(+), 111 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 7d64c20..fd87dd9 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -51,15 +51,18 @@ pub mod ChainLib { creators_content: Map::, content: Map::, content_tags: Map::>, - // Purchase related storage content_prices: Map::, // Maps content_id to price next_purchase_id: u256, // Tracking the next available purchase ID purchases: Map::, // Store purchases by ID user_purchase_count: Map::, // Count of purchases per user - user_purchase_ids: Map::<(ContractAddress, u32), u256>, // Map of (user, index) to purchase ID + user_purchase_ids: Map::< + (ContractAddress, u32), u256 + >, // Map of (user, index) to purchase ID content_purchase_count: Map::, // Count of purchases per content - content_purchase_ids: Map::<(felt252, u32), u256>, // Map of (content_id, index) to purchase ID + content_purchase_ids: Map::< + (felt252, u32), u256 + >, // Map of (content_id, index) to purchase ID } @@ -89,7 +92,7 @@ pub mod ChainLib { pub struct UserCreated { pub id: u256, } - + #[derive(Drop, starknet::Event)] pub struct ContentPurchased { pub purchase_id: u256, @@ -98,7 +101,7 @@ pub mod ChainLib { pub price: u256, pub timestamp: u64, } - + #[derive(Drop, starknet::Event)] pub struct PurchaseStatusUpdated { pub purchase_id: u256, @@ -242,7 +245,7 @@ pub mod ChainLib { // Return the verification status user.verified } - + /// @notice Gets the admin address of the contract /// @dev Returns the address of the contract admin /// @param self The contract state reference. @@ -251,7 +254,7 @@ pub mod ChainLib { let admin = self.admin.read(); admin } - + /// @notice Sets the price for a content item (admin only) /// @dev This function allows the admin to set or update the price of a content item. /// @param self The contract state reference. @@ -261,7 +264,7 @@ pub mod ChainLib { // Only admin can set content prices let caller = get_caller_address(); assert(self.admin.read() == caller, 'Admin only'); - + // Set the price for the content self.content_prices.write(content_id, price); } @@ -278,58 +281,50 @@ pub mod ChainLib { // Validate input parameters assert!(content_id != 0, "Content ID cannot be empty"); assert!(transaction_hash != 0, "Transaction hash cannot be empty"); - + // Get the price for the content let price = self.content_prices.read(content_id); assert!(price > 0, "Content either doesn't exist"); - + // Get the buyer's address let buyer = get_caller_address(); - + // Get the next purchase ID and increment for future use let purchase_id = self.next_purchase_id.read(); self.next_purchase_id.write(purchase_id + 1); - + // Create the purchase record let purchase = Purchase { id: purchase_id, content_id: content_id, buyer: buyer, price: price, - status: PurchaseStatus::Pending, + status: PurchaseStatus::Pending, timestamp: get_block_timestamp(), transaction_hash: transaction_hash, }; - + // Store the purchase in the purchases mapping self.purchases.write(purchase_id, purchase); - + // Add the purchase ID to the user's purchase list let user_purchase_count = self.user_purchase_count.read(buyer); self.user_purchase_ids.write((buyer, user_purchase_count), purchase_id); self.user_purchase_count.write(buyer, user_purchase_count + 1); - + // Add the purchase ID to the content's purchase list let content_purchase_count = self.content_purchase_count.read(content_id); self.content_purchase_ids.write((content_id, content_purchase_count), purchase_id); self.content_purchase_count.write(content_id, content_purchase_count + 1); - + // Emit event for the purchase let timestamp = get_block_timestamp(); - self.emit( - ContentPurchased { - purchase_id, - content_id, - buyer, - price, - timestamp, - } - ); - + self.emit(ContentPurchased { purchase_id, content_id, buyer, price, timestamp, }); + // Return the purchase ID purchase_id } - + /// @notice Retrieves details of a specific purchase. /// @dev Fetches purchase information by its ID. /// @param self The contract state reference. @@ -340,7 +335,7 @@ pub mod ChainLib { let purchase = self.purchases.read(purchase_id); purchase } - + /// @notice Retrieves all purchases made by a specific user. /// @dev Returns an array of purchase records for the user. /// @param self The contract state reference. @@ -351,31 +346,31 @@ pub mod ChainLib { ) -> Array { // Initialize an empty array to hold the purchases let mut purchases: Array = ArrayTrait::new(); - + // Get the number of purchases for this user let purchase_count = self.user_purchase_count.read(user_address); - + // Iterate through the purchase IDs and fetch each purchase let mut i: u32 = 0; - + while i < purchase_count { // Get the purchase ID at the current index let purchase_id = self.user_purchase_ids.read((user_address, i)); - + // Fetch the purchase details using the ID let purchase = self.purchases.read(purchase_id); - - // Add the purchase to the array + + // Add the purchase to the array purchases.append(purchase); - + // Move to the next index i += 1; }; - + // Return the array of purchases purchases } - + /// @notice Verifies if a purchase is valid and completed. /// @dev Checks the status of a purchase to confirm it has been completed successfully. /// @param self The contract state reference. @@ -384,7 +379,7 @@ pub mod ChainLib { fn verify_purchase(ref self: ContractState, purchase_id: u256) -> bool { // Get the purchase details let purchase = self.purchases.read(purchase_id); - + // A purchase is valid if its status is Completed if purchase.status == PurchaseStatus::Completed { return true; @@ -392,7 +387,7 @@ pub mod ChainLib { return false; } } - + /// @notice Updates the status of a purchase. /// @dev Only admin can update the status to prevent unauthorized changes. /// @param self The contract state reference. @@ -405,22 +400,22 @@ pub mod ChainLib { // Only admin can update purchase status let caller = get_caller_address(); assert(self.admin.read() == caller, 'Only admin can update status'); - + // Get the current purchase let mut purchase = self.purchases.read(purchase_id); - + // Validate that we're not trying to update a purchase that doesn't exist assert(purchase.id == purchase_id, 'Purchase does not exist'); - + // Update the status purchase.status = status; - + // Save the updated purchase self.purchases.write(purchase_id, purchase); - + // Emit event for the status update let timestamp = get_block_timestamp(); - + // Convert PurchaseStatus to u8 let status_code: u8 = match status { PurchaseStatus::Pending => 0_u8, @@ -428,15 +423,9 @@ pub mod ChainLib { PurchaseStatus::Failed => 2_u8, PurchaseStatus::Refunded => 3_u8, }; - - self.emit( - PurchaseStatusUpdated { - purchase_id, - new_status: status_code, - timestamp, - } - ); - + + self.emit(PurchaseStatusUpdated { purchase_id, new_status: status_code, timestamp, }); + true } } diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index d27377c..43c6054 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -20,13 +20,15 @@ pub trait IChainLib { fn retrieve_user_profile(ref self: TContractState, user_id: u256) -> User; fn getAdmin(self: @TContractState) -> ContractAddress; fn is_verified(ref self: TContractState, user_id: u256) -> bool; - + fn set_content_price(ref self: TContractState, content_id: felt252, price: u256); fn purchase_content( ref self: TContractState, content_id: felt252, transaction_hash: felt252 ) -> u256; fn get_purchase_details(ref self: TContractState, purchase_id: u256) -> Purchase; - fn get_user_purchases(ref self: TContractState, user_address: ContractAddress) -> Array; + fn get_user_purchases( + ref self: TContractState, user_address: ContractAddress + ) -> Array; fn verify_purchase(ref self: TContractState, purchase_id: u256) -> bool; fn update_purchase_status( ref self: TContractState, purchase_id: u256, status: PurchaseStatus diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index f5b7eb9..ba628e7 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -8,12 +8,15 @@ use chain_lib::base::types::{Role, Rank, PurchaseStatus}; /// Helper function to create a content item with a price /// We'll use the set_content_price function implemented in the contract fn setup_content_with_price( - dispatcher: IChainLibDispatcher, admin_address: ContractAddress, - contract_address: ContractAddress, content_id: felt252, price: u256 + dispatcher: IChainLibDispatcher, + admin_address: ContractAddress, + contract_address: ContractAddress, + content_id: felt252, + price: u256 ) { // Set admin as caller for setting content price cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Use the new set_content_price function to set the price dispatcher.set_content_price(content_id, price); } @@ -150,21 +153,21 @@ fn test_purchase_content() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; let transaction_hash: felt252 = 'tx1'; - + // Set up content with price setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); - + // We set user as the caller for the purchase cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - - // Call the purchase function + + // Call the purchase function let purchase_id = dispatcher.purchase_content(content_id, transaction_hash); - + // Verify the purchase details let purchase = dispatcher.get_purchase_details(purchase_id); assert(purchase.id == purchase_id, 'ID mismatch'); @@ -181,14 +184,14 @@ fn test_purchase_nonexistent_content() { let (contract_address, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set user as caller cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - + // Attempt to purchase nonexistent content let content_id: felt252 = 'none'; let transaction_hash: felt252 = 'tx1'; - + // This should fail because the content doesn't exist (no price set) let _ = dispatcher.purchase_content(content_id, transaction_hash); } @@ -198,40 +201,40 @@ fn test_get_user_purchases() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set up multiple content items let content_id_1: felt252 = 'content1'; let content_id_2: felt252 = 'content2'; let price: u256 = 1000_u256; - + // Set admin as caller to prepare the contract state cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Set up content with price setup_content_with_price(dispatcher, admin_address, contract_address, content_id_1, price); setup_content_with_price(dispatcher, admin_address, contract_address, content_id_2, price * 2); - + // Set user as caller for purchases cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - + // Purchase multiple content items let purchase_id_1 = dispatcher.purchase_content(content_id_1, 'tx1'); let purchase_id_2 = dispatcher.purchase_content(content_id_2, 'tx2'); - + // Get user purchases let _user_purchases = dispatcher.get_user_purchases(user_address); - + // Verify the purchases array contains the expected items assert(_user_purchases.len() == 2, 'Wrong number of purchases'); - + // Due to Copy trait constraints, we need to modify how we access array elements // Instead of dereferencing, we'll check directly using the array let purchase_1 = dispatcher.get_purchase_details(purchase_id_1); let purchase_2 = dispatcher.get_purchase_details(purchase_id_2); - + assert(purchase_1.id == purchase_id_1, 'Purchase 1 ID mismatch'); assert(purchase_1.content_id == content_id_1, 'Purchase 1 content mismatch'); - + assert(purchase_2.id == purchase_id_2, 'Purchase 2 ID mismatch'); assert(purchase_2.content_id == content_id_2, 'Purchase 2 content mismatch'); } @@ -241,34 +244,34 @@ fn test_verify_purchase() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; - + // Set admin as caller to set up content price cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Set up content with price setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); - + // Set user as caller cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - + // Purchase the content let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); - + // Initially, purchase should not be verified (status is Pending) let is_verified = dispatcher.verify_purchase(purchase_id); assert(!is_verified, 'Purchase should not be verified'); - + // Set admin as caller to update the purchase status cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Update purchase status to Completed let update_result = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); assert(update_result, 'Failed to update status'); - + // Now the purchase should be verified let is_now_verified = dispatcher.verify_purchase(purchase_id); assert(is_now_verified, 'Purchase should be verified'); @@ -279,49 +282,51 @@ fn test_update_purchase_status() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; - + // Set admin as caller to set up content price cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Set up content with price setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); - + // Set user as caller for purchase cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - + // Purchase the content let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); - + // Get initial purchase let initial_purchase = dispatcher.get_purchase_details(purchase_id); assert(initial_purchase.status == PurchaseStatus::Pending, 'Status should be Pending'); - + // Set admin as caller to update the purchase status cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Test updating to different statuses // 1. Update to Completed - let update_to_completed = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); + let update_to_completed = dispatcher + .update_purchase_status(purchase_id, PurchaseStatus::Completed); assert(update_to_completed, 'Failed to update to Completed'); - + let completed_purchase = dispatcher.get_purchase_details(purchase_id); assert(completed_purchase.status == PurchaseStatus::Completed, 'Status should be Completed'); - + // 2. Update to Failed let update_to_failed = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Failed); assert(update_to_failed, 'Failed to update to Failed'); - + let failed_purchase = dispatcher.get_purchase_details(purchase_id); assert(failed_purchase.status == PurchaseStatus::Failed, 'Status should be Failed'); - + // 3. Update to Refunded - let update_to_refunded = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Refunded); + let update_to_refunded = dispatcher + .update_purchase_status(purchase_id, PurchaseStatus::Refunded); assert(update_to_refunded, 'Failed to update to Refunded'); - + let refunded_purchase = dispatcher.get_purchase_details(purchase_id); assert(refunded_purchase.status == PurchaseStatus::Refunded, 'Status should be Refunded'); } @@ -332,23 +337,23 @@ fn test_update_purchase_status_not_admin() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); - + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; - + // Set admin as caller to set up content price cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Set up content with price setup_content_with_price(dispatcher, admin_address, contract_address, content_id, price); - + // Set user as caller for purchase cheat_caller_address(contract_address, user_address, CheatSpan::Indefinite); - + // Purchase the content let purchase_id = dispatcher.purchase_content(content_id, 'tx1'); - + // Attempt to update purchase status as non-admin user // This should fail with the "Only admin can update status" error let _ = dispatcher.update_purchase_status(purchase_id, PurchaseStatus::Completed); @@ -359,10 +364,10 @@ fn test_update_purchase_status_not_admin() { fn test_update_nonexistent_purchase() { let (contract_address, admin_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; - + // Set admin as caller cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); - + // Attempt to update a purchase that doesn't exist let nonexistent_purchase_id = 42_u256; let _ = dispatcher.update_purchase_status(nonexistent_purchase_id, PurchaseStatus::Completed); From 3df049ccf0f38c2a956a64b44d03d904e8d895d7 Mon Sep 17 00:00:00 2001 From: hoossayn Date: Wed, 30 Apr 2025 11:28:23 +0100 Subject: [PATCH 4/6] Fix interface syntax and remove unused content_tags mapping --- src/chainlib/ChainLib.cairo | 1 - src/interfaces/IChainLib.cairo | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 54834b4..faf70e7 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -59,7 +59,6 @@ pub mod ChainLib { operator_permissions: Map::< (u256, ContractAddress), Permissions >, // Maps account_id and operator to permissions - content_tags: Map::>, // Purchase related storage content_prices: Map::, // Maps content_id to price next_purchase_id: u256, // Tracking the next available purchase ID diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index 77739d6..c906a80 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -55,9 +55,10 @@ pub trait IChainLib { ) -> felt252; fn get_content(ref self: TContractState, content_id: felt252) -> ContentMetadata; -} + fn set_content_price(ref self: TContractState, content_id: felt252, price: u256); + fn purchase_content( ref self: TContractState, content_id: felt252, transaction_hash: felt252 ) -> u256; From f5e9b36cb347fbf0dbfffd01ca31a948f2af1355 Mon Sep 17 00:00:00 2001 From: hoossayn Date: Sat, 3 May 2025 08:59:01 +0100 Subject: [PATCH 5/6] fix merge error --- src/interfaces/IChainLib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index db4b40d..86456e6 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -182,7 +182,6 @@ pub trait IChainLib { fn initialize_access_control(ref self: TContractState, default_cache_ttl: u64) -> bool; fn clear_access_cache(ref self: TContractState, user_id: u256, content_id: felt252) -> bool; -} fn purchase_content( ref self: TContractState, content_id: felt252, transaction_hash: felt252 @@ -195,4 +194,5 @@ pub trait IChainLib { fn update_purchase_status( ref self: TContractState, purchase_id: u256, status: PurchaseStatus ) -> bool; + } From 0ae382fc96eefb0f0729a39cbc460c24d20fbca9 Mon Sep 17 00:00:00 2001 From: hoossayn Date: Sat, 3 May 2025 11:43:06 +0100 Subject: [PATCH 6/6] fmt file --- src/base/types.cairo | 2 +- src/chainlib/ChainLib.cairo | 20 +++++++++----------- src/interfaces/IChainLib.cairo | 9 ++++----- tests/test_ChainLib.cairo | 2 +- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/base/types.cairo b/src/base/types.cairo index 61b7d73..15b713b 100644 --- a/src/base/types.cairo +++ b/src/base/types.cairo @@ -125,7 +125,7 @@ pub enum PurchaseStatus { Pending, Completed, Failed, - Refunded + Refunded, } #[derive(Drop, Serde, starknet::Store, Debug)] diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 8b2e87a..95837c8 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -17,7 +17,7 @@ pub mod ChainLib { use crate::base::types::{ TokenBoundAccount, User, Role, Rank, Permissions, permission_flags, AccessRule, AccessType, - VerificationRequirement, VerificationType, Purchase, PurchaseStatus + VerificationRequirement, VerificationType, Purchase, PurchaseStatus, }; // Define delegation-specific structures and constants @@ -155,18 +155,17 @@ pub mod ChainLib { user_reputation_verifications: Map, user_ownership_verifications: Map, user_custom_verifications: Map, - content_prices: Map::, // Maps content_id to price next_purchase_id: u256, // Tracking the next available purchase ID purchases: Map::, // Store purchases by ID user_purchase_count: Map::, // Count of purchases per user user_purchase_ids: Map::< - (ContractAddress, u32), u256 + (ContractAddress, u32), u256, >, // Map of (user, index) to purchase ID content_purchase_count: Map::, // Count of purchases per content content_purchase_ids: Map::< - (felt252, u32), u256 - >, // Map of (content_id, index) to purchase ID + (felt252, u32), u256, + > // Map of (content_id, index) to purchase ID } @@ -201,7 +200,6 @@ pub mod ChainLib { DelegationRevoked: DelegationRevoked, DelegationUsed: DelegationUsed, DelegationExpired: DelegationExpired, - ContentPurchased: ContentPurchased, PurchaseStatusUpdated: PurchaseStatusUpdated, } @@ -1562,7 +1560,7 @@ pub mod ChainLib { /// @param transaction_hash The hash of the transaction being used for payment. /// @return The unique ID of the newly created purchase. fn purchase_content( - ref self: ContractState, content_id: felt252, transaction_hash: felt252 + ref self: ContractState, content_id: felt252, transaction_hash: felt252, ) -> u256 { // Validate input parameters assert!(content_id != 0, "Content ID cannot be empty"); @@ -1605,7 +1603,7 @@ pub mod ChainLib { // Emit event for the purchase let timestamp = get_block_timestamp(); - self.emit(ContentPurchased { purchase_id, content_id, buyer, price, timestamp, }); + self.emit(ContentPurchased { purchase_id, content_id, buyer, price, timestamp }); // Return the purchase ID purchase_id @@ -1628,7 +1626,7 @@ pub mod ChainLib { /// @param user_address The address of the user whose purchases are being retrieved. /// @return Array An array of purchase records for the user. fn get_user_purchases( - ref self: ContractState, user_address: ContractAddress + ref self: ContractState, user_address: ContractAddress, ) -> Array { // Initialize an empty array to hold the purchases let mut purchases: Array = ArrayTrait::new(); @@ -1681,7 +1679,7 @@ pub mod ChainLib { /// @param status The new status to set for the purchase. /// @return bool True if the status was updated successfully. fn update_purchase_status( - ref self: ContractState, purchase_id: u256, status: PurchaseStatus + ref self: ContractState, purchase_id: u256, status: PurchaseStatus, ) -> bool { // Only admin can update purchase status let caller = get_caller_address(); @@ -1710,7 +1708,7 @@ pub mod ChainLib { PurchaseStatus::Refunded => 3_u8, }; - self.emit(PurchaseStatusUpdated { purchase_id, new_status: status_code, timestamp, }); + self.emit(PurchaseStatusUpdated { purchase_id, new_status: status_code, timestamp }); true } diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index 86456e6..0eabec4 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -2,7 +2,7 @@ use starknet::ContractAddress; use core::array::Array; use crate::base::types::{ TokenBoundAccount, User, Role, Rank, Permissions, AccessRule, VerificationRequirement, - VerificationType, Purchase, PurchaseStatus + VerificationType, Purchase, PurchaseStatus, }; use crate::chainlib::ChainLib::ChainLib::{ Category, Subscription, Payment, ContentType, ContentMetadata, DelegationInfo, @@ -184,15 +184,14 @@ pub trait IChainLib { fn clear_access_cache(ref self: TContractState, user_id: u256, content_id: felt252) -> bool; fn purchase_content( - ref self: TContractState, content_id: felt252, transaction_hash: felt252 + ref self: TContractState, content_id: felt252, transaction_hash: felt252, ) -> u256; fn get_purchase_details(ref self: TContractState, purchase_id: u256) -> Purchase; fn get_user_purchases( - ref self: TContractState, user_address: ContractAddress + ref self: TContractState, user_address: ContractAddress, ) -> Array; fn verify_purchase(ref self: TContractState, purchase_id: u256) -> bool; fn update_purchase_status( - ref self: TContractState, purchase_id: u256, status: PurchaseStatus + ref self: TContractState, purchase_id: u256, status: PurchaseStatus, ) -> bool; - } diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 3e9c973..6c09343 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -20,7 +20,7 @@ fn setup_content_with_price( admin_address: ContractAddress, contract_address: ContractAddress, content_id: felt252, - price: u256 + price: u256, ) { // Set admin as caller for setting content price cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite);