diff --git a/.tool-versions b/.tool-versions index 28c03e7..37dc27f 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ scarb 2.11.4 -starknet-foundry 0.40.0 \ No newline at end of file +starknet-foundry 0.43.1 diff --git a/src/base/types.cairo b/src/base/types.cairo index 9abd5ca..aac0e03 100644 --- a/src/base/types.cairo +++ b/src/base/types.cairo @@ -147,3 +147,24 @@ pub struct Purchase { pub transaction_hash: felt252, pub timeout_expiry: u64, } + + +#[derive(Drop, Serde, starknet::Store, Debug)] +pub enum ReceiptStatus { + #[default] + Invalid, + Valid, +} + +#[derive(Drop, Serde, starknet::Store, Debug)] +pub struct Receipt { + pub id: u256, + pub purchase_id: u256, + pub content_id: felt252, + pub buyer: ContractAddress, + pub creator: ContractAddress, + pub price: u256, + pub status: ReceiptStatus, + pub issued_at: u64, + pub transaction_hash: felt252, +} diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 5849618..7fb6247 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -14,8 +14,9 @@ pub mod ChainLib { }; use crate::base::errors::payment_errors; use crate::base::types::{ - AccessRule, AccessType, Permissions, Purchase, PurchaseStatus, Rank, Role, Status, - TokenBoundAccount, User, VerificationRequirement, VerificationType, permission_flags, + AccessRule, AccessType, Permissions, Purchase, PurchaseStatus, Rank, Receipt, ReceiptStatus, + Role, Status, TokenBoundAccount, User, VerificationRequirement, VerificationType, + permission_flags, }; use crate::interfaces::IChainLib::IChainLib; @@ -215,7 +216,12 @@ pub mod ChainLib { subscription_count: Map< u256, u256, >, // subscriber count to number of times the subscription record has been updated - token_address: ContractAddress // Address of the token contract used for payments + // RECEIPTS + receipt_counter: u256, + receipt: Map, + creator_sales: Map, + total_sales_for_content: Map, + token_address: ContractAddress, } @@ -259,6 +265,7 @@ pub mod ChainLib { PurchaseStatusUpdated: PurchaseStatusUpdated, SubscriptionCancelled: SubscriptionCancelled, SubscriptionRenewed: SubscriptionRenewed, + ReceiptGenerated: ReceiptGenerated, } #[derive(Drop, starknet::Event)] @@ -285,6 +292,11 @@ pub mod ChainLib { new_end_time: u64, } + #[derive(Drop, starknet::Event)] + struct ReceiptGenerated { + receipt_id: u256, + } + #[derive(Drop, starknet::Event)] struct SubscriptionCancelled { user: ContractAddress, @@ -1853,7 +1865,23 @@ pub mod ChainLib { fn verify_purchase(ref self: ContractState, purchase_id: u256) -> bool { // Get the purchase details let purchase = self.purchases.read(purchase_id); + let content = self.content.read(purchase.content_id); + let total_content_sales = self.total_sales_for_content.read(purchase.content_id) + + purchase.price; + let total_creator_sales = self.creator_sales.read(content.creator) + purchase.price; + self.creator_sales.write(content.creator, total_creator_sales); + + self.total_sales_for_content.write(purchase.content_id, total_content_sales); + self + .issue_receipt( + purchase_id, + purchase.content_id, + purchase.buyer, + content.creator, + purchase.price, + purchase.transaction_hash, + ); // A purchase is valid if its status is Completed if purchase.status == PurchaseStatus::Completed { return true; @@ -1984,6 +2012,61 @@ pub mod ChainLib { true } + + fn issue_receipt( + ref self: ContractState, + purchase_id: u256, + content_id: felt252, + buyer: ContractAddress, + creator: ContractAddress, + price: u256, + transaction_hash: felt252, + ) -> u256 { + let receipt_id = self.receipt_counter.read() + 1; + let issued_at = starknet::get_block_timestamp(); + + let receipt = Receipt { + id: receipt_id, + purchase_id, + content_id, + buyer, + creator, + price, + status: ReceiptStatus::Valid, + issued_at, + transaction_hash, + }; + self.receipt_counter.write(receipt_id); + self.receipt.write(receipt_id, receipt); + + self.emit(ReceiptGenerated { receipt_id }); + + receipt_id + } + + fn get_receipt(self: @ContractState, receipt_id: u256) -> Receipt { + let receipt = self.receipt.read(receipt_id); + receipt + } + + fn is_receipt_valid(self: @ContractState, receipt_id: u256) -> bool { + let receipt = self.receipt.read(receipt_id); + + match receipt.status { + ReceiptStatus::Valid => true, + ReceiptStatus::Invalid => false, + } + } + + fn get_total_sales_by_creator(self: @ContractState, creator: ContractAddress) -> u256 { + let total_sales = self.creator_sales.read(creator); + total_sales + } + + fn get_total_sales_for_content(self: @ContractState, content_id: felt252) -> u256 { + let total_content_sales = self.total_sales_for_content.read(content_id); + total_content_sales + } } #[generate_trait] diff --git a/src/interfaces/IChainLib.cairo b/src/interfaces/IChainLib.cairo index c0a487b..95d9c05 100644 --- a/src/interfaces/IChainLib.cairo +++ b/src/interfaces/IChainLib.cairo @@ -1,7 +1,7 @@ use core::array::Array; use starknet::ContractAddress; use crate::base::types::{ - AccessRule, Permissions, Purchase, PurchaseStatus, Rank, Role, TokenBoundAccount, User, + AccessRule, Permissions, Purchase, PurchaseStatus, Rank, Receipt, Role, TokenBoundAccount, User, VerificationRequirement, VerificationType, }; use crate::chainlib::ChainLib::ChainLib::{ @@ -213,4 +213,20 @@ pub trait IChainLib { fn get_user_subscription_record(ref self: TContractState, user_id: u256) -> Array; fn cancel_subscription(ref self: TContractState, user_id: u256) -> bool; fn renew_subscription(ref self: TContractState, user_id: u256) -> bool; + fn issue_receipt( + ref self: TContractState, + purchase_id: u256, + content_id: felt252, + buyer: ContractAddress, + creator: ContractAddress, + price: u256, + transaction_hash: felt252, + ) -> u256; + + fn get_receipt(self: @TContractState, receipt_id: u256) -> Receipt; + fn is_receipt_valid(self: @TContractState, receipt_id: u256) -> bool; + fn get_total_sales_by_creator(self: @TContractState, creator: ContractAddress) -> u256; + fn get_total_sales_for_content(self: @TContractState, content_id: felt252) -> u256; + // fn get_daily_sales(self: @TContractState, day: u64) -> u256; +// fn get_unique_buyers_count(self: @TContractState) -> u256; } diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 067a439..e16f6a0 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -714,6 +714,10 @@ fn test_verify_purchase() { // Now the purchase should be verified let is_now_verified = dispatcher.verify_purchase(purchase_id); assert(is_now_verified, 'Purchase should be verified'); + + let receipt = dispatcher.get_receipt(1); + + assert(receipt.purchase_id == purchase_id, 'receipt error'); } #[test]