diff --git a/src/contracts/factory.cairo b/src/contracts/factory.cairo index 94b12f8..c5bb322 100644 --- a/src/contracts/factory.cairo +++ b/src/contracts/factory.cairo @@ -1,3 +1,15 @@ +/// ## A Starknet contract that serves as a factory for deploying organizations. +/// +/// This factory contract handles the creation of entire organizational structures, +/// each comprising a `Core` contract and a `Vault` contract. +/// +/// This contract is responsible for: +/// - Deploying new `Vault` and `Core` contracts using predefined class hashes. +/// - Linking each `Core` contract to its corresponding `Vault`. +/// - Maintaining a registry of all deployed organizations and their owners. +/// - Storing updatable class hashes to allow for future upgrades of the `Core` and `Vault` logic. +/// - Acting as a central lookup service for cross-organization data, such as memberships and +/// invitations. #[starknet::contract] pub mod Factory { use littlefinger::interfaces::ifactory::IFactory; @@ -21,19 +33,31 @@ pub mod Factory { component!(path: OwnableComponent, storage: ownable, event: OwnableEvent); component!(path: UpgradeableComponent, storage: upgradeable, event: UpgradeableEvent); + /// Defines the storage layout for the `Factory` contract. #[storage] pub struct Storage { + /// Maps an organization's ID to its deployed `Core` contract address. deployed_orgs: Map, //org_id should be the same with vault_id + /// Maps a vault's ID to its deployed `Vault` contract address. deployed_vaults: Map, + /// Maps an owner's address to a list of their associated (Core, Vault) contract pairs. vault_org_pairs: Map>, + /// Maps a member's address to a list of organizations they belong to. member_of: Map>, + /// Maps an invitee's address to their invitation details and the inviting organization. org_invites: Map, + /// Counter for the total number of deployed organizations. orgs_count: u64, + /// Counter for the total number of deployed vaults. vaults_count: u64, //Open to the possibility of an organization somehow having more than one vault + /// The current `ClassHash` for deploying new `Vault` contracts. vault_class_hash: ClassHash, + /// The current `ClassHash` for deploying new `Core` contracts. org_core_class_hash: ClassHash, + /// Substorage for the Ownable component. #[substorage(v0)] ownable: OwnableComponent::Storage, + /// Substorage for the Upgradeable component. #[substorage(v0)] upgradeable: UpgradeableComponent::Storage, } @@ -50,29 +74,42 @@ pub mod Factory { // const VAULT_CLASS_HASH: felt252 = // 0x017195343b9bf99c3933a7a998bcba8244d14a95ec35d26afbbfa6bbe4cded8d; + /// Events emitted by the `Factory` contract. #[event] #[derive(Drop, starknet::Event)] pub enum Event { + /// Emits ownable-related events. #[flat] OwnableEvent: OwnableComponent::Event, + /// Emits upgradeable-related events. #[flat] UpgradeableEvent: UpgradeableComponent::Event, + /// Emitted when a new vault is deployed. VaultDeployed: VaultDeployed, + /// Emitted when a new core organization contract is deployed. OrgCoreDeployed: OrgCoreDeployed, } + /// Event data for a vault deployment. #[derive(Drop, starknet::Event)] pub struct VaultDeployed { - address: ContractAddress, - deployed_at: u64, + pub address: ContractAddress, + pub deployed_at: u64, } + /// Event data for a core organization contract deployment. #[derive(Drop, starknet::Event)] pub struct OrgCoreDeployed { - address: ContractAddress, - deployed_at: u64, + pub address: ContractAddress, + pub deployed_at: u64, } + /// Initializes the Factory contract. + /// + /// ### Parameters + /// - `owner`: The address that will have ownership of the factory. + /// - `org_core_class_hash`: The initial class hash for the `Core` contract. + /// - `vault_class_hash`: The initial class hash for the `Vault` contract. #[constructor] fn constructor( ref self: ContractState, @@ -91,6 +128,13 @@ pub mod Factory { #[abi(embed_v0)] impl UpgradeableImpl of IUpgradeable { + /// Upgrades the factory contract to a new class hash. + /// + /// ### Parameters + /// - `new_class_hash`: The class hash of the new contract implementation. + /// + /// ### Panics + /// - If the caller is not the owner. fn upgrade(ref self: ContractState, new_class_hash: ClassHash) { // This might be upgraded from the factory self.ownable.assert_only_owner(); @@ -98,8 +142,24 @@ pub mod Factory { } } + /// # FactoryImpl + /// + /// Public-facing implementation of the `IFactory` interface. #[abi(embed_v0)] pub impl FactoryImpl of IFactory { + /// Deploys a new organization, consisting of a Vault and a Core contract. + /// + /// ### Parameters + /// - `token`: The ERC20 token address for the vault. + /// - `salt`: A unique value for deterministic deployment. + /// - `owner`: The owner of the new organization. + /// - `name`, `ipfs_url`: Metadata for the organization. + /// - `first_admin_fname`, `first_admin_lname`, `first_admin_alias`: Details for the initial + /// admin. + /// - `organization_type`: A numerical identifier for the organization type. + /// + /// ### Returns + /// - A tuple `(ContractAddress, ContractAddress)` with the new Core and Vault addresses. fn setup_org( ref self: ContractState, token: ContractAddress, @@ -140,6 +200,10 @@ pub mod Factory { (org_core_address, vault_address) } + /// Returns a list of all deployed vault addresses. + /// + /// ### Returns + /// - `Array`: A list of vault contract addresses. fn get_deployed_vaults(self: @ContractState) -> Array { let mut vaults = array![]; let vaults_count: u256 = (self.vaults_count.read()).try_into().unwrap(); @@ -150,6 +214,11 @@ pub mod Factory { } vaults } + + /// Returns a list of all deployed core organization addresses. + /// + /// ### Returns + /// - `Array`: A list of core contract addresses. fn get_deployed_org_cores(self: @ContractState) -> Array { let mut orgs = array![]; let orgs_count: u256 = (self.orgs_count.read()).try_into().unwrap(); @@ -161,16 +230,37 @@ pub mod Factory { orgs } + /// Updates the class hash for new Vault deployments. + /// + /// ### Parameters + /// - `vault_hash`: The new `ClassHash` for the Vault contract. + /// + /// ### Panics + /// - If the caller is not the owner. fn update_vault_hash(ref self: ContractState, vault_hash: ClassHash) { self.ownable.assert_only_owner(); self.vault_class_hash.write(vault_hash); } + /// Updates the class hash for new Core deployments. + /// + /// ### Parameters + /// - `core_hash`: The new `ClassHash` for the Core contract. + /// + /// ### Panics + /// - If the caller is not the owner. fn update_core_hash(ref self: ContractState, core_hash: ClassHash) { self.ownable.assert_only_owner(); self.org_core_class_hash.write(core_hash); } + /// Returns all (Core, Vault) pairs for a given owner. + /// + /// ### Parameters + /// - `caller`: The address of the owner. + /// + /// ### Returns + /// - `Array<(ContractAddress, ContractAddress)>`: A list of associated contract pairs. fn get_vault_org_pairs( self: @ContractState, caller: ContractAddress, ) -> Array<(ContractAddress, ContractAddress)> { @@ -187,6 +277,13 @@ pub mod Factory { vault_org_pairs } + /// Returns all organizations a given address is a member of. + /// + /// ### Parameters + /// - `caller`: The address of the member. + /// + /// ### Returns + /// - `Array`: A list of Core contracts the user is a member of. fn get_member_orgs( self: @ContractState, caller: ContractAddress, ) -> Array { @@ -203,12 +300,23 @@ pub mod Factory { orgs } + /// Adds a record indicating that a user is a member of an organization. + /// + /// ### Parameters + /// - `member`: The address of the new member. + /// - `org_core`: The address of the organization they joined. fn update_member_of( ref self: ContractState, member: ContractAddress, org_core: ContractAddress, ) { self.member_of.entry(member).push(org_core); } + /// Stores invitation details in the central factory registry. + /// + /// ### Parameters + /// - `invitee`: The address of the user being invited. + /// - `invite_details`: The details of the invitation. + /// - `core_org`: The address of the inviting organization. fn create_invite( ref self: ContractState, invitee: ContractAddress, @@ -218,6 +326,10 @@ pub mod Factory { self.org_invites.entry(invitee).write((core_org, invite_details)); } + /// Updates an invitation's status to `ACCEPTED` in the factory registry. + /// + /// ### Parameters + /// - `invitee`: The address of the user who accepted the invitation. fn accpet_invite(ref self: ContractState, invitee: ContractAddress) { let (core_org, mut invite_details) = self.org_invites.entry(invitee).read(); @@ -226,6 +338,13 @@ pub mod Factory { self.org_invites.entry(invitee).write((core_org, invite_details)); } + /// Retrieves the details for a specific invitation. + /// + /// ### Parameters + /// - `invitee`: The address of the user whose invitation is being requested. + /// + /// ### Returns + /// - `MemberInvite`: A struct containing the invitation details. fn get_invite_details(self: @ContractState, invitee: ContractAddress) -> MemberInvite { let (_, invite_details) = self.org_invites.entry(invitee).read(); @@ -233,8 +352,20 @@ pub mod Factory { } } + /// # InternalImpl + /// + /// Internal functions for contract deployment, not exposed in the public ABI. #[generate_trait] pub impl InternalImpl of InternalTrait { + /// Deploys a new Vault contract. + /// + /// ### Parameters + /// - `token`: The ERC20 token the vault will manage. + /// - `salt`: A unique value for deterministic deployment. + /// - `owner`: The owner of the new vault. + /// + /// ### Returns + /// - `ContractAddress`: The address of the newly deployed vault. fn deploy_vault( ref self: ContractState, // class_hash: felt252, //unwrap it into class has using into, and it will be removed // once I declare the vault @@ -273,6 +404,20 @@ pub mod Factory { // Initialize member // If custom owner is not supplied at deployment, deployer is used as owner, and becomes the // first admin + /// Deploys a new Core organization contract. + /// + /// ### Parameters + /// - `owner`: The owner of the new organization. + /// - `name`, `ipfs_url`: Metadata for the organization. + /// - `vault_address`: The address of the associated vault. + /// - `first_admin_fname`, `first_admin_lname`, `first_admin_alias`: Details for the initial + /// admin. + /// - `salt`: A unique value for deterministic deployment. + /// - `organization_type`: A numerical identifier for the organization type. + /// - `factory`: The address of this factory contract. + /// + /// ### Returns + /// - `ContractAddress`: The address of the newly deployed Core contract. fn deploy_org_core( ref self: ContractState, // class_hash: felt252, diff --git a/src/interfaces/ifactory.cairo b/src/interfaces/ifactory.cairo index ee217db..688a0ce 100644 --- a/src/interfaces/ifactory.cairo +++ b/src/interfaces/ifactory.cairo @@ -1,6 +1,13 @@ use littlefinger::structs::member_structs::MemberInvite; use starknet::{ClassHash, ContractAddress}; +/// # IFactory +/// +/// This trait defines the public interface for a factory contract. The factory is responsible +/// for deploying and managing entire organizations, which consist of a `Core` contract and a +/// corresponding `Vault` contract. It simplifies the creation process, maintains a registry +/// of all deployed instances, and facilitates cross-organization interactions like member +/// invitations and lookups. #[starknet::interface] pub trait IFactory { // fn deploy_vault( @@ -28,6 +35,29 @@ pub trait IFactory { // first_admin_alias: felt252, // salt: felt252, // ) -> ContractAddress; + + /// # setup_org + /// + /// Deploys and configures a new organization, including its `Core` contract and `Vault`. + /// This is the main entry point for creating a new, fully functional organization. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `token`: The `ContractAddress` of the ERC20 token the organization's vault will manage. + /// - `salt`: A unique value (`felt252`) to ensure deterministic yet unique contract addresses. + /// - `owner`: The `ContractAddress` of the user who will own the new organization. + /// - `name`: The name of the organization. + /// - `ipfs_url`: A URL (e.g., on IPFS) pointing to organization metadata. + /// - `first_admin_fname`: First name of the initial administrator (the owner). + /// - `first_admin_lname`: Last name of the initial administrator. + /// - `first_admin_alias`: Alias of the initial administrator. + /// - `organization_type`: A numerical identifier for the type of organization. + /// + /// ## Returns + /// + /// A tuple `(ContractAddress, ContractAddress)` containing the addresses of the newly + /// deployed `Core` contract and `Vault` contract, respectively. fn setup_org( ref self: T, // class_hash: felt252, //unwrap it into class has using into, and it will be removed once I @@ -47,29 +77,140 @@ pub trait IFactory { organization_type: u8, // salt: felt252, ) -> (ContractAddress, ContractAddress); + + /// # get_deployed_vaults + /// + /// Retrieves a list of all `Vault` contracts deployed by this factory. + /// + /// ## Parameters + /// + /// - `self: @T`: A snapshot of the contract's state. + /// + /// ## Returns + /// + /// An `Array` of all deployed vault addresses. fn get_deployed_vaults(self: @T) -> Array; + + /// # get_deployed_org_cores + /// + /// Retrieves a list of all `Core` contracts deployed by this factory. + /// + /// ## Parameters + /// + /// - `self: @T`: A snapshot of the contract's state. + /// + /// ## Returns + /// + /// An `Array` of all deployed core organization addresses. fn get_deployed_org_cores(self: @T) -> Array; + + /// # get_vault_org_pairs + /// + /// For a given owner, retrieves all associated pairs of (Core, Vault) contracts. + /// + /// ## Parameters + /// + /// - `self: @T`: A snapshot of the contract's state. + /// - `caller`: The address of the owner. + /// + /// ## Returns + /// + /// An `Array<(ContractAddress, ContractAddress)>` of associated contract pairs. fn get_vault_org_pairs( self: @T, caller: ContractAddress, ) -> Array<(ContractAddress, ContractAddress)>; + + /// # get_member_orgs + /// + /// For a given user, retrieves all organizations they are a member of. + /// + /// ## Parameters + /// + /// - `self: @T`: A snapshot of the contract's state. + /// - `caller`: The address of the member. + /// + /// ## Returns + /// + /// An `Array` of `Core` contracts the user is a member of. fn get_member_orgs(self: @T, caller: ContractAddress) -> Array; + + /// # update_vault_hash + /// + /// Updates the class hash for the `Vault` contract. New deployments will use this updated hash. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `vault_hash`: The new `ClassHash` for the vault contract. fn update_vault_hash(ref self: T, vault_hash: ClassHash); + + /// # update_core_hash + /// + /// Updates the class hash for the `Core` contract. New deployments will use this updated hash. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `core_hash`: The new `ClassHash` for the core contract. fn update_core_hash(ref self: T, core_hash: ClassHash); + + /// # update_member_of + /// + /// Records a user's membership in a specific organization. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `member`: The `ContractAddress` of the user who became a member. + /// - `org_core`: The `ContractAddress` of the organization they joined. fn update_member_of(ref self: T, member: ContractAddress, org_core: ContractAddress); + + /// # create_invite + /// + /// Stores the details of an invitation sent from an organization to a user. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `invitee`: The `ContractAddress` of the user being invited. + /// - `invite_details`: A `MemberInvite` struct containing the invitation details. + /// - `core_org`: The `ContractAddress` of the inviting organization. fn create_invite( ref self: T, invitee: ContractAddress, invite_details: MemberInvite, core_org: ContractAddress, ); + + /// # accpet_invite + /// + /// Marks an existing invitation as accepted in the factory's records. + /// + /// ## Parameters + /// + /// - `ref self: T`: The current state of the contract. + /// - `invitee`: The `ContractAddress` of the user who accepted the invite. fn accpet_invite(ref self: T, invitee: ContractAddress); + + /// # get_invite_details + /// + /// Retrieves the stored details of an invitation for a specific user. + /// + /// ## Parameters + /// + /// - `self: @T`: A snapshot of the contract's state. + /// - `invitee`: The `ContractAddress` of the user whose invitation is being queried. + /// + /// ## Returns + /// + /// A `MemberInvite` struct with the invitation details. fn get_invite_details(self: @T, invitee: ContractAddress) -> MemberInvite; // fn get_vault_org_pairs(self: @T) -> Array<(ContractAddress, ContractAddress)>; // in the future, you can upgrade a deployed org core from here -// fn initialize_upgrade(ref self: T, vaults: Array, cores: -// Array); -// this function would pick the updated class hash from the storage, if the class hash has been -// updated at present, it can only pick the latest... -// in the future, it can pick a specific class hash version + // fn initialize_upgrade(ref self: T, vaults: Array, cores: + // Array); + // this function would pick the updated class hash from the storage, if the class hash has been + // updated at present, it can only pick the latest... + // in the future, it can pick a specific class hash version }