diff --git a/listings/getting-started/calling_other_contracts/src/callee.cairo b/listings/getting-started/calling_other_contracts/src/callee.cairo deleted file mode 100644 index 523135fc..00000000 --- a/listings/getting-started/calling_other_contracts/src/callee.cairo +++ /dev/null @@ -1,23 +0,0 @@ -#[starknet::interface] -pub trait ICallee { - fn set_value(ref self: TContractState, value: u128) -> u128; -} - -#[starknet::contract] -pub mod Callee { - use starknet::ContractAddress; - - #[storage] - struct Storage { - value: u128, - names: LegacyMap::, - } - - #[abi(embed_v0)] - impl ICalleeImpl of super::ICallee { - fn set_value(ref self: ContractState, value: u128) -> u128 { - self.value.write(value); - value - } - } -} diff --git a/listings/getting-started/calling_other_contracts/src/caller.cairo b/listings/getting-started/calling_other_contracts/src/caller.cairo index 1433cd86..35ac8381 100644 --- a/listings/getting-started/calling_other_contracts/src/caller.cairo +++ b/listings/getting-started/calling_other_contracts/src/caller.cairo @@ -1,20 +1,39 @@ -use starknet::ContractAddress; - -// We need to have the interface of the callee contract defined -// so that we can import the Dispatcher. +// ANCHOR: callee_contract +// This will automatically generate ICalleeDispatcher and ICalleeDispatcherTrait #[starknet::interface] pub trait ICallee { fn set_value(ref self: TContractState, value: u128) -> u128; } +#[starknet::contract] +pub mod Callee { + #[storage] + struct Storage { + value: u128, + } + + #[abi(embed_v0)] + impl ICalleeImpl of super::ICallee { + fn set_value(ref self: ContractState, value: u128) -> u128 { + self.value.write(value); + value + } + } +} +// ANCHOR_END: callee_contract + #[starknet::interface] pub trait ICaller { - fn set_value_from_address(ref self: TContractState, addr: ContractAddress, value: u128); + fn set_value_from_address( + ref self: TContractState, addr: starknet::ContractAddress, value: u128 + ); } +// ANCHOR: caller_contract #[starknet::contract] pub mod Caller { - // We import the Dispatcher of the called contract + // We need to import the dispatcher of the callee contract + // If you don't have a proper import, you can redefine the interface by yourself use super::{ICalleeDispatcher, ICalleeDispatcherTrait}; use starknet::ContractAddress; @@ -28,3 +47,46 @@ pub mod Caller { } } } +// ANCHOR_END: caller_contract + +#[cfg(test)] +mod tests { + use super::{ + Callee, ICalleeDispatcher, ICalleeDispatcherTrait, Callee::valueContractMemberStateTrait, + Caller, ICallerDispatcher, ICallerDispatcherTrait + }; + use starknet::{ + ContractAddress, contract_address_const, testing::set_contract_address, + syscalls::deploy_syscall, SyscallResultTrait + }; + + fn deploy() -> (ICalleeDispatcher, ICallerDispatcher) { + let (address_callee, _) = deploy_syscall( + Callee::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false + ) + .unwrap_syscall(); + let (address_caller, _) = deploy_syscall( + Caller::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false + ) + .unwrap_syscall(); + ( + ICalleeDispatcher { contract_address: address_callee }, + ICallerDispatcher { contract_address: address_caller } + ) + } + + #[test] + fn test_caller() { + let init_value: u128 = 42; + + let (callee, caller) = deploy(); + caller.set_value_from_address(callee.contract_address, init_value); + + let state = Callee::contract_state_for_testing(); + set_contract_address(callee.contract_address); + + let value_read: u128 = state.value.read(); + + assert_eq!(value_read, init_value); + } +} diff --git a/listings/getting-started/calling_other_contracts/src/lib.cairo b/listings/getting-started/calling_other_contracts/src/lib.cairo index 7675fe68..49b21d4d 100644 --- a/listings/getting-started/calling_other_contracts/src/lib.cairo +++ b/listings/getting-started/calling_other_contracts/src/lib.cairo @@ -1,5 +1 @@ -mod callee; mod caller; - -#[cfg(test)] -mod tests; diff --git a/listings/getting-started/calling_other_contracts/src/tests.cairo b/listings/getting-started/calling_other_contracts/src/tests.cairo deleted file mode 100644 index 75b157e8..00000000 --- a/listings/getting-started/calling_other_contracts/src/tests.cairo +++ /dev/null @@ -1,43 +0,0 @@ -mod tests { - use calling_other_contracts::callee::{ - Callee, ICalleeDispatcher, ICalleeDispatcherTrait, Callee::valueContractMemberStateTrait - }; - use calling_other_contracts::caller::{Caller, ICallerDispatcher, ICallerDispatcherTrait}; - use starknet::{ContractAddress, contract_address_const}; - use starknet::testing::{set_contract_address}; - use starknet::syscalls::deploy_syscall; - use starknet::SyscallResultTrait; - - - fn deploy() -> (ICalleeDispatcher, ICallerDispatcher) { - let calldata: Span = array![].span(); - let (address_callee, _) = deploy_syscall( - Callee::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata, false - ) - .unwrap_syscall(); - let (address_caller, _) = deploy_syscall( - Caller::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false - ) - .unwrap_syscall(); - ( - ICalleeDispatcher { contract_address: address_callee }, - ICallerDispatcher { contract_address: address_caller } - ) - } - - #[test] - #[available_gas(2000000000)] - fn test_caller() { - let init_value: u128 = 42; - - let (callee, caller) = deploy(); - caller.set_value_from_address(callee.contract_address, init_value); - - let state = Callee::contract_state_for_testing(); - set_contract_address(callee.contract_address); - - let value_read: u128 = state.value.read(); - - assert(value_read == init_value, 'Wrong value read'); - } -} diff --git a/listings/getting-started/events/src/counter.cairo b/listings/getting-started/events/src/counter.cairo index fa4a0b39..346e47b5 100644 --- a/listings/getting-started/events/src/counter.cairo +++ b/listings/getting-started/events/src/counter.cairo @@ -1,9 +1,9 @@ -// ANCHOR: contract #[starknet::interface] pub trait IEventCounter { fn increment(ref self: TContractState, amount: u128); } +// ANCHOR: contract #[starknet::contract] pub mod EventCounter { use starknet::{get_caller_address, ContractAddress}; @@ -94,17 +94,17 @@ mod tests { assert_eq!(state.counter.read(), amount); // Notice the order: the first event emitted is the first to be popped. - /// ANCHOR: test_event + /// ANCHOR: test_events assert_eq!( starknet::testing::pop_log(contract_address), Option::Some(Event::CounterIncreased(CounterIncreased { amount })) ); + // ANCHOR_END: test_events assert_eq!( starknet::testing::pop_log(contract_address), Option::Some( Event::UserIncreaseCounter(UserIncreaseCounter { user: caller, new_value: amount }) ) ); - // ANCHOR_END: test_events } } diff --git a/listings/getting-started/factory/src/lib.cairo b/listings/getting-started/factory/src/lib.cairo index effb7e79..05d376bc 100644 --- a/listings/getting-started/factory/src/lib.cairo +++ b/listings/getting-started/factory/src/lib.cairo @@ -1,5 +1 @@ mod simple_factory; -mod target; - -#[cfg(test)] -mod tests; diff --git a/listings/getting-started/factory/src/simple_factory.cairo b/listings/getting-started/factory/src/simple_factory.cairo index fb4670b5..83e6f22f 100644 --- a/listings/getting-started/factory/src/simple_factory.cairo +++ b/listings/getting-started/factory/src/simple_factory.cairo @@ -18,8 +18,7 @@ pub trait ICounterFactory { #[starknet::contract] pub mod CounterFactory { - use starknet::{ContractAddress, ClassHash, SyscallResultTrait}; - use starknet::syscalls::deploy_syscall; + use starknet::{ContractAddress, ClassHash, SyscallResultTrait, syscalls::deploy_syscall}; #[storage] struct Storage { @@ -67,4 +66,125 @@ pub mod CounterFactory { } // ANCHOR_END: contract +#[cfg(test)] +mod tests { + use super::{CounterFactory, ICounterFactoryDispatcher, ICounterFactoryDispatcherTrait}; + use starknet::{ + SyscallResultTrait, ContractAddress, ClassHash, contract_address_const, + syscalls::deploy_syscall + }; + // Define a target contract to deploy + mod target { + #[starknet::interface] + pub trait ISimpleCounter { + fn get_current_count(self: @TContractState) -> u128; + fn increment(ref self: TContractState); + fn decrement(ref self: TContractState); + } + + #[starknet::contract] + pub mod SimpleCounter { + #[storage] + struct Storage { + // Counter variable + counter: u128, + } + + #[constructor] + fn constructor(ref self: ContractState, init_value: u128) { + // Store initial value + self.counter.write(init_value); + } + + #[abi(embed_v0)] + impl SimpleCounter of super::ISimpleCounter { + fn get_current_count(self: @ContractState) -> u128 { + self.counter.read() + } + + fn increment(ref self: ContractState) { + // Store counter value + 1 + let mut counter: u128 = self.counter.read() + 1; + self.counter.write(counter); + } + fn decrement(ref self: ContractState) { + // Store counter value - 1 + let mut counter: u128 = self.counter.read() - 1; + self.counter.write(counter); + } + } + } + } + use target::{ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait}; + + /// Deploy a counter factory contract + fn deploy_factory( + counter_class_hash: ClassHash, init_value: u128 + ) -> ICounterFactoryDispatcher { + let mut constructor_calldata: Array:: = array![ + init_value.into(), counter_class_hash.into() + ]; + + let (contract_address, _) = deploy_syscall( + CounterFactory::TEST_CLASS_HASH.try_into().unwrap(), + 0, + constructor_calldata.span(), + false + ) + .unwrap_syscall(); + + ICounterFactoryDispatcher { contract_address } + } + + #[test] + fn test_deploy_counter_constructor() { + let init_value = 10; + + let counter_class_hash: ClassHash = target::SimpleCounter::TEST_CLASS_HASH + .try_into() + .unwrap(); + let factory = deploy_factory(counter_class_hash, init_value); + + let counter_address = factory.create_counter(); + let counter = target::ISimpleCounterDispatcher { contract_address: counter_address }; + + assert_eq!(counter.get_current_count(), init_value); + } + + #[test] + fn test_deploy_counter_argument() { + let init_value = 10; + let argument_value = 20; + + let counter_class_hash: ClassHash = target::SimpleCounter::TEST_CLASS_HASH + .try_into() + .unwrap(); + let factory = deploy_factory(counter_class_hash, init_value); + + let counter_address = factory.create_counter_at(argument_value); + let counter = target::ISimpleCounterDispatcher { contract_address: counter_address }; + + assert_eq!(counter.get_current_count(), argument_value); + } + + #[test] + fn test_deploy_multiple() { + let init_value = 10; + let argument_value = 20; + + let counter_class_hash: ClassHash = target::SimpleCounter::TEST_CLASS_HASH + .try_into() + .unwrap(); + let factory = deploy_factory(counter_class_hash, init_value); + + let mut counter_address = factory.create_counter(); + let counter_1 = target::ISimpleCounterDispatcher { contract_address: counter_address }; + + counter_address = factory.create_counter_at(argument_value); + let counter_2 = target::ISimpleCounterDispatcher { contract_address: counter_address }; + + assert_eq!(counter_1.get_current_count(), init_value); + assert_eq!(counter_2.get_current_count(), argument_value); + } +} diff --git a/listings/getting-started/factory/src/target.cairo b/listings/getting-started/factory/src/target.cairo deleted file mode 100644 index 0f139272..00000000 --- a/listings/getting-started/factory/src/target.cairo +++ /dev/null @@ -1,39 +0,0 @@ -#[starknet::interface] -pub trait ISimpleCounter { - fn get_current_count(self: @TContractState) -> u128; - fn increment(ref self: TContractState); - fn decrement(ref self: TContractState); -} - -#[starknet::contract] -pub mod SimpleCounter { - #[storage] - struct Storage { - // Counter variable - counter: u128, - } - - #[constructor] - fn constructor(ref self: ContractState, init_value: u128) { - // Store initial value - self.counter.write(init_value); - } - - #[abi(embed_v0)] - impl SimpleCounter of super::ISimpleCounter { - fn get_current_count(self: @ContractState) -> u128 { - self.counter.read() - } - - fn increment(ref self: ContractState) { - // Store counter value + 1 - let mut counter: u128 = self.counter.read() + 1; - self.counter.write(counter); - } - fn decrement(ref self: ContractState) { - // Store counter value - 1 - let mut counter: u128 = self.counter.read() - 1; - self.counter.write(counter); - } - } -} diff --git a/listings/getting-started/factory/src/tests.cairo b/listings/getting-started/factory/src/tests.cairo deleted file mode 100644 index e0c633b9..00000000 --- a/listings/getting-started/factory/src/tests.cairo +++ /dev/null @@ -1,80 +0,0 @@ -mod tests { - use starknet::SyscallResultTrait; - use starknet::{ContractAddress, ClassHash, contract_address_const}; - use starknet::syscalls::deploy_syscall; - - use factory::simple_factory::{ - CounterFactory, ICounterFactoryDispatcher, ICounterFactoryDispatcherTrait - }; - use factory::target::{SimpleCounter, ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait}; - - /// Deploy a counter factory contract - fn deploy_factory( - counter_class_hash: ClassHash, init_value: u128 - ) -> ICounterFactoryDispatcher { - // let caller_address: ContractAddress = contract_address_const::<'caller'>(); - // let deployed_contract_address = contract_address_const::<'market_factory'>(); - - let mut constructor_calldata: Array:: = array![ - init_value.into(), counter_class_hash.into() - ]; - - let (factory_address, _) = deploy_syscall( - CounterFactory::TEST_CLASS_HASH.try_into().unwrap(), - 0, - constructor_calldata.span(), - false - ) - .unwrap_syscall(); - - ICounterFactoryDispatcher { contract_address: factory_address } - } - - #[test] - #[available_gas(20000000)] - fn test_deploy_counter_constructor() { - let init_value = 10; - - let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); - let factory = deploy_factory(counter_class_hash, init_value); - - let counter_address = factory.create_counter(); - let counter = ISimpleCounterDispatcher { contract_address: counter_address }; - - assert(counter.get_current_count() == init_value, 'Incorrect initial value'); - } - - #[test] - #[available_gas(20000000)] - fn test_deploy_counter_argument() { - let init_value = 10; - let argument_value = 20; - - let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); - let factory = deploy_factory(counter_class_hash, init_value); - - let counter_address = factory.create_counter_at(argument_value); - let counter = ISimpleCounterDispatcher { contract_address: counter_address }; - - assert(counter.get_current_count() == argument_value, 'Incorrect argument value'); - } - - #[test] - #[available_gas(20000000)] - fn test_deploy_multiple() { - let init_value = 10; - let argument_value = 20; - - let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); - let factory = deploy_factory(counter_class_hash, init_value); - - let mut counter_address = factory.create_counter(); - let counter_1 = ISimpleCounterDispatcher { contract_address: counter_address }; - - counter_address = factory.create_counter_at(argument_value); - let counter_2 = ISimpleCounterDispatcher { contract_address: counter_address }; - - assert(counter_1.get_current_count() == init_value, 'Incorrect initial value'); - assert(counter_2.get_current_count() == argument_value, 'Incorrect argument value'); - } -} diff --git a/listings/getting-started/interfaces_traits/src/explicit.cairo b/listings/getting-started/interfaces_traits/src/explicit.cairo index 3fb1f809..d31bd219 100644 --- a/listings/getting-started/interfaces_traits/src/explicit.cairo +++ b/listings/getting-started/interfaces_traits/src/explicit.cairo @@ -1,3 +1,4 @@ +// ANCHOR: contract #[starknet::interface] pub trait IExplicitInterfaceContract { fn get_value(self: @TContractState) -> u32; @@ -22,3 +23,32 @@ pub mod ExplicitInterfaceContract { } } } +// ANCHOR_END: contract + +#[cfg(test)] +mod tests { + use super::{ + IExplicitInterfaceContract, ExplicitInterfaceContract, IExplicitInterfaceContractDispatcher, + IExplicitInterfaceContractDispatcherTrait + }; + use starknet::{ContractAddress, SyscallResultTrait, syscalls::deploy_syscall}; + + #[test] + fn test_interface() { + let (contract_address, _) = deploy_syscall( + ExplicitInterfaceContract::TEST_CLASS_HASH.try_into().unwrap(), + 0, + array![].span(), + false + ) + .unwrap_syscall(); + let mut contract = IExplicitInterfaceContractDispatcher { contract_address }; + + let value: u32 = 20; + contract.set_value(value); + + let read_value = contract.get_value(); + + assert_eq!(read_value, value); + } +} diff --git a/listings/getting-started/interfaces_traits/src/implicit.cairo b/listings/getting-started/interfaces_traits/src/implicit.cairo index bca71f9e..47e8e5f3 100644 --- a/listings/getting-started/interfaces_traits/src/implicit.cairo +++ b/listings/getting-started/interfaces_traits/src/implicit.cairo @@ -1,3 +1,4 @@ +// ANCHOR: contract #[starknet::contract] pub mod ImplicitInterfaceContract { #[storage] @@ -7,7 +8,7 @@ pub mod ImplicitInterfaceContract { #[abi(per_item)] #[generate_trait] - impl ImplicitInterfaceContract of IImplicitInterfaceContract { + pub impl ImplicitInterfaceContract of IImplicitInterfaceContract { #[external(v0)] fn get_value(self: @ContractState) -> u32 { self.value.read() @@ -19,3 +20,34 @@ pub mod ImplicitInterfaceContract { } } } +// ANCHOR_END: contract + +#[cfg(test)] +mod tests { + use super::{ + ImplicitInterfaceContract, ImplicitInterfaceContract::valueContractMemberStateTrait, + ImplicitInterfaceContract::IImplicitInterfaceContract + }; + use starknet::{ + ContractAddress, SyscallResultTrait, syscalls::deploy_syscall, testing::set_contract_address + }; + + #[test] + fn test_interface() { + let (contract_address, _) = deploy_syscall( + ImplicitInterfaceContract::TEST_CLASS_HASH.try_into().unwrap(), + 0, + array![].span(), + false + ) + .unwrap_syscall(); + set_contract_address(contract_address); + let mut state = ImplicitInterfaceContract::contract_state_for_testing(); + + let value = 42; + state.set_value(value); + let read_value = state.get_value(); + + assert_eq!(read_value, value); + } +} diff --git a/listings/getting-started/interfaces_traits/src/implicit_internal.cairo b/listings/getting-started/interfaces_traits/src/implicit_internal.cairo index 6350e863..a10cf8f6 100644 --- a/listings/getting-started/interfaces_traits/src/implicit_internal.cairo +++ b/listings/getting-started/interfaces_traits/src/implicit_internal.cairo @@ -1,3 +1,4 @@ +// ANCHOR: contract #[starknet::interface] pub trait IImplicitInternalContract { fn add(ref self: TContractState, nb: u32); @@ -17,6 +18,7 @@ pub mod ImplicitInternalContract { fn set_value(ref self: ContractState, value: u32) { self.value.write(value); } + fn get_const() -> u32 { 42 } @@ -32,11 +34,41 @@ pub mod ImplicitInternalContract { fn add(ref self: ContractState, nb: u32) { self.set_value(self.value.read() + nb); } + fn get_value(self: @ContractState) -> u32 { self.value.read() } + fn get_const(self: @ContractState) -> u32 { self.get_const() } } } +// ANCHOR_END: contract + +#[cfg(test)] +mod tests { + use super::{ + IImplicitInternalContract, ImplicitInternalContract, IImplicitInternalContractDispatcher, + IImplicitInternalContractDispatcherTrait + }; + use starknet::{ContractAddress, SyscallResultTrait, syscalls::deploy_syscall}; + + #[test] + fn test_interface() { + // Set up. + let (contract_address, _) = deploy_syscall( + ImplicitInternalContract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false + ) + .unwrap_syscall(); + let mut contract = IImplicitInternalContractDispatcher { contract_address }; + + let initial_value: u32 = 0; + assert_eq!(contract.get_value(), initial_value); + + let add_value: u32 = 10; + contract.add(add_value); + + assert_eq!(contract.get_value(), initial_value + add_value); + } +} diff --git a/listings/getting-started/interfaces_traits/src/lib.cairo b/listings/getting-started/interfaces_traits/src/lib.cairo index 73b9608f..500b0482 100644 --- a/listings/getting-started/interfaces_traits/src/lib.cairo +++ b/listings/getting-started/interfaces_traits/src/lib.cairo @@ -1,6 +1,3 @@ mod explicit; mod implicit_internal; mod implicit; - -#[cfg(test)] -mod tests; diff --git a/listings/getting-started/interfaces_traits/src/tests.cairo b/listings/getting-started/interfaces_traits/src/tests.cairo deleted file mode 100644 index 840fce3a..00000000 --- a/listings/getting-started/interfaces_traits/src/tests.cairo +++ /dev/null @@ -1,63 +0,0 @@ -mod tests { // TODO -} - -mod explicit_interface_contract_tests { - use interfaces_traits::explicit::{ - IExplicitInterfaceContract, ExplicitInterfaceContract, IExplicitInterfaceContractDispatcher, - IExplicitInterfaceContractDispatcherTrait - }; - use starknet::ContractAddress; - use starknet::SyscallResultTrait; - use starknet::syscalls::deploy_syscall; - - #[test] - #[available_gas(2000000000)] - fn test_interface() { - let (contract_address, _) = deploy_syscall( - ExplicitInterfaceContract::TEST_CLASS_HASH.try_into().unwrap(), - 0, - array![].span(), - false - ) - .unwrap_syscall(); - - let mut contract = IExplicitInterfaceContractDispatcher { contract_address }; - - let value: u32 = 20; - contract.set_value(value); - - let read_value = contract.get_value(); - - assert(read_value == value, 'wrong value'); - } -} - -mod implicit_internal_contract_tests { - use interfaces_traits::implicit_internal::{ - IImplicitInternalContract, ImplicitInternalContract, IImplicitInternalContractDispatcher, - IImplicitInternalContractDispatcherTrait - }; - use starknet::ContractAddress; - use starknet::SyscallResultTrait; - use starknet::syscalls::deploy_syscall; - - #[test] - #[available_gas(2000000000)] - fn test_interface() { - // Set up. - let (contract_address, _) = deploy_syscall( - ImplicitInternalContract::TEST_CLASS_HASH.try_into().unwrap(), 0, array![].span(), false - ) - .unwrap_syscall(); - - let mut contract = IImplicitInternalContractDispatcher { contract_address }; - - let initial_value: u32 = 0; - assert(contract.get_value() == initial_value, 'wrong value'); - - let add_value: u32 = 10; - contract.add(add_value); - - assert(contract.get_value() == initial_value + add_value, 'wrong value after add'); - } -} diff --git a/listings/getting-started/testing_how_to/src/contract.cairo b/listings/getting-started/testing_how_to/src/contract.cairo index 85dd454a..0ef9f56e 100644 --- a/listings/getting-started/testing_how_to/src/contract.cairo +++ b/listings/getting-started/testing_how_to/src/contract.cairo @@ -1,9 +1,8 @@ -use starknet::ContractAddress; - +// ANCHOR: contract #[starknet::interface] pub trait ISimpleContract { fn get_value(self: @TContractState) -> u32; - fn get_owner(self: @TContractState) -> ContractAddress; + fn get_owner(self: @TContractState) -> starknet::ContractAddress; fn set_value(ref self: TContractState, value: u32); } @@ -18,13 +17,13 @@ pub mod SimpleContract { } #[constructor] - fn constructor(ref self: ContractState, initial_value: u32) { + pub fn constructor(ref self: ContractState, initial_value: u32) { self.value.write(initial_value); self.owner.write(get_caller_address()); } #[abi(embed_v0)] - impl SimpleContract of super::ISimpleContract { + pub impl SimpleContractImpl of super::ISimpleContract { fn get_value(self: @ContractState) -> u32 { self.value.read() } @@ -39,3 +38,193 @@ pub mod SimpleContract { } } } +// ANCHOR_END: contract + +// ANCHOR: tests +#[cfg(test)] +mod tests { + // Import the interface and dispatcher to be able to interact with the contract. + use super::{SimpleContract, ISimpleContractDispatcher, ISimpleContractDispatcherTrait}; + + // Import the deploy syscall to be able to deploy the contract. + use starknet::{SyscallResultTrait, syscalls::deploy_syscall}; + use starknet::{ + ContractAddress, get_caller_address, get_contract_address, contract_address_const + }; + + // Use starknet test utils to fake the contract_address + use starknet::testing::set_contract_address; + + // Deploy the contract and return its dispatcher. + fn deploy(initial_value: u32) -> ISimpleContractDispatcher { + // Declare and deploy + let (contract_address, _) = deploy_syscall( + SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), + 0, + array![initial_value.into()].span(), + false + ) + .unwrap_syscall(); + + // Return the dispatcher. + // The dispatcher allows to interact with the contract based on its interface. + ISimpleContractDispatcher { contract_address } + } + + #[test] + fn test_deploy() { + let initial_value: u32 = 10; + let contract = deploy(initial_value); + + assert_eq!(contract.get_value(), initial_value); + assert_eq!(contract.get_owner(), get_contract_address()); + } + + #[test] + fn test_set_as_owner() { + // Fake the contract address to owner + let owner = contract_address_const::<'owner'>(); + set_contract_address(owner); + + // When deploying the contract, the owner is the caller. + let contract = deploy(10); + assert_eq!(contract.get_owner(), owner); + + // As the current caller is the owner, the value can be set. + let new_value: u32 = 20; + contract.set_value(new_value); + + assert_eq!(contract.get_value(), new_value); + } + + #[test] + #[should_panic] + fn test_set_not_owner() { + let owner = contract_address_const::<'owner'>(); + set_contract_address(owner); + let contract = deploy(10); + + // Fake the contract address to another address + let not_owner = contract_address_const::<'not owner'>(); + set_contract_address(not_owner); + + // As the current caller is not the owner, the value cannot be set. + let new_value: u32 = 20; + contract.set_value(new_value); + // Panic expected + } + + #[test] + #[available_gas(150000)] + fn test_deploy_gas() { + deploy(10); + } +} +// ANCHOR_END: tests + +// ANCHOR: tests_with_state +#[cfg(test)] +mod tests_with_states { + // Only import the contract + use super::SimpleContract; + + // For accessing storage variables and entrypoints, + // we must import the contract member state traits and implementation. + use SimpleContract::{ + SimpleContractImpl, valueContractMemberStateTrait, ownerContractMemberStateTrait + }; + + use starknet::contract_address_const; + use starknet::testing::set_caller_address; + use core::num::traits::Zero; + + #[test] + fn test_standalone_state() { + let mut state = SimpleContract::contract_state_for_testing(); + + // As no contract was deployed, the constructor was not called on the state + // - with valueContractMemberStateTrait + assert_eq!(state.value.read(), 0); + // - with SimpleContractImpl + assert_eq!(state.get_value(), 0); + assert_eq!(state.owner.read(), Zero::zero()); + + // We can still directly call the constructor to initialize the state. + let owner = contract_address_const::<'owner'>(); + // We are not setting the contract address but the caller address here, + // as we are not deploying the contract but directly calling the constructor function. + set_caller_address(owner); + + let initial_value: u32 = 10; + SimpleContract::constructor(ref state, initial_value); + assert_eq!(state.get_value(), initial_value); + assert_eq!(state.get_owner(), owner); + + // As the current caller is the owner, the value can be set. + let new_value: u32 = 20; + state.set_value(new_value); + assert_eq!(state.get_value(), new_value); + } + + // But we can also deploy the contract and interact with it using the dispatcher + // as shown in the previous tests, and still use the state for testing. + use super::{ISimpleContractDispatcher, ISimpleContractDispatcherTrait}; + use starknet::{ + ContractAddress, SyscallResultTrait, syscalls::deploy_syscall, testing::set_contract_address + }; + + #[test] + fn test_state_with_contract() { + let owner = contract_address_const::<'owner'>(); + let not_owner = contract_address_const::<'not owner'>(); + + // Deploy as owner + let initial_value: u32 = 10; + set_contract_address(owner); + let (contract_address, _) = deploy_syscall( + SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), + 0, + array![initial_value.into()].span(), + false + ) + .unwrap_syscall(); + let mut contract = ISimpleContractDispatcher { contract_address }; + + // create the state + // - Set back as not owner + set_contract_address(not_owner); + let mut state = SimpleContract::contract_state_for_testing(); + // - Currently, the state is not 'linked' to the contract + assert_ne!(state.get_value(), initial_value); + assert_ne!(state.get_owner(), owner); + // - Link the state to the contract by setting the contract address + set_contract_address(contract.contract_address); + assert_eq!(state.get_value(), initial_value); + assert_eq!(state.get_owner(), owner); + + // Mutating the state from the contract change the testing state + set_contract_address(owner); + let new_value: u32 = 20; + contract.set_value(new_value); + set_contract_address(contract.contract_address); + assert_eq!(state.get_value(), new_value); + + // Mutating the state from the testing state change the contract state + set_caller_address(owner); + state.set_value(initial_value); + assert_eq!(contract.get_value(), initial_value); + + // Directly mutating the state allows to change state + // in ways that are not allowed by the contract, such as changing the owner. + let new_owner = contract_address_const::<'new owner'>(); + state.owner.write(new_owner); + assert_eq!(contract.get_owner(), new_owner); + + set_caller_address(new_owner); + state.set_value(new_value); + assert_eq!(contract.get_value(), new_value); + } +} +// ANCHOR_END: tests + + diff --git a/listings/getting-started/testing_how_to/src/lib.cairo b/listings/getting-started/testing_how_to/src/lib.cairo index 11ada17a..6ccaa47d 100644 --- a/listings/getting-started/testing_how_to/src/lib.cairo +++ b/listings/getting-started/testing_how_to/src/lib.cairo @@ -1,4 +1 @@ mod contract; - -#[cfg(test)] -mod tests; diff --git a/listings/getting-started/testing_how_to/src/tests.cairo b/listings/getting-started/testing_how_to/src/tests.cairo deleted file mode 100644 index 8beb1c3a..00000000 --- a/listings/getting-started/testing_how_to/src/tests.cairo +++ /dev/null @@ -1,78 +0,0 @@ -#[cfg(test)] -mod tests { - // Import the interface and dispatcher to be able to interact with the contract. - use testing_how_to::contract::{ - ISimpleContract, SimpleContract, ISimpleContractDispatcher, ISimpleContractDispatcherTrait - }; - - // Import the deploy syscall to be able to deploy the contract. - use starknet::{ - ContractAddress, get_caller_address, get_contract_address, contract_address_const - }; - use starknet::SyscallResultTrait; - use starknet::syscalls::deploy_syscall; - - // Use starknet test utils to fake the transaction context. - use starknet::testing::{set_caller_address, set_contract_address}; - - // Deploy the contract and return its dispatcher. - fn deploy(initial_value: u32) -> ISimpleContractDispatcher { - // Set up constructor arguments. - let mut calldata = array![]; - initial_value.serialize(ref calldata); - - // Declare and deploy - let (contract_address, _) = deploy_syscall( - SimpleContract::TEST_CLASS_HASH.try_into().unwrap(), 0, calldata.span(), false - ) - .unwrap_syscall(); - - // Return the dispatcher. - // The dispatcher allows to interact with the contract based on its interface. - ISimpleContractDispatcher { contract_address } - } - - #[test] - #[available_gas(2000000000)] - fn test_deploy() { - let initial_value: u32 = 10; - let contract = deploy(initial_value); - - assert(contract.get_value() == initial_value, 'wrong initial value'); - assert(contract.get_owner() == get_contract_address(), 'wrong owner'); - } - - #[test] - #[available_gas(2000000000)] - fn test_set_as_owner() { - // Fake the caller address to address 1 - let owner = contract_address_const::<1>(); - set_contract_address(owner); - - let contract = deploy(10); - assert(contract.get_owner() == owner, 'wrong owner'); - - // Fake the contract address to address 1 - set_contract_address(owner); - let new_value: u32 = 20; - contract.set_value(new_value); - - assert(contract.get_value() == new_value, 'wrong value'); - } - - #[test] - #[should_panic] - #[available_gas(2000000000)] - fn test_set_not_owner() { - let owner = contract_address_const::<1>(); - set_contract_address(owner); - - let contract = deploy(10); - - let not_owner = contract_address_const::<2>(); - set_contract_address(not_owner); - - let new_value: u32 = 20; - contract.set_value(new_value); - } -} diff --git a/src/ch00/interacting/calling_other_contracts.md b/src/ch00/interacting/calling_other_contracts.md index 91a499d1..62f02078 100644 --- a/src/ch00/interacting/calling_other_contracts.md +++ b/src/ch00/interacting/calling_other_contracts.md @@ -5,14 +5,18 @@ There are two different ways to call other contracts in Cairo. The easiest way to call other contracts is by using the dispatcher of the contract you want to call. You can read more about Dispatchers in the [Cairo Book](https://book.cairo-lang.org/ch99-02-02-contract-dispatcher-library-dispatcher-and-system-calls.html#contract-dispatcher) -The other way is to use the `starknet::call_contract_syscall` syscall yourself. However, this method is not recommended. +The other way is to use the `starknet::call_contract_syscall` syscall yourself. However, this method is not recommended and will not be covered in this example. In order to call other contracts using dispatchers, you will need to define the called contract's interface as a trait annotated with the `#[starknet::interface]` attribute, and then import the `IContractDispatcher` and `IContractDispatcherTrait` items in your contract. +Here's the `Callee` contract interface and implementation: + ```rust -{{#include ../../../listings/getting-started/calling_other_contracts/src/callee.cairo}} +{{#rustdoc_include ../../../listings/getting-started/calling_other_contracts/src/caller.cairo:callee_contract}} ``` +The following `Caller` contract use the `Callee` interface to call the `Callee` contract: + ```rust -{{#include ../../../listings/getting-started/calling_other_contracts/src/caller.cairo}} -``` \ No newline at end of file +{{#rustdoc_include ../../../listings/getting-started/calling_other_contracts/src/caller.cairo:caller_contract}} +``` diff --git a/src/ch00/interacting/factory.md b/src/ch00/interacting/factory.md index 91b74b7f..4d5c8baa 100644 --- a/src/ch00/interacting/factory.md +++ b/src/ch00/interacting/factory.md @@ -17,7 +17,7 @@ Using the factory pattern, we can deploy multiple instances of the same contract Here's a minimal example of a factory contract that deploy the `SimpleCounter` contract: ```rust -{{#include ../../../listings/getting-started/factory/src/simple_factory.cairo:contract}} +{{#rustdoc_include ../../../listings/getting-started/factory/src/simple_factory.cairo:contract}} ``` This factory can be used to deploy multiple instances of the `SimpleCounter` contract by calling the `create_counter` and `create_counter_at` functions. diff --git a/src/ch00/interacting/interfaces-traits.md b/src/ch00/interacting/interfaces-traits.md index a5e4d1da..f9b49cb7 100644 --- a/src/ch00/interacting/interfaces-traits.md +++ b/src/ch00/interacting/interfaces-traits.md @@ -11,18 +11,18 @@ You can use the `#[generate_trait]` attribute to implicitly generate the trait f In summary, there's two ways to handle interfaces: - Explicitly, by defining a trait annoted with `#[starknet::interface]` -- Implicitly, by using `#[generate_trait]` combined with the #[abi(per_item)]` attributes, and annotating each function inside the implementation block with the appropriate attribute. +- Implicitly, by using `#[generate_trait]` combined with the `#[abi(per_item)]` attributes, and annotating each function inside the implementation block with the appropriate attribute. ## Explicit interface ```rust -{{#include ../../../listings/getting-started/interfaces_traits/src/explicit.cairo}} +{{#rustdoc_include ../../../listings/getting-started/interfaces_traits/src/explicit.cairo:contract}} ``` ## Implicit interface ```rust -{{#include ../../../listings/getting-started/interfaces_traits/src/implicit.cairo}} +{{#rustdoc_include ../../../listings/getting-started/interfaces_traits/src/implicit.cairo:contract}} ``` > Note: You can import an implicitly generated contract interface with `use contract::{GeneratedContractInterface}`. However, the `Dispatcher` will not be generated automatically. @@ -33,5 +33,5 @@ You can also use `#[generate_trait]` for your internal functions. Since this trait is generated in the context of the contract, you can define pure functions as well (functions without the `self` parameter). ```rust -{{#include ../../../listings/getting-started/interfaces_traits/src/implicit_internal.cairo}} +{{#rustdoc_include ../../../listings/getting-started/interfaces_traits/src/implicit_internal.cairo:contract}} ``` diff --git a/src/ch00/testing/contract-testing.md b/src/ch00/testing/contract-testing.md index e0b0a3b1..5d9c3e0c 100644 --- a/src/ch00/testing/contract-testing.md +++ b/src/ch00/testing/contract-testing.md @@ -3,36 +3,78 @@ Testing plays a crucial role in software development, especially for smart contracts. In this section, we'll guide you through the basics of testing a smart contract on Starknet with `scarb`. Let's start with a simple smart contract as an example: + ```rust -{{#include ../../../listings/getting-started/testing_how_to/src/contract.cairo}} +{{#include ../../../listings/getting-started/testing_how_to/src/contract.cairo:contract}} ``` Now, take a look at the tests for this contract: + ```rust -{{#include ../../../listings/getting-started/testing_how_to/src/tests.cairo}} +{{#include ../../../listings/getting-started/testing_how_to/src/contract.cairo:tests}} ``` To define our test, we use scarb, which allows us to create a separate module guarded with `#[cfg(test)]`. This ensures that the test module is only compiled when running tests using `scarb test`. Each test is defined as a function with the `#[test]` attribute. You can also check if a test panics using the `#[should_panic]` attribute. -As we are in the context of a smart contract, it's essential to set up the gas limit. You do this by using the `#[available_gas(X)]` attribute to specify the gas limit for a test. This is also a great way to ensure that your contract's features stay under a certain gas limit! +As we are in the context of a smart contract, you can also set up the gas limit for a test by using the `#[available_gas(X)]`. This is a great way to ensure that some of your contract's features stay under a certain gas limit! > Note: The term "gas" here refers to Sierra gas, not L1 gas Now, let's move on to the testing process: + - Use the `deploy` function logic to declare and deploy your contract. - Use `assert` to verify that the contract behaves as expected in the given context. + - You can also use assertions macros: `assert_eq`, `assert_ne`, `assert_gt`, `assert_ge`, `assert_lt`, `assert_le` + +If you didn't noticed yet, every examples in this book have hidden tests, you can see them by clicking on the "Show hidden lines" (eyes icon) on the top right of code blocks. +You can also find a detailed explanation of testing in cairo in the [Cairo book - Chapter 10](https://book.cairo-lang.org/ch10-00-testing-cairo-programs.html). + +## Using the contract state + +You can use the `Contract::contract_state_for_testing` function to access the contract state. This function is only available in the test environment and allows you to mutate and read the contract state directly. + +This can be useful for testing internal functions, or specific state mutations that are not exposed to the contract's interface. You can either use it with a deployed contract or as a standalone state. + +Here is an example of how to do the same previous test using the contract state: + +```rust +{{#include ../../../listings/getting-started/testing_how_to/src/contract.cairo:tests_with_state}} +``` + +## Testing events + +In order to test events, you need to use the `starknet::pop_log` function. If the contract did not emit any events, the function will return `Option::None`. + +See the test for the [Events](../events.md) section: + +```rust +{{#rustdoc_include ../../../listings/getting-started/events/src/counter.cairo:test_events}} +``` + +## Starknet Corelib Testing Module To make testing more convenient, the `testing` module of the corelib provides some helpful functions: + - `set_caller_address(address: ContractAddress)` - `set_contract_address(address: ContractAddress)` - `set_block_number(block_number: u64)` - `set_block_timestamp(block_timestamp: u64)` - `set_account_contract_address(address: ContractAddress)` +- `set_sequencer_address(address: ContractAddress)` +- `set_version(version: felt252)` +- `set_transaction_hash(hash: felt252)` +- `set_chain_id(chain_id: felt252)` +- `set_nonce(nonce: felt252)` +- `set_signature(signature: felt252)` - `set_max_fee(fee: u128)` +- `pop_log_raw(address: ContractAddress) -> Option<(Span, Span)>` +- `pop_log>(address: ContractAddress) -> Option` +- `pop_l2_to_l1_message(address: ContractAddress) -> Option<(felt252, Span)>` You may also need the `info` module from the corelib, which allows you to access information about the current execution context (see [syscalls](../basics/syscalls.md)): + - `get_caller_address() -> ContractAddress` - `get_contract_address() -> ContractAddress` - `get_block_info() -> Box` @@ -41,14 +83,9 @@ You may also need the `info` module from the corelib, which allows you to access - `get_block_number() -> u64` You can found the full list of functions in the [Starknet Corelib repo](https://github.com/starkware-libs/cairo/tree/main/corelib/src/starknet). -You can also find a detailed explanation of testing in cairo in the [Cairo book - Chapter 9](https://book.cairo-lang.org/ch09-01-how-to-write-tests.html). - - ## Starknet Foundry - - Starknet Foundry is a powerful toolkit for developing smart contracts on Starknet. It offers support for testing Starknet smart contracts on top of `scarb` with the `Forge` tool. Testing with `snforge` is similar to the process we just described but simplified. Moreover, additional features are on the way, including cheatcodes or parallel tests execution. We highly recommend exploring Starknet Foundry and incorporating it into your projects.