From 040556c5f41caefb65ab1907832db7348901503b Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 16:58:59 +0100 Subject: [PATCH 01/15] feat: include openzeppelion in scarb --- Scarb.lock | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++ Scarb.toml | 1 + 2 files changed, 121 insertions(+) diff --git a/Scarb.lock b/Scarb.lock index d2649db..67416b6 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -5,9 +5,129 @@ version = 1 name = "chain_lib" version = "0.1.0" dependencies = [ + "openzeppelin", "snforge_std", ] +[[package]] +name = "openzeppelin" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:320185f3e17cf9fafda88b1ce490f5eaed0bfcc273036b56cd22ce4fb8de628f" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_governance", + "openzeppelin_introspection", + "openzeppelin_merkle_tree", + "openzeppelin_presets", + "openzeppelin_security", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_access" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a39a4ea1582916c637bf7e3aee0832c3fe1ea3a3e39191955e8dc39d08327f9b" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_account" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:7e943a2de32ddca4d48e467e52790e380ab1f49c4daddbbbc4634dd930d0243f" +dependencies = [ + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_finance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:9fa9e91d39b6ccdfa31eef32fdc087cd06c0269cc9c6b86e32d57f5a6997d98b" +dependencies = [ + "openzeppelin_access", + "openzeppelin_token", +] + +[[package]] +name = "openzeppelin_governance" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:c05add2974b3193c3a5c022b9586a84cf98c5970cdb884dcf201c77dbe359f55" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_introspection" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:34e088ecf19e0b3012481a29f1fbb20e600540cb9a5db1c3002a97ebb7f5a32a" + +[[package]] +name = "openzeppelin_merkle_tree" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:a5341705514a3d9beeeb39cf11464111f7355be621639740d2c5006786aa63dc" + +[[package]] +name = "openzeppelin_presets" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:4eb098e2ee3ac0e67b6828115a7de62f781418beab767d4e80b54e176808369d" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_finance", + "openzeppelin_introspection", + "openzeppelin_token", + "openzeppelin_upgrades", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_security" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:1deb811a239c4f9cc28fc302039e2ffcb19911698a8c612487207448d70d2e6e" + +[[package]] +name = "openzeppelin_token" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:33fcb84a1a76d2d3fff9302094ff564f78d45b743548fd7568c130b272473f66" +dependencies = [ + "openzeppelin_access", + "openzeppelin_account", + "openzeppelin_introspection", + "openzeppelin_utils", +] + +[[package]] +name = "openzeppelin_upgrades" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:36f7a03e7e7111577916aacf31f88ad0053de20f33ee10b0ab3804849c3aa373" + +[[package]] +name = "openzeppelin_utils" +version = "1.0.0" +source = "registry+https://scarbs.xyz/" +checksum = "sha256:fd348b31c4a4407add33adc3c2b8f26dca71dbd7431faaf726168f37a91db0c1" + [[package]] name = "snforge_scarb_plugin" version = "0.40.0" diff --git a/Scarb.toml b/Scarb.toml index 92d89cd..805dec4 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -5,6 +5,7 @@ edition = "2023_11" [dependencies] starknet = "2.11.2" +openzeppelin = "1.0.0" [dev-dependencies] snforge_std = { git = "https://github.com/foundry-rs/starknet-foundry", tag = "v0.40.0" } From c335a785e9118bc3c7a80441f9729b74d0c4c23d Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:06:27 +0100 Subject: [PATCH 02/15] feat: add token address to constructor and update storage structure --- src/chainlib/ChainLib.cairo | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 9bc077d..0c087aa 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -15,6 +15,8 @@ pub mod ChainLib { TokenBoundAccount, User, VerificationRequirement, VerificationType, permission_flags, }; use crate::interfaces::IChainLib::IChainLib; + use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + // Define delegation-specific structures and constants @@ -211,14 +213,16 @@ pub mod ChainLib { subscription_record: Map>, // subcription id to subscription record subscription_count: Map< u256, u256, - > // subscriber count to number of times the subscription record has been updated + >, // subscriber count to number of times the subscription record has been updated + token_address: ContractAddress, // Address of the token contract used for payments } #[constructor] - fn constructor(ref self: ContractState, admin: ContractAddress) { + fn constructor(ref self: ContractState, admin: ContractAddress, token_address: ContractAddress) { // Store the values in contract state self.admin.write(admin); + self.token_address.write(token_address); // Initialize purchase ID counter self.next_purchase_id.write(1_u256); self.purchase_timeout_duration.write(3600); From faac29e4dfe3126d52218f16b374b25735db5925 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:09:57 +0100 Subject: [PATCH 03/15] feat: implement payment processing and token allowance check in ChainLib --- src/chainlib/ChainLib.cairo | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 0c087aa..3ff3c70 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -764,6 +764,8 @@ pub mod ChainLib { // Only allow the subscriber themselves to create a subscription assert(caller == subscriber, 'Only subscriber can call'); + self.process_payment(amount); + // Create a new subscription let subscription_id = self.subscription_id.read(); let subscription_plan: Subscription = self.subscriptions.read(subscription_id); @@ -1971,4 +1973,18 @@ pub mod ChainLib { true } } + + #[generate_trait] + impl internal of InternalTraits { + fn process_payment(ref self: ContractState, amount: u256){ + + } + + fn check_token_allowance( + ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, + ) -> bool { + + } + + } } From 5d11124e28bbfa9138c68c80959fb4ed0bd25531 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:18:04 +0100 Subject: [PATCH 04/15] feat: impl the process_payment function --- src/chainlib/ChainLib.cairo | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 3ff3c70..60ed007 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -8,7 +8,7 @@ pub mod ChainLib { StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, }; use starknet::{ - ContractAddress, contract_address_const, get_block_timestamp, get_caller_address, + ContractAddress, contract_address_const, get_block_timestamp, get_caller_address, get_contract_address }; use crate::base::types::{ AccessRule, AccessType, Permissions, Purchase, PurchaseStatus, Rank, Role, Status, @@ -1976,14 +1976,34 @@ pub mod ChainLib { #[generate_trait] impl internal of InternalTraits { + /// @notice Processes a payment for a subscription or content purchase. + /// @dev Checks the token allowance and balance before transferring tokens. + /// @param self The contract state reference. + /// @param amount The amount of tokens to transfer. + /// @require The caller must have sufficient token allowance and balance. fn process_payment(ref self: ContractState, amount: u256){ - + let strk_token = IERC20Dispatcher { contract_address: self.token_address.read() }; + let caller = get_caller_address(); + let contract_address = get_contract_address(); + self.check_token_allowance(caller, amount); + self.check_token_balance(caller, amount); + strk_token.transfer_from(caller, contract_address, amount); } fn check_token_allowance( - ref self: ContractState, owner: ContractAddress, spender: ContractAddress, amount: u256, - ) -> bool { - + ref self: ContractState, spender: ContractAddress, amount: u256, + ) { + // let token = IERC20Dispatcher { contract_address: self.token_address.read() }; + // let allowance = token.allowance(spender, starknet::get_contract_address()); + // assert(allowance >= amount, 'Insufficient token allowance'); + } + + fn check_token_balance( + ref self: ContractState, caller: ContractAddress, amount: u256, + ) { + // let token = IERC20Dispatcher { contract_address: self.token_address.read() }; + // let balance = token.balance_of(caller); + // assert(balance >= amount, 'Insufficient token balance'); } } From 7d3a05457e58350f7184cd4e106cc0e79b1bfa2e Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:18:44 +0100 Subject: [PATCH 05/15] feat: implement the check_token_allowance_function --- src/chainlib/ChainLib.cairo | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 60ed007..64a00fe 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -1990,12 +1990,18 @@ pub mod ChainLib { strk_token.transfer_from(caller, contract_address, amount); } + /// @notice Checks if the caller has sufficient token allowance. + /// @dev Asserts that the caller has enough allowance to transfer the specified amount. + /// @param self The contract state reference. + /// @param spender The address of the spender (usually the contract itself). + /// @param amount The amount of tokens to check allowance for. + /// @require The caller must have sufficient token allowance. fn check_token_allowance( ref self: ContractState, spender: ContractAddress, amount: u256, ) { - // let token = IERC20Dispatcher { contract_address: self.token_address.read() }; - // let allowance = token.allowance(spender, starknet::get_contract_address()); - // assert(allowance >= amount, 'Insufficient token allowance'); + let token = IERC20Dispatcher { contract_address: self.token_address.read() }; + let allowance = token.allowance(spender, starknet::get_contract_address()); + assert(allowance >= amount, 'Insufficient token allowance'); } fn check_token_balance( From 7d75fa1e3c7cef289e8ab59a7e2b22ce1925cd5d Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:23:42 +0100 Subject: [PATCH 06/15] feat: add check_token_balance function to verify caller's token balance --- src/chainlib/ChainLib.cairo | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 64a00fe..7c7a70f 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -2004,12 +2004,18 @@ pub mod ChainLib { assert(allowance >= amount, 'Insufficient token allowance'); } + /// @notice Checks if the caller has sufficient token balance. + /// @dev Asserts that the caller has enough balance to transfer the specified amount. + /// @param self The contract state reference. + /// @param caller The address of the caller (usually the user). + /// @param amount The amount of tokens to check balance for. + /// @require The caller must have sufficient token balance. fn check_token_balance( ref self: ContractState, caller: ContractAddress, amount: u256, ) { - // let token = IERC20Dispatcher { contract_address: self.token_address.read() }; - // let balance = token.balance_of(caller); - // assert(balance >= amount, 'Insufficient token balance'); + let token = IERC20Dispatcher { contract_address: self.token_address.read() }; + let balance = token.balance_of(caller); + assert(balance >= amount, 'Insufficient token balance'); } } From a7c73dcee530f7cbdb5b82c490a87ed2993c95b1 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:25:24 +0100 Subject: [PATCH 07/15] refactor: rename payment processing functions for consistency and clarity --- src/chainlib/ChainLib.cairo | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 7c7a70f..9c7843d 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -764,7 +764,7 @@ pub mod ChainLib { // Only allow the subscriber themselves to create a subscription assert(caller == subscriber, 'Only subscriber can call'); - self.process_payment(amount); + self._process_payment(amount); // Create a new subscription let subscription_id = self.subscription_id.read(); @@ -1981,12 +1981,12 @@ pub mod ChainLib { /// @param self The contract state reference. /// @param amount The amount of tokens to transfer. /// @require The caller must have sufficient token allowance and balance. - fn process_payment(ref self: ContractState, amount: u256){ + fn _process_payment(ref self: ContractState, amount: u256){ let strk_token = IERC20Dispatcher { contract_address: self.token_address.read() }; let caller = get_caller_address(); let contract_address = get_contract_address(); - self.check_token_allowance(caller, amount); - self.check_token_balance(caller, amount); + self._check_token_allowance(caller, amount); + self._check_token_balance(caller, amount); strk_token.transfer_from(caller, contract_address, amount); } @@ -1996,7 +1996,7 @@ pub mod ChainLib { /// @param spender The address of the spender (usually the contract itself). /// @param amount The amount of tokens to check allowance for. /// @require The caller must have sufficient token allowance. - fn check_token_allowance( + fn _check_token_allowance( ref self: ContractState, spender: ContractAddress, amount: u256, ) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; @@ -2010,7 +2010,7 @@ pub mod ChainLib { /// @param caller The address of the caller (usually the user). /// @param amount The amount of tokens to check balance for. /// @require The caller must have sufficient token balance. - fn check_token_balance( + fn _check_token_balance( ref self: ContractState, caller: ContractAddress, amount: u256, ) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; From 322ba4100b6be45bdcf53ebba8001a05222f57c1 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:29:15 +0100 Subject: [PATCH 08/15] feat: add payment error constants and update assertions in ChainLib --- src/base/errors.cairo | 5 +++++ src/chainlib/ChainLib.cairo | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/base/errors.cairo b/src/base/errors.cairo index de7d923..a88a62a 100644 --- a/src/base/errors.cairo +++ b/src/base/errors.cairo @@ -6,3 +6,8 @@ pub mod permission_errors { pub const INVALID_PERMISSION: felt252 = 'Invalid permission value'; pub const ZERO_ADDRESS: felt252 = 'Zero address'; } + +pub mod payment_errors { + pub const INSUFFICIENT_ALLOWANCE: felt252 = 'Insufficient token allowance'; + pub const INSUFFICIENT_BALANCE: felt252 = 'Insufficient token balance'; +} \ No newline at end of file diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 9c7843d..0fee456 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -16,7 +16,7 @@ pub mod ChainLib { }; use crate::interfaces::IChainLib::IChainLib; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; - + use crate::base::errors::payment_errors; // Define delegation-specific structures and constants @@ -2001,7 +2001,7 @@ pub mod ChainLib { ) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; let allowance = token.allowance(spender, starknet::get_contract_address()); - assert(allowance >= amount, 'Insufficient token allowance'); + assert(allowance >= amount, payment_errors::INSUFFICIENT_ALLOWANCE); } /// @notice Checks if the caller has sufficient token balance. @@ -2015,7 +2015,7 @@ pub mod ChainLib { ) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; let balance = token.balance_of(caller); - assert(balance >= amount, 'Insufficient token balance'); + assert(balance >= amount, payment_errors::INSUFFICIENT_BALANCE); } } From 0bb7f7d8e9e0069e36dd435aa9ae18e477ed9259 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:43:01 +0100 Subject: [PATCH 09/15] feat: update payment error handling and improve code formatting in ChainLib --- src/base/errors.cairo | 2 +- src/chainlib/ChainLib.cairo | 38 +++++++++++++++++++++++++------------ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/base/errors.cairo b/src/base/errors.cairo index a88a62a..c95eb45 100644 --- a/src/base/errors.cairo +++ b/src/base/errors.cairo @@ -10,4 +10,4 @@ pub mod permission_errors { pub mod payment_errors { pub const INSUFFICIENT_ALLOWANCE: felt252 = 'Insufficient token allowance'; pub const INSUFFICIENT_BALANCE: felt252 = 'Insufficient token balance'; -} \ No newline at end of file +} diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index 0fee456..c1b9eb6 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -3,20 +3,21 @@ pub mod ChainLib { use core::array::{Array, ArrayTrait}; use core::option::OptionTrait; use core::traits::Into; + use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use starknet::storage::{ Map, MutableVecTrait, StorageMapReadAccess, StorageMapWriteAccess, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, Vec, VecTrait, }; use starknet::{ - ContractAddress, contract_address_const, get_block_timestamp, get_caller_address, get_contract_address + ContractAddress, contract_address_const, get_block_timestamp, get_caller_address, + get_contract_address, }; + use crate::base::errors::payment_errors; use crate::base::types::{ AccessRule, AccessType, Permissions, Purchase, PurchaseStatus, Rank, Role, Status, TokenBoundAccount, User, VerificationRequirement, VerificationType, permission_flags, }; use crate::interfaces::IChainLib::IChainLib; - use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; - use crate::base::errors::payment_errors; // Define delegation-specific structures and constants @@ -214,12 +215,14 @@ 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 + token_address: ContractAddress // Address of the token contract used for payments } #[constructor] - fn constructor(ref self: ContractState, admin: ContractAddress, token_address: ContractAddress) { + fn constructor( + ref self: ContractState, admin: ContractAddress, token_address: ContractAddress, + ) { // Store the values in contract state self.admin.write(admin); self.token_address.write(token_address); @@ -868,6 +871,9 @@ pub mod ChainLib { // For simplicity, we'll allow any recurring payment after the initial payment assert(current_time > subscription.last_payment_date, 'Payment not due yet'); + // Process the payment + self._process_payment(subscription.amount); + // Default subscription period is 30 days (in seconds) let subscription_period: u64 = 30 * 24 * 60 * 60; @@ -985,6 +991,9 @@ pub mod ChainLib { // Verify payment exists and is not already refunded assert(!payment.is_refunded, 'Payment already refunded'); + // process the refund + self._process_refund(payment.amount, payment.refund_address); + // Mark payment as refunded payment.is_refunded = true; self.payments.write(payment_id, payment); @@ -1745,6 +1754,9 @@ pub mod ChainLib { let price = self.content_prices.read(content_id); assert!(price > 0, "Content either doesn't exist or has no price"); + // process the purchase + self._process_payment(price); + let buyer = get_caller_address(); let current_time = get_block_timestamp(); @@ -1981,7 +1993,7 @@ pub mod ChainLib { /// @param self The contract state reference. /// @param amount The amount of tokens to transfer. /// @require The caller must have sufficient token allowance and balance. - fn _process_payment(ref self: ContractState, amount: u256){ + fn _process_payment(ref self: ContractState, amount: u256) { let strk_token = IERC20Dispatcher { contract_address: self.token_address.read() }; let caller = get_caller_address(); let contract_address = get_contract_address(); @@ -1996,9 +2008,7 @@ pub mod ChainLib { /// @param spender The address of the spender (usually the contract itself). /// @param amount The amount of tokens to check allowance for. /// @require The caller must have sufficient token allowance. - fn _check_token_allowance( - ref self: ContractState, spender: ContractAddress, amount: u256, - ) { + fn _check_token_allowance(ref self: ContractState, spender: ContractAddress, amount: u256) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; let allowance = token.allowance(spender, starknet::get_contract_address()); assert(allowance >= amount, payment_errors::INSUFFICIENT_ALLOWANCE); @@ -2010,13 +2020,17 @@ pub mod ChainLib { /// @param caller The address of the caller (usually the user). /// @param amount The amount of tokens to check balance for. /// @require The caller must have sufficient token balance. - fn _check_token_balance( - ref self: ContractState, caller: ContractAddress, amount: u256, - ) { + fn _check_token_balance(ref self: ContractState, caller: ContractAddress, amount: u256) { let token = IERC20Dispatcher { contract_address: self.token_address.read() }; let balance = token.balance_of(caller); assert(balance >= amount, payment_errors::INSUFFICIENT_BALANCE); } + fn _process_refund(ref self: ContractState, amount: u256, refund_address: ContractAddress) { + let token = IERC20Dispatcher { contract_address: self.token_address.read() }; + let contract_address = get_contract_address(); + self._check_token_balance(contract_address, amount); + token.transfer(refund_address, amount); + } } } From 991a2d92e76d3c099e10e446123d6859c0ed94d7 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sat, 28 Jun 2025 17:50:52 +0100 Subject: [PATCH 10/15] feat: update refund processing to use caller's address instead of refund address --- src/chainlib/ChainLib.cairo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/chainlib/ChainLib.cairo b/src/chainlib/ChainLib.cairo index c1b9eb6..5849618 100644 --- a/src/chainlib/ChainLib.cairo +++ b/src/chainlib/ChainLib.cairo @@ -992,7 +992,7 @@ pub mod ChainLib { assert(!payment.is_refunded, 'Payment already refunded'); // process the refund - self._process_refund(payment.amount, payment.refund_address); + self._process_refund(payment.amount, get_caller_address()); // Mark payment as refunded payment.is_refunded = true; From 424dd47c35efe23fa91c472a2cdfd909be22eb52 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sun, 29 Jun 2025 17:49:14 +0100 Subject: [PATCH 11/15] feat: add mock ERC20 contract implementation and update tests to integrate ERC20 functionality --- src/lib.cairo | 3 ++ src/presets/mock_erc20.cairo | 76 ++++++++++++++++++++++++++++++ tests/test_ChainLib.cairo | 91 +++++++++++++++++++++++------------- 3 files changed, 138 insertions(+), 32 deletions(-) create mode 100644 src/presets/mock_erc20.cairo diff --git a/src/lib.cairo b/src/lib.cairo index f727319..d74f5d8 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -9,3 +9,6 @@ pub mod interfaces { pub mod IChainLib; } +pub mod presets { + pub mod mock_erc20; +} \ No newline at end of file diff --git a/src/presets/mock_erc20.cairo b/src/presets/mock_erc20.cairo new file mode 100644 index 0000000..509ef76 --- /dev/null +++ b/src/presets/mock_erc20.cairo @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: MIT +use starknet::ContractAddress; + +#[starknet::interface] +pub trait IExternal { + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256); +} +#[starknet::contract] +pub mod mock_erc20 { + use openzeppelin::access::ownable::OwnableComponent; + use openzeppelin::token::erc20::interface::IERC20Metadata; + use openzeppelin::token::erc20::{ERC20Component, ERC20HooksEmptyImpl}; + use starknet::ContractAddress; + + component!(path: ERC20Component, storage: erc20, event: ERC20Event); + component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); + + #[storage] + pub struct Storage { + #[substorage(v0)] + pub erc20: ERC20Component::Storage, + #[substorage(v0)] + pub ownable: OwnableComponent::Storage, + custom_decimals: u8 // Add custom decimals storage + } + + #[event] + #[derive(Drop, starknet::Event)] + enum Event { + #[flat] + ERC20Event: ERC20Component::Event, + #[flat] + OwnableEvent: OwnableComponent::Event, + } + + #[constructor] + fn constructor( + ref self: ContractState, recipient: ContractAddress, owner: ContractAddress, decimals: u8, + ) { + self.erc20.initializer(format!("USDC"), format!("USDC")); + self.ownable.initializer(owner); + self.custom_decimals.write(decimals); + + self.erc20.mint(recipient, core::num::traits::Bounded::::MAX); + } + + #[abi(embed_v0)] + impl CustomERC20MetadataImpl of IERC20Metadata { + fn name(self: @ContractState) -> ByteArray { + self.erc20.name() + } + + fn symbol(self: @ContractState) -> ByteArray { + self.erc20.symbol() + } + + fn decimals(self: @ContractState) -> u8 { + self.custom_decimals.read() // Return custom value + } + } + + // Keep existing implementations + #[abi(embed_v0)] + impl ERC20Impl = ERC20Component::ERC20Impl; + #[abi(embed_v0)] + impl OwnableImpl = OwnableComponent::OwnableImpl; + impl InternalImpl = ERC20Component::InternalImpl; + impl OwnableInternalImpl = OwnableComponent::InternalImpl; + + #[abi(embed_v0)] + impl ExternalImpl of super::IExternal { + fn mint(ref self: ContractState, recipient: ContractAddress, amount: u256) { + self.erc20.mint(recipient, amount); + } + } +} diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index e368833..6b0d6ec 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -3,6 +3,8 @@ use chain_lib::base::types::{PurchaseStatus, Rank, Role, Status}; use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentType, PlanType, SubscriptionStatus}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; + use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, start_cheat_caller_address, stop_cheat_caller_address, @@ -28,25 +30,32 @@ fn setup_content_with_price( dispatcher.set_content_price(content_id, price); } -fn setup() -> (ContractAddress, ContractAddress) { - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'Contract declaration failed'); +fn setup() -> (ContractAddress, ContractAddress, ContractAddress) { let admin_address: ContractAddress = contract_address_const::<'admin'>(); + // Deploy mock ERC20 + let erc20_class = declare("mock_erc20").unwrap().contract_class(); + let mut calldata = array![admin_address.into(), admin_address.into(), 6]; + let (erc20_address, _) = erc20_class.deploy(@calldata).unwrap(); + + // Deploy the ChainLib contract + let declare_result = declare("ChainLib"); + assert(declare_result.is_ok(), 'Contract declaration failed'); + let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into()]; + let mut calldata = array![admin_address.into(), erc20_address.into()]; let deploy_result = contract_class.deploy(@calldata); assert(deploy_result.is_ok(), 'Contract deployment failed'); let (contract_address, _) = deploy_result.unwrap(); - (contract_address, admin_address) + (contract_address, admin_address, erc20_address) } #[test] fn test_initial_data() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; @@ -59,7 +68,7 @@ fn test_initial_data() { #[test] fn test_create_token_bount_account() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -83,7 +92,7 @@ fn test_create_token_bount_account() { #[test] fn test_create_user() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -109,7 +118,7 @@ fn test_create_user() { #[test] fn test_verify_user() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -134,7 +143,7 @@ fn test_verify_user() { #[test] fn test_update_user() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -185,7 +194,7 @@ fn test_update_user() { #[test] fn test_deactivate_user() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -216,7 +225,7 @@ fn test_deactivate_user() { #[test] #[should_panic] fn test_deactivate_user_should_panic_if_diffrent_Address() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -248,7 +257,7 @@ fn test_deactivate_user_should_panic_if_diffrent_Address() { #[test] #[should_panic] fn test_update_user_should_panic_when_address_is_0() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -291,7 +300,7 @@ fn test_update_user_should_panic_when_address_is_0() { #[test] #[should_panic(expected: 'Only admin can verify users')] fn test_verify_user_not_admin() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -315,7 +324,7 @@ fn test_verify_user_not_admin() { #[test] fn test_create_subscription() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -339,7 +348,7 @@ fn test_create_subscription() { #[test] #[should_panic(expected: 'User does not exist')] fn test_create_subscription_invalid_user() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -359,7 +368,7 @@ fn test_create_subscription_invalid_user() { #[test] #[should_panic(expected: "Only WRITER can post content")] fn test_grant_premium_access_test_admin() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -386,7 +395,7 @@ fn test_grant_premium_access_test_admin() { #[test] #[should_panic(expected: "Only WRITER can post content")] fn test_is_in_blacklist() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let creator = contract_address_const::<'creator'>(); @@ -416,7 +425,7 @@ fn test_is_in_blacklist() { #[test] #[should_panic(expected: "Only WRITER can post content")] fn test_revoke_access_by_admin() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -443,7 +452,7 @@ fn test_revoke_access_by_admin() { #[test] fn test_has_active_subscription() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -464,7 +473,7 @@ fn test_has_active_subscription() { #[test] fn test_set_cache_ttl() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; start_cheat_caller_address(contract_address, admin); @@ -476,7 +485,7 @@ fn test_set_cache_ttl() { #[test] #[should_panic(expected: "Only WRITER can post content")] fn test_verify_access() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -502,7 +511,7 @@ fn test_verify_access() { #[test] #[should_panic(expected: "Only WRITER can post content")] fn test_determine_access() { - let (contract_address, admin) = setup(); + let (contract_address, admin, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -527,7 +536,7 @@ fn test_determine_access() { #[test] fn test_purchase_content() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -558,7 +567,7 @@ fn test_purchase_content() { #[test] #[should_panic(expected: "Content either doesn't exist")] fn test_purchase_nonexistent_content() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -575,7 +584,7 @@ fn test_purchase_nonexistent_content() { #[test] fn test_get_user_purchases() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -584,6 +593,24 @@ fn test_get_user_purchases() { let content_id_2: felt252 = 'content2'; let price: u256 = 1000_u256; + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + + // transfer from origin to user + start_cheat_caller_address(contract_address, admin_address); + token_dispatcher.transfer(user_address, 1000); + stop_cheat_caller_address(contract_address); + + let user_token_balance = token_dispatcher.balance_of(user_address); + assert(user_token_balance >= 1000, 'User tokens not gotten'); + + // Simulate delegate's approval: + start_cheat_caller_address(contract_address, user_address); + token_dispatcher.approve(contract_address, 1000); + stop_cheat_caller_address(contract_address); + + let allowance = token_dispatcher.allowance(user_address, contract_address); + assert(allowance >= 1000, 'Allowance not set correctly'); + // Set admin as caller to prepare the contract state cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); @@ -618,7 +645,7 @@ fn test_get_user_purchases() { #[test] fn test_verify_purchase() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -656,7 +683,7 @@ fn test_verify_purchase() { #[test] fn test_update_purchase_status() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -711,7 +738,7 @@ fn test_update_purchase_status() { #[test] #[should_panic(expected: 'Only admin can update status')] fn test_update_purchase_status_not_admin() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); @@ -739,7 +766,7 @@ fn test_update_purchase_status_not_admin() { #[test] #[should_panic(expected: 'Purchase does not exist')] fn test_update_nonexistent_purchase() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Set admin as caller @@ -752,7 +779,7 @@ fn test_update_nonexistent_purchase() { #[test] fn test_cancel_subscription() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -783,7 +810,7 @@ fn test_cancel_subscription() { #[test] fn test_renew_subscription() { - let (contract_address, _) = setup(); + let (contract_address, _, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values From 0ac24eab574067e5e9cda7e4169e9ac982c607a8 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sun, 29 Jun 2025 19:14:38 +0100 Subject: [PATCH 12/15] feat: implement token faucet and allowance setup in tests for ChainLib --- src/lib.cairo | 2 +- tests/test_ChainLib.cairo | 58 +++++++++++++++++++++++++-------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/src/lib.cairo b/src/lib.cairo index d74f5d8..ceb6807 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -11,4 +11,4 @@ pub mod interfaces { pub mod presets { pub mod mock_erc20; -} \ No newline at end of file +} diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 6b0d6ec..c6ccfe1 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -4,7 +4,6 @@ use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentType, PlanType, SubscriptionStatus}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; - use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, start_cheat_caller_address, stop_cheat_caller_address, @@ -30,6 +29,33 @@ fn setup_content_with_price( dispatcher.set_content_price(content_id, price); } +// Token faucet and allowance setup +fn token_faucet_and_allowance( + dispatcher: IChainLibDispatcher, + user_address: ContractAddress, + erc20_address: ContractAddress, + token_amount: u256, +) { + let admin_address = contract_address_const::<'admin'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(user_address, token_amount); + stop_cheat_caller_address(erc20_address); + + let user_token_balance = token_dispatcher.balance_of(user_address); + assert(user_token_balance >= token_amount, 'User tokens not gotten'); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, user_address); + token_dispatcher.approve(dispatcher.contract_address, token_amount); + stop_cheat_caller_address(erc20_address); + + let allowance = token_dispatcher.allowance(user_address, dispatcher.contract_address); + assert(allowance >= token_amount, 'Allowance not set correctly'); +} + fn setup() -> (ContractAddress, ContractAddress, ContractAddress) { let admin_address: ContractAddress = contract_address_const::<'admin'>(); @@ -40,7 +66,7 @@ fn setup() -> (ContractAddress, ContractAddress, ContractAddress) { // Deploy the ChainLib contract let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'Contract declaration failed'); + assert(declare_result.is_ok(), 'Contract declaration failed'); let contract_class = declare_result.unwrap().contract_class(); let mut calldata = array![admin_address.into(), erc20_address.into()]; @@ -540,6 +566,9 @@ fn test_purchase_content() { let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); + // Token faucet and allowance setup + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; @@ -592,28 +621,11 @@ fn test_get_user_purchases() { let content_id_1: felt252 = 'content1'; let content_id_2: felt252 = 'content2'; let price: u256 = 1000_u256; - - let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; - - // transfer from origin to user - start_cheat_caller_address(contract_address, admin_address); - token_dispatcher.transfer(user_address, 1000); - stop_cheat_caller_address(contract_address); - - let user_token_balance = token_dispatcher.balance_of(user_address); - assert(user_token_balance >= 1000, 'User tokens not gotten'); - - // Simulate delegate's approval: - start_cheat_caller_address(contract_address, user_address); - token_dispatcher.approve(contract_address, 1000); - stop_cheat_caller_address(contract_address); - - let allowance = token_dispatcher.allowance(user_address, contract_address); - assert(allowance >= 1000, 'Allowance not set correctly'); + + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); // 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); @@ -649,6 +661,7 @@ fn test_verify_purchase() { let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; @@ -687,6 +700,8 @@ fn test_update_purchase_status() { let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); + // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; @@ -742,6 +757,7 @@ fn test_update_purchase_status_not_admin() { let dispatcher = IChainLibDispatcher { contract_address }; let user_address = contract_address_const::<'user'>(); + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); // Set up test data let content_id: felt252 = 'content1'; let price: u256 = 1000_u256; From ee5c3635ce4b0ad45586ee928f908f884b9a9117 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sun, 29 Jun 2025 20:49:33 +0100 Subject: [PATCH 13/15] feat: add test utilities for setup and token management in ChainLib tests --- src/lib.cairo | 4 ++ src/utils/test_utils.cairo | 74 ++++++++++++++++++++++ tests/lib.cairo | 1 - tests/test_ChainLib.cairo | 71 +-------------------- tests/test_account_delegation.cairo | 35 ++++------- tests/test_contentaccess.cairo | 26 ++------ tests/test_contentpost.cairo | 45 ++++++------- tests/test_permissions.cairo | 35 +++-------- tests/test_subscription.cairo | 97 ++++++++++++++++++----------- 9 files changed, 187 insertions(+), 201 deletions(-) create mode 100644 src/utils/test_utils.cairo diff --git a/src/lib.cairo b/src/lib.cairo index ceb6807..32a53ed 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -12,3 +12,7 @@ pub mod interfaces { pub mod presets { pub mod mock_erc20; } + +pub mod utils { + pub mod test_utils; +} diff --git a/src/utils/test_utils.cairo b/src/utils/test_utils.cairo new file mode 100644 index 0000000..e893ecf --- /dev/null +++ b/src/utils/test_utils.cairo @@ -0,0 +1,74 @@ +use chain_lib::interfaces::IChainLib::{IChainLibDispatcher, IChainLibDispatcherTrait}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; +use snforge_std::{ + CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, + start_cheat_caller_address, stop_cheat_caller_address, +}; +use starknet::{ContractAddress, contract_address_const}; + +pub fn setup() -> (ContractAddress, ContractAddress, ContractAddress) { + let admin_address: ContractAddress = contract_address_const::<'admin'>(); + + // Deploy mock ERC20 + let erc20_class = declare("mock_erc20").unwrap().contract_class(); + let mut calldata = array![admin_address.into(), admin_address.into(), 6]; + let (erc20_address, _) = erc20_class.deploy(@calldata).unwrap(); + + // Deploy the ChainLib contract + let declare_result = declare("ChainLib"); + assert(declare_result.is_ok(), 'Contract declaration failed'); + + let contract_class = declare_result.unwrap().contract_class(); + let mut calldata = array![admin_address.into(), erc20_address.into()]; + + let deploy_result = contract_class.deploy(@calldata); + assert(deploy_result.is_ok(), 'Contract deployment failed'); + + let (contract_address, _) = deploy_result.unwrap(); + + (contract_address, admin_address, erc20_address) +} + + +// Token faucet and allowance setup +pub fn token_faucet_and_allowance( + dispatcher: IChainLibDispatcher, + user_address: ContractAddress, + erc20_address: ContractAddress, + token_amount: u256, +) { + let admin_address = contract_address_const::<'admin'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(user_address, token_amount); + stop_cheat_caller_address(erc20_address); + + let user_token_balance = token_dispatcher.balance_of(user_address); + assert(user_token_balance >= token_amount, 'User tokens not gotten'); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, user_address); + token_dispatcher.approve(dispatcher.contract_address, token_amount); + stop_cheat_caller_address(erc20_address); + + let allowance = token_dispatcher.allowance(user_address, dispatcher.contract_address); + assert(allowance >= token_amount, 'Allowance not set correctly'); +} + +/// Helper function to create a content item with a price +/// We'll use the set_content_price function implemented in the contract +pub 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); +} diff --git a/tests/lib.cairo b/tests/lib.cairo index 6da5df4..d142618 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -5,4 +5,3 @@ pub mod test_contentaccess; pub mod test_contentpost; pub mod test_permissions; pub mod test_subscription; - diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index c6ccfe1..419c56b 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -3,6 +3,7 @@ use chain_lib::base::types::{PurchaseStatus, Rank, Role, Status}; use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentType, PlanType, SubscriptionStatus}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, @@ -13,72 +14,6 @@ use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::testing::{set_caller_address, set_contract_address}; -/// 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); -} - -// Token faucet and allowance setup -fn token_faucet_and_allowance( - dispatcher: IChainLibDispatcher, - user_address: ContractAddress, - erc20_address: ContractAddress, - token_amount: u256, -) { - let admin_address = contract_address_const::<'admin'>(); - - let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; - // Transfer tokens from admin to user - start_cheat_caller_address(erc20_address, admin_address); - token_dispatcher.transfer(user_address, token_amount); - stop_cheat_caller_address(erc20_address); - - let user_token_balance = token_dispatcher.balance_of(user_address); - assert(user_token_balance >= token_amount, 'User tokens not gotten'); - - // Set user as caller to approve the contract - start_cheat_caller_address(erc20_address, user_address); - token_dispatcher.approve(dispatcher.contract_address, token_amount); - stop_cheat_caller_address(erc20_address); - - let allowance = token_dispatcher.allowance(user_address, dispatcher.contract_address); - assert(allowance >= token_amount, 'Allowance not set correctly'); -} - -fn setup() -> (ContractAddress, ContractAddress, ContractAddress) { - let admin_address: ContractAddress = contract_address_const::<'admin'>(); - - // Deploy mock ERC20 - let erc20_class = declare("mock_erc20").unwrap().contract_class(); - let mut calldata = array![admin_address.into(), admin_address.into(), 6]; - let (erc20_address, _) = erc20_class.deploy(@calldata).unwrap(); - - // Deploy the ChainLib contract - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'Contract declaration failed'); - - let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into(), erc20_address.into()]; - - let deploy_result = contract_class.deploy(@calldata); - assert(deploy_result.is_ok(), 'Contract deployment failed'); - - let (contract_address, _) = deploy_result.unwrap(); - - (contract_address, admin_address, erc20_address) -} - #[test] fn test_initial_data() { let (contract_address, admin_address, erc20_address) = setup(); @@ -621,9 +556,9 @@ fn test_get_user_purchases() { let content_id_1: felt252 = 'content1'; let content_id_2: felt252 = 'content2'; let price: u256 = 1000_u256; - + token_faucet_and_allowance(dispatcher, user_address, erc20_address, 100000); - + // Set admin as caller to prepare the contract state cheat_caller_address(contract_address, admin_address, CheatSpan::Indefinite); // Set up content with price diff --git a/tests/test_account_delegation.cairo b/tests/test_account_delegation.cairo index 54c23e8..851e50a 100644 --- a/tests/test_account_delegation.cairo +++ b/tests/test_account_delegation.cairo @@ -10,6 +10,7 @@ use chain_lib::chainlib::ChainLib::ChainLib::{ delegation_flags, }; use chain_lib::interfaces::IChainLib::{IChainLibDispatcher, IChainLibDispatcherTrait}; +use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; // use chain_lib::interfaces::IChainLib::{ // IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait // }; @@ -27,22 +28,10 @@ use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_block_timestamp, get_caller_address}; -fn setup() -> ContractAddress { - let contract_class = declare("ChainLib").unwrap().contract_class(); - - // Initial owner address - let owner: ContractAddress = contract_address_const::<'OWNER'>(); - - let calldata = array![owner.into()]; - - let (contract_address, _) = contract_class.deploy(@calldata).unwrap(); - contract_address -} - #[test] fn test_create_delegation() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -88,7 +77,7 @@ fn test_create_delegation() { #[test] #[should_panic(expected: 'Invalid delegate address')] fn test_create_delegation_zero_address() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -112,7 +101,7 @@ fn test_create_delegation_zero_address() { #[test] fn test_revoke_delegation() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -157,7 +146,7 @@ fn test_revoke_delegation() { #[test] #[should_panic(expected: 'Delegate mismatch')] fn test_revoke_delegation_wrong_delegate() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -185,7 +174,7 @@ fn test_revoke_delegation_wrong_delegate() { #[test] fn test_use_delegation() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -254,7 +243,7 @@ fn test_use_delegation() { #[test] #[should_panic(expected: 'Permission denied')] fn test_use_delegation_exceed_max_actions() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -287,7 +276,7 @@ fn test_use_delegation_exceed_max_actions() { #[test] fn test_is_delegated_expired() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -324,7 +313,7 @@ fn test_is_delegated_expired() { #[test] fn test_is_delegated_multiple_permissions() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -364,7 +353,7 @@ fn test_is_delegated_multiple_permissions() { #[test] #[should_panic(expected: 'Permission denied')] fn test_use_delegation_wrong_delegate() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -396,7 +385,7 @@ fn test_use_delegation_wrong_delegate() { #[test] fn test_delegation_unlimited() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses @@ -439,7 +428,7 @@ fn test_delegation_unlimited() { #[test] fn test_get_delegation_info() { - let contract_address = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let contract_instance = IChainLibDispatcher { contract_address }; // Setup addresses diff --git a/tests/test_contentaccess.cairo b/tests/test_contentaccess.cairo index 89297f6..7c8e4fa 100644 --- a/tests/test_contentaccess.cairo +++ b/tests/test_contentaccess.cairo @@ -9,6 +9,7 @@ mod permission_tests { use chain_lib::interfaces::IChainLib::{ IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait, }; + use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use core::array::ArrayTrait; use core::option::OptionTrait; use snforge_std::{ @@ -18,25 +19,10 @@ mod permission_tests { use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_caller_address}; - fn setup() -> (ContractAddress, ContractAddress) { - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'declare failed'); - let admin_address: ContractAddress = contract_address_const::<'admin'>(); - - let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into()]; - - let deploy_result = contract_class.deploy(@calldata); - assert(deploy_result.is_ok(), 'deploy failed'); - - let (contract_address, _) = deploy_result.unwrap(); - - (contract_address, admin_address) - } #[test] fn test_content_access_rules_workflow() { - let (contract_address, _) = setup(); + let (contract_address, _, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let content_id = 123; let user = contract_address_const::<'user'>(); @@ -89,7 +75,7 @@ mod permission_tests { #[test] fn test_verification_workflow() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let content_id = 456; let user = contract_address_const::<0x03>(); @@ -156,7 +142,7 @@ mod permission_tests { #[test] fn test_edge_cases() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let content_id = 999; let user = contract_address_const::<0x08>(); @@ -196,7 +182,7 @@ mod permission_tests { #[test] #[should_panic] fn test_unauthorized_access() { - let (contract_address, _) = setup(); + let (contract_address, _, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let content_id = 111; let non_admin = contract_address_const::<0x0a>(); @@ -209,7 +195,7 @@ mod permission_tests { #[test] fn test_multiple_verification_types() { - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, _) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; let content_id = 222; let user = contract_address_const::<0x0b>(); diff --git a/tests/test_contentpost.cairo b/tests/test_contentpost.cairo index 4ef3e75..b0337d8 100644 --- a/tests/test_contentpost.cairo +++ b/tests/test_contentpost.cairo @@ -2,6 +2,7 @@ use chain_lib::base::types::{Rank, Role}; use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentMetadata, ContentType}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpy, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, @@ -12,26 +13,10 @@ use starknet::contract_address::contract_address_const; use starknet::testing::{set_caller_address, set_contract_address}; -fn setup() -> (ContractAddress, ContractAddress) { - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'Contract declaration failed'); - let admin_address: ContractAddress = contract_address_const::<'admin'>(); - - let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into()]; - - let deploy_result = contract_class.deploy(@calldata); - assert(deploy_result.is_ok(), 'Contract deployment failed'); - - let (contract_address, _) = deploy_result.unwrap(); - - (contract_address, admin_address) -} - - #[test] fn test_register_content() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; let mut spy = spy_events(); @@ -91,7 +76,8 @@ fn test_register_content() { #[test] fn test_register_content_with_different_types() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; let mut spy = spy_events(); @@ -159,7 +145,8 @@ fn test_register_content_with_different_types() { #[test] #[should_panic] fn test_register_content_not_writer() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; let title: felt252 = 'Unauthorized Content'; @@ -188,7 +175,8 @@ fn test_register_content_not_writer() { #[test] #[should_panic] fn test_register_content_empty_title() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; // Set up content with empty title @@ -218,7 +206,8 @@ fn test_register_content_empty_title() { #[test] #[should_panic] fn test_register_content_empty_description() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; // Set up content with empty description @@ -247,7 +236,8 @@ fn test_register_content_empty_description() { #[test] fn test_register_content_multiple_users() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; let mut spy = spy_events(); @@ -337,7 +327,8 @@ fn test_register_content_multiple_users() { #[test] fn test_content_metadata_retrieval() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; // Create a user with WRITER role @@ -418,7 +409,8 @@ fn test_content_metadata_retrieval() { #[test] fn test_register_multiple_content_items() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; let mut spy = spy_events(); @@ -526,7 +518,8 @@ fn test_register_multiple_content_items() { #[test] #[should_panic] fn test_get_non_existent_content() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; // Create a user with WRITER role diff --git a/tests/test_permissions.cairo b/tests/test_permissions.cairo index b431746..ed35d3e 100644 --- a/tests/test_permissions.cairo +++ b/tests/test_permissions.cairo @@ -5,6 +5,7 @@ mod permission_tests { use chain_lib::interfaces::IChainLib::{ IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait, }; + use chain_lib::utils::test_utils::setup; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, }; @@ -12,25 +13,9 @@ mod permission_tests { use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_caller_address}; - fn setup() -> (ContractAddress, ContractAddress) { - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'declare failed'); - let admin_address: ContractAddress = contract_address_const::<'admin'>(); - - let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into()]; - - let deploy_result = contract_class.deploy(@calldata); - assert(deploy_result.is_ok(), 'deploy failed'); - - let (contract_address, _) = deploy_result.unwrap(); - - (contract_address, admin_address) - } - #[test] fn test_token_account_owner_permissions() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -50,7 +35,7 @@ mod permission_tests { #[test] fn test_set_and_get_operator_permissions() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Test input values @@ -106,7 +91,7 @@ mod permission_tests { #[test] fn test_manage_operators_permission() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create a token account @@ -139,7 +124,7 @@ mod permission_tests { #[test] fn test_modify_account_permissions() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create token account @@ -170,7 +155,7 @@ mod permission_tests { #[test] fn test_multiple_operators() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create token account @@ -247,7 +232,7 @@ mod permission_tests { #[test] fn test_permission_combinations() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create token account @@ -293,7 +278,7 @@ mod permission_tests { #[test] #[should_panic(expected: 'No permission')] fn test_unauthorized_set_operator() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create token account @@ -318,7 +303,7 @@ mod permission_tests { #[test] #[should_panic(expected: 'No permission')] fn test_insufficient_permissions() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Create token account @@ -348,7 +333,7 @@ mod permission_tests { #[test] fn test_nonexistent_account() { - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); let dispatcher = IChainLibDispatcher { contract_address }; // Attempt to get permissions for a non-existent account diff --git a/tests/test_subscription.cairo b/tests/test_subscription.cairo index afc6325..b0ffcbe 100644 --- a/tests/test_subscription.cairo +++ b/tests/test_subscription.cairo @@ -3,6 +3,7 @@ use chain_lib::chainlib::ChainLib::ChainLib::{ Event, PaymentProcessed, PaymentVerified, RecurringPaymentProcessed, RefundProcessed, }; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, @@ -11,24 +12,6 @@ use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::testing::{set_caller_address, set_contract_address}; use starknet::{ContractAddress, get_block_timestamp}; - - -fn setup() -> (ContractAddress, ContractAddress) { - let declare_result = declare("ChainLib"); - assert(declare_result.is_ok(), 'Contract declaration failed'); - let admin_address: ContractAddress = contract_address_const::<'admin'>(); - - let contract_class = declare_result.unwrap().contract_class(); - let mut calldata = array![admin_address.into()]; - - let deploy_result = contract_class.deploy(@calldata); - assert(deploy_result.is_ok(), 'Contract deployment failed'); - - let (contract_address, _) = deploy_result.unwrap(); - - (contract_address, admin_address) -} - // Helper function to create a token-bound account for testing fn create_test_account(dispatcher: IChainLibDispatcher) -> (u256, ContractAddress) { // Test input values for token-bound account @@ -50,7 +33,7 @@ fn create_test_account(dispatcher: IChainLibDispatcher) -> (u256, ContractAddres #[test] fn test_initial_payment() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -59,6 +42,10 @@ fn test_initial_payment() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Set the caller to the subscriber for the entire test cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -81,7 +68,7 @@ fn test_initial_payment() { #[should_panic(expected: 'Only subscriber can call')] fn test_initial_payment_invalid_subscriber() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create subscription dispatcher let subscription_dispatcher = IChainLibDispatcher { contract_address }; @@ -99,7 +86,7 @@ fn test_initial_payment_invalid_subscriber() { #[should_panic(expected: 'Only subscriber can call')] fn test_initial_payment_unauthorized() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -121,7 +108,7 @@ fn test_initial_payment_unauthorized() { #[test] fn test_token_bound_account_creation() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatcher let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -151,7 +138,7 @@ fn test_token_bound_account_creation() { #[test] fn test_initial_payment_event() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -203,7 +190,7 @@ fn test_initial_payment_event() { #[test] fn test_process_recurring_payment() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -212,6 +199,10 @@ fn test_process_recurring_payment() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Set the caller to the subscriber for the entire test cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -251,7 +242,7 @@ fn test_process_recurring_payment() { #[should_panic(expected: 'Payment not due yet')] fn test_process_recurring_payment_not_due() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -260,6 +251,10 @@ fn test_process_recurring_payment_not_due() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Set the caller to the subscriber for the entire test cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -289,7 +284,7 @@ fn test_process_recurring_payment_not_due() { #[should_panic(expected: 'Subscription not found')] fn test_process_recurring_payment_not_found() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let subscription_dispatcher = IChainLibDispatcher { contract_address }; @@ -310,7 +305,7 @@ fn test_process_recurring_payment_not_found() { #[test] fn test_process_recurring_payment_event() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -324,6 +319,10 @@ fn test_process_recurring_payment_event() { // Set the caller to the subscriber for the entire test cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Create a token-bound account let user_name: felt252 = 'Mark'; let init_param1: felt252 = 'Mark@yahoo.com'; @@ -375,7 +374,7 @@ fn test_process_recurring_payment_event() { #[should_panic(expected: 'Only admin can verify payments')] fn test_verify_payment_admin_only() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -386,6 +385,9 @@ fn test_verify_payment_admin_only() { // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Create a token-bound account let user_name: felt252 = 'Mark'; @@ -416,7 +418,7 @@ fn test_verify_payment_admin_only() { #[should_panic(expected: 'Payment not found')] fn test_verify_payment_not_found() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let _chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -438,7 +440,7 @@ fn test_verify_payment_not_found() { #[should_panic(expected: 'Payment already verified')] fn test_verify_payment_success() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -447,6 +449,9 @@ fn test_verify_payment_success() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -478,7 +483,7 @@ fn test_verify_payment_success() { #[test] fn test_verify_payment_event() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -490,6 +495,10 @@ fn test_verify_payment_event() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -531,7 +540,7 @@ fn test_verify_payment_event() { #[should_panic(expected: 'Only admin can process refunds')] fn test_process_refund_admin_only() { // Setup the contract - let (contract_address, _) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -558,7 +567,10 @@ fn test_process_refund_admin_only() { // Since this is the first subscription, its ID is 0 let subscription_id: u256 = 0; - + + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Try to process a refund as a non-admin (should panic) // We're still using the subscriber address as the caller subscription_dispatcher.process_refund(subscription_id); @@ -572,7 +584,7 @@ fn test_process_refund_admin_only() { #[should_panic(expected: 'Subscription not found')] fn test_process_refund_subscription_not_found() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let _chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -593,7 +605,7 @@ fn test_process_refund_subscription_not_found() { #[test] fn test_process_refund_success() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -601,6 +613,9 @@ fn test_process_refund_success() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -636,7 +651,7 @@ fn test_process_refund_success() { #[should_panic(expected: 'Subscription not active')] fn test_process_refund_already_refunded() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -644,7 +659,9 @@ fn test_process_refund_already_refunded() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); - + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -685,7 +702,7 @@ fn test_process_refund_already_refunded() { #[test] fn test_process_refund_event() { // Setup the contract - let (contract_address, admin_address) = setup(); + let (contract_address, admin_address, erc20_address) = setup(); // Create dispatchers for both interfaces let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; @@ -703,6 +720,10 @@ fn test_process_refund_event() { let init_param2: felt252 = 'Mark is a boy'; chain_lib_dispatcher.create_token_account(user_name, init_param1, init_param2); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Process an initial payment (caller is already set to subscriber) let amount: u256 = 100000000000000000; // 0.1 STRK in wei let result = subscription_dispatcher.process_initial_payment(amount, subscriber_address); From 3e54fd47c5e168b123f6f60252790b980827f720 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sun, 29 Jun 2025 21:05:08 +0100 Subject: [PATCH 14/15] feat: reorganize test utilities and remove unused test_utils module --- src/lib.cairo | 3 --- tests/lib.cairo | 2 ++ tests/test_ChainLib.cairo | 2 +- tests/test_account_delegation.cairo | 2 +- tests/test_contentaccess.cairo | 2 +- tests/test_contentpost.cairo | 2 +- tests/test_permissions.cairo | 2 +- tests/test_subscription.cairo | 15 +++++++++------ {src/utils => tests}/test_utils.cairo | 0 9 files changed, 16 insertions(+), 14 deletions(-) rename {src/utils => tests}/test_utils.cairo (100%) diff --git a/src/lib.cairo b/src/lib.cairo index 32a53ed..cb802eb 100644 --- a/src/lib.cairo +++ b/src/lib.cairo @@ -13,6 +13,3 @@ pub mod presets { pub mod mock_erc20; } -pub mod utils { - pub mod test_utils; -} diff --git a/tests/lib.cairo b/tests/lib.cairo index d142618..dc89ff1 100644 --- a/tests/lib.cairo +++ b/tests/lib.cairo @@ -5,3 +5,5 @@ pub mod test_contentaccess; pub mod test_contentpost; pub mod test_permissions; pub mod test_subscription; + +pub mod test_utils; diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 419c56b..6ce32b5 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -3,7 +3,6 @@ use chain_lib::base::types::{PurchaseStatus, Rank, Role, Status}; use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentType, PlanType, SubscriptionStatus}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; -use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, @@ -13,6 +12,7 @@ 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 crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; #[test] fn test_initial_data() { diff --git a/tests/test_account_delegation.cairo b/tests/test_account_delegation.cairo index 851e50a..30b17e3 100644 --- a/tests/test_account_delegation.cairo +++ b/tests/test_account_delegation.cairo @@ -10,7 +10,6 @@ use chain_lib::chainlib::ChainLib::ChainLib::{ delegation_flags, }; use chain_lib::interfaces::IChainLib::{IChainLibDispatcher, IChainLibDispatcherTrait}; -use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; // use chain_lib::interfaces::IChainLib::{ // IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait // }; @@ -27,6 +26,7 @@ use snforge_std::{ use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_block_timestamp, get_caller_address}; +use crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; #[test] diff --git a/tests/test_contentaccess.cairo b/tests/test_contentaccess.cairo index 7c8e4fa..9a76038 100644 --- a/tests/test_contentaccess.cairo +++ b/tests/test_contentaccess.cairo @@ -9,7 +9,6 @@ mod permission_tests { use chain_lib::interfaces::IChainLib::{ IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait, }; - use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use core::array::ArrayTrait; use core::option::OptionTrait; use snforge_std::{ @@ -18,6 +17,7 @@ mod permission_tests { use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_caller_address}; + use crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; #[test] diff --git a/tests/test_contentpost.cairo b/tests/test_contentpost.cairo index b0337d8..dcb555c 100644 --- a/tests/test_contentpost.cairo +++ b/tests/test_contentpost.cairo @@ -2,7 +2,6 @@ use chain_lib::base::types::{Rank, Role}; use chain_lib::chainlib::ChainLib; use chain_lib::chainlib::ChainLib::ChainLib::{Category, ContentMetadata, ContentType}; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; -use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpy, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, @@ -11,6 +10,7 @@ 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 crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; #[test] diff --git a/tests/test_permissions.cairo b/tests/test_permissions.cairo index ed35d3e..735faaf 100644 --- a/tests/test_permissions.cairo +++ b/tests/test_permissions.cairo @@ -5,13 +5,13 @@ mod permission_tests { use chain_lib::interfaces::IChainLib::{ IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait, }; - use chain_lib::utils::test_utils::setup; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, cheat_caller_address, declare, }; use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::{ContractAddress, get_caller_address}; + use crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; #[test] fn test_token_account_owner_permissions() { diff --git a/tests/test_subscription.cairo b/tests/test_subscription.cairo index b0ffcbe..20bdb28 100644 --- a/tests/test_subscription.cairo +++ b/tests/test_subscription.cairo @@ -3,7 +3,6 @@ use chain_lib::chainlib::ChainLib::ChainLib::{ Event, PaymentProcessed, PaymentVerified, RecurringPaymentProcessed, RefundProcessed, }; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; -use chain_lib::utils::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, cheat_caller_address, declare, spy_events, @@ -12,6 +11,7 @@ use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; use starknet::testing::{set_caller_address, set_contract_address}; use starknet::{ContractAddress, get_block_timestamp}; +use crate::test_utils::{setup, setup_content_with_price, token_faucet_and_allowance}; // Helper function to create a token-bound account for testing fn create_test_account(dispatcher: IChainLibDispatcher) -> (u256, ContractAddress) { // Test input values for token-bound account @@ -148,7 +148,9 @@ fn test_initial_payment_event() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); - + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); // Set the caller to the subscriber for the entire test cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -549,6 +551,10 @@ fn test_process_refund_admin_only() { // Create a specific subscriber address and use it consistently let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + token_faucet_and_allowance( + chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, + ); + // Set the caller to the subscriber for creating a subscription cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); @@ -567,10 +573,7 @@ fn test_process_refund_admin_only() { // Since this is the first subscription, its ID is 0 let subscription_id: u256 = 0; - - token_faucet_and_allowance( - chain_lib_dispatcher, subscriber_address, erc20_address, 1000000000000000000, - ); + // Try to process a refund as a non-admin (should panic) // We're still using the subscriber address as the caller subscription_dispatcher.process_refund(subscription_id); diff --git a/src/utils/test_utils.cairo b/tests/test_utils.cairo similarity index 100% rename from src/utils/test_utils.cairo rename to tests/test_utils.cairo From a6f81a502ab737687a59b428fbdb44218e433148 Mon Sep 17 00:00:00 2001 From: Akshola00 Date: Sun, 29 Jun 2025 22:00:40 +0100 Subject: [PATCH 15/15] feat: add tests for purchase content with insufficient balance and allowance --- tests/test_ChainLib.cairo | 87 +++++++++++++++++++ tests/test_subscription.cairo | 155 +++++++++++++++++++++++++++++++++- 2 files changed, 241 insertions(+), 1 deletion(-) diff --git a/tests/test_ChainLib.cairo b/tests/test_ChainLib.cairo index 6ce32b5..067a439 100644 --- a/tests/test_ChainLib.cairo +++ b/tests/test_ChainLib.cairo @@ -528,6 +528,93 @@ fn test_purchase_content() { assert(purchase.transaction_hash == transaction_hash, 'Transaction hash mismatch'); } + +#[test] +#[should_panic(expected: 'Insufficient token balance')] +fn test_purchase_content_should_fail_if_insufficient_balance() { + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(user_address, 10); + stop_cheat_caller_address(erc20_address); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, user_address); + token_dispatcher.approve(dispatcher.contract_address, 10000); + stop_cheat_caller_address(erc20_address); + + // 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: 'Insufficient token allowance')] +fn test_purchase_content_should_fail_if_insufficient_allowance() { + let (contract_address, admin_address, erc20_address) = setup(); + let dispatcher = IChainLibDispatcher { contract_address }; + let user_address = contract_address_const::<'user'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(user_address, 10000); + stop_cheat_caller_address(erc20_address); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, user_address); + token_dispatcher.approve(dispatcher.contract_address, 100); + stop_cheat_caller_address(erc20_address); + + // 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() { diff --git a/tests/test_subscription.cairo b/tests/test_subscription.cairo index 20bdb28..26d9bd2 100644 --- a/tests/test_subscription.cairo +++ b/tests/test_subscription.cairo @@ -3,9 +3,11 @@ use chain_lib::chainlib::ChainLib::ChainLib::{ Event, PaymentProcessed, PaymentVerified, RecurringPaymentProcessed, RefundProcessed, }; use chain_lib::interfaces::IChainLib::{IChainLib, IChainLibDispatcher, IChainLibDispatcherTrait}; +use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait}; use snforge_std::{ CheatSpan, ContractClassTrait, DeclareResultTrait, EventSpyAssertionsTrait, - cheat_caller_address, declare, spy_events, + cheat_caller_address, declare, spy_events, start_cheat_caller_address, + stop_cheat_caller_address, }; use starknet::class_hash::ClassHash; use starknet::contract_address::contract_address_const; @@ -63,6 +65,86 @@ fn test_initial_payment() { assert(result == true, 'Initial payment failed'); } + +#[test] +#[should_panic(expected: 'Insufficient token allowance')] +fn test_initial_payment_should_panic_if_no_allowance() { + // Setup the contract + let (contract_address, admin_address, erc20_address) = setup(); + + // Create dispatchers for both interfaces + let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; + let subscription_dispatcher = IChainLibDispatcher { contract_address }; + + // Create a specific subscriber address and use it consistently + let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(subscriber_address, 100000000000000000); + stop_cheat_caller_address(erc20_address); + + // Set the caller to the subscriber for the entire test + cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + + // Create a token-bound account + let user_name: felt252 = 'Mark'; + let init_param1: felt252 = 'Mark@yahoo.com'; + let init_param2: felt252 = 'Mark is a boy'; + chain_lib_dispatcher.create_token_account(user_name, init_param1, init_param2); + + // Process an initial payment (caller is already set to subscriber) + let amount: u256 = 100000000000000000; // 0.1 STRK in wei + let result = subscription_dispatcher.process_initial_payment(amount, subscriber_address); + + // Verify the payment was processed successfully + assert(result == true, 'Initial payment failed'); +} + + +#[test] +#[should_panic(expected: 'Insufficient token balance')] +fn test_initial_payment_should_panic_if_insufficient_balance() { + // Setup the contract + let (contract_address, admin_address, erc20_address) = setup(); + + // Create dispatchers for both interfaces + let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; + let subscription_dispatcher = IChainLibDispatcher { contract_address }; + + // Create a specific subscriber address and use it consistently + let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(subscriber_address, 1000000000); + stop_cheat_caller_address(erc20_address); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, subscriber_address); + token_dispatcher.approve(subscription_dispatcher.contract_address, 100000000000000000); + stop_cheat_caller_address(erc20_address); + + // Set the caller to the subscriber for the entire test + cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + + // Create a token-bound account + let user_name: felt252 = 'Mark'; + let init_param1: felt252 = 'Mark@yahoo.com'; + let init_param2: felt252 = 'Mark is a boy'; + chain_lib_dispatcher.create_token_account(user_name, init_param1, init_param2); + + // Process an initial payment (caller is already set to subscriber) + let amount: u256 = 100000000000000000; // 0.1 STRK in wei + let result = subscription_dispatcher.process_initial_payment(amount, subscriber_address); + + // Verify the payment was processed successfully + assert(result == true, 'Initial payment failed'); +} + + // Test that the initial payment fails if the caller is not the subscriber #[test] #[should_panic(expected: 'Only subscriber can call')] @@ -238,6 +320,77 @@ fn test_process_recurring_payment() { assert(recurring_result == true, 'Recurring payment failed'); } +#[test] +#[should_panic(expected: 'Insufficient token allowance')] +fn test_process_recurring_payment_should_panic_if_insufficient_allowance() { + // Setup the contract + let (contract_address, admin_address, erc20_address) = setup(); + + // Create dispatchers for both interfaces + let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; + let subscription_dispatcher = IChainLibDispatcher { contract_address }; + + // Create a specific subscriber address and use it consistently + let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(subscriber_address, 1000000000); + stop_cheat_caller_address(erc20_address); + + // Set the caller to the subscriber for the entire test + cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + + // Create a token-bound account + let user_name: felt252 = 'Mark'; + let init_param1: felt252 = 'Mark@yahoo.com'; + let init_param2: felt252 = 'Mark is a boy'; + chain_lib_dispatcher.create_token_account(user_name, init_param1, init_param2); + + // Process an initial payment (caller is already set to subscriber) + let amount: u256 = 100000000000000000; // 0.1 STRK in wei + let result = subscription_dispatcher.process_initial_payment(amount, subscriber_address); +} + + +#[test] +#[should_panic(expected: 'Insufficient token balance')] +fn test_process_recurring_payment_should_panic_if_insufficient_balance() { + // Setup the contract + let (contract_address, admin_address, erc20_address) = setup(); + + // Create dispatchers for both interfaces + let chain_lib_dispatcher = IChainLibDispatcher { contract_address }; + let subscription_dispatcher = IChainLibDispatcher { contract_address }; + + // Create a specific subscriber address and use it consistently + let subscriber_address: ContractAddress = contract_address_const::<'subscriber'>(); + + let token_dispatcher = IERC20Dispatcher { contract_address: erc20_address }; + // Transfer tokens from admin to user + start_cheat_caller_address(erc20_address, admin_address); + token_dispatcher.transfer(subscriber_address, 1000000000); + stop_cheat_caller_address(erc20_address); + + // Set user as caller to approve the contract + start_cheat_caller_address(erc20_address, subscriber_address); + token_dispatcher.approve(subscription_dispatcher.contract_address, 100000000000000000); + stop_cheat_caller_address(erc20_address); + + // Set the caller to the subscriber for the entire test + cheat_caller_address(contract_address, subscriber_address, CheatSpan::Indefinite); + + // Create a token-bound account + let user_name: felt252 = 'Mark'; + let init_param1: felt252 = 'Mark@yahoo.com'; + let init_param2: felt252 = 'Mark is a boy'; + chain_lib_dispatcher.create_token_account(user_name, init_param1, init_param2); + + // Process an initial payment (caller is already set to subscriber) + let amount: u256 = 100000000000000000; // 0.1 STRK in wei + let result = subscription_dispatcher.process_initial_payment(amount, subscriber_address); +} // test should panic if payment not due yet #[test]