Simple Counter

We now understand how to create a contract with state variables and functions. Let's create a simple counter contract that increments and decrements a counter.

Here's how it works:

  • The contract has a state variable counter that is initialized to 0.

  • When a user calls the increment entrypoint, the contract increments counter by 1.

  • When a user calls the decrement, the contract decrements counter by 1.

#[starknet::interface]
pub trait ISimpleCounter<TContractState> {
    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<ContractState> {
        fn get_current_count(self: @ContractState) -> u128 {
            return self.counter.read();
        }

        fn increment(ref self: ContractState) {
            // Store counter value + 1
            let counter = self.counter.read() + 1;
            self.counter.write(counter);
        }

        fn decrement(ref self: ContractState) {
            // Store counter value - 1
            let counter = self.counter.read() - 1;
            self.counter.write(counter);
        }
    }
}

#[cfg(test)]
mod test {
    use super::{SimpleCounter, ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait};
    use starknet::{ContractAddress, SyscallResultTrait, syscalls::deploy_syscall};

    fn deploy(init_value: u128) -> ISimpleCounterDispatcher {
        let (contract_address, _) = deploy_syscall(
            SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(),
            0,
            array![init_value.into()].span(),
            false
        )
            .unwrap_syscall();
        ISimpleCounterDispatcher { contract_address }
    }

    #[test]
    fn should_deploy() {
        let init_value = 10;
        let contract = deploy(init_value);

        let read_value = contract.get_current_count();
        assert_eq!(read_value, init_value);
    }

    #[test]
    fn should_increment() {
        let init_value = 10;
        let contract = deploy(init_value);

        contract.increment();
        assert_eq!(contract.get_current_count(), init_value + 1);

        contract.increment();
        contract.increment();
        assert_eq!(contract.get_current_count(), init_value + 3);
    }

    #[test]
    fn should_decrement() {
        let init_value = 10;
        let contract = deploy(init_value);

        contract.decrement();
        assert_eq!(contract.get_current_count(), init_value - 1);

        contract.decrement();
        contract.decrement();
        assert_eq!(contract.get_current_count(), init_value - 3);
    }

    #[test]
    fn should_increment_and_decrement() {
        let init_value = 10;
        let contract = deploy(init_value);

        contract.increment();
        contract.decrement();
        assert_eq!(contract.get_current_count(), init_value);

        contract.decrement();
        contract.increment();
        assert_eq!(contract.get_current_count(), init_value);
    }
}
Last change: 2024-04-12, commit: be52b01