The factory pattern is a well known pattern in object oriented programming. It provides an abstraction on how to instantiate a class. In the case of smart contracts, we can use this pattern by defining a factory contract that has the sole responsibility of creating and managing other contracts.

Class hash and contract instance

In Starknet, there’s a separation between contract’s classes and instances. A contract class serves as a blueprint, defined by the underlying Cairo bytecode, contract’s entrypoints, ABI and Sierra program hash. The contract class is identified by a class hash. When you want to add a new class to the network, you first need to declare it. When deploying a contract, you need to specify the class hash of the contract you want to deploy. Each instance of a contract has their own storage regardless of the class hash. Using the factory pattern, we can deploy multiple instances of the same contract class and handle upgrades easily.

Minimal example

Here’s a minimal example of a factory contract that deploys the SimpleCounter contract:
#[starknet::interface]
trait IFactory<TContractState> {
    fn create_counter(ref self: TContractState) -> ContractAddress;
    fn create_counter_at(ref self: TContractState, salt: felt252) -> ContractAddress;
    fn update_counter_class_hash(ref self: TContractState, new_class_hash: ClassHash);
}

#[starknet::contract]
mod Factory {
    use starknet::{
        contract_address_const,
        class_hash_const,
        get_caller_address,
        ContractAddress,
        ClassHash,
    };

    #[storage]
    struct Storage {
        counter_class_hash: ClassHash,
    }

    #[constructor]
    fn constructor(ref self: ContractState, counter_class_hash: ClassHash) {
        self.counter_class_hash.write(counter_class_hash);
    }

    #[abi(embed_v0)]
    impl Factory of super::IFactory<ContractState> {
        fn create_counter(ref self: ContractState) -> ContractAddress {
            let salt = starknet::contract_address_const::<0x123>();
            self.create_counter_at(salt)
        }

        fn create_counter_at(ref self: ContractState, salt: felt252) -> ContractAddress {
            let counter_class_hash = self.counter_class_hash.read();
            let constructor_calldata = array![];
            
            let (contract_address, _) = starknet::deploy_contract_syscall(
                counter_class_hash,
                constructor_calldata.span(),
                salt,
                false,
            ).unwrap();
            
            contract_address
        }

        fn update_counter_class_hash(ref self: ContractState, new_class_hash: ClassHash) {
            self.counter_class_hash.write(new_class_hash);
        }
    }
}
This factory can be used to deploy multiple instances of the SimpleCounter contract by calling the create_counter and create_counter_at functions. The SimpleCounter class hash is stored inside the factory, and can be upgraded with the update_counter_class_hash function which allows to reuse the same factory contract when the SimpleCounter contract is upgraded.
This minimal example lacks several useful features such as access control, tracking of deployed contracts, events etc.