From a533789a2d32df426ee4fb43a60ada91561f804c Mon Sep 17 00:00:00 2001 From: Dhara Pandya Date: Thu, 28 Aug 2025 23:10:35 +0530 Subject: [PATCH 1/2] feat: Implement granular permission manager --- src/components/organization.cairo | 62 ++++++++-- src/components/permission_manager.cairo | 143 ++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 src/components/permission_manager.cairo diff --git a/src/components/organization.cairo b/src/components/organization.cairo index e203406..eed01af 100644 --- a/src/components/organization.cairo +++ b/src/components/organization.cairo @@ -16,7 +16,13 @@ pub mod OrganizationComponent { OrganizationConfig, OrganizationConfigNode, OrganizationInfo, OrganizationType, }; use super::super::member_manager::MemberManagerComponent; - + use core::array::{ArrayTrait, SpanTrait}; + use super::super::permission_manager::PermissionManagerComponent; + use super::super::permission_manager::PermissionManagerComponent::Role; + use super::super::permission_manager::PermissionManagerComponent::PermissionInternalTrait; + use core::option::OptionTrait; + use core::array::array; + use core::traits::Into; /// Defines the storage layout for the `OrganizationComponent`. #[storage] @@ -26,9 +32,9 @@ pub mod OrganizationComponent { /// Maps a committee member's address to their power level or rank. pub commitee: Map, // address -> level of power /// The configuration node for the organization. - pub config: OrganizationConfigNode, // refactor to OrganizationConfig + pub config: Mutable, // refactor to OrganizationConfig /// Struct containing the core information of the organization. - pub org_info: OrganizationInfo, + pub org_info: Mutable, } /// Events emitted by the `OrganizationComponent`. @@ -45,6 +51,7 @@ pub mod OrganizationComponent { +HasComponent, +Drop, impl Member: MemberManagerComponent::HasComponent, + impl Permission: PermissionManagerComponent::HasComponent, > of IOrganization> { /// Transfers the claim of the organization to a new address. /// @@ -55,7 +62,16 @@ pub mod OrganizationComponent { /// - `to`: The address of the new owner. fn transfer_organization_claim( ref self: ComponentState, to: ContractAddress, - ) {} + ) { + // only OWNER can transfer Ownership + let caller = get_caller_address(); + let is_owner = self.permission_manager.internal._has_permission(caller, Permission::GRANT_ADMIN); + assert(is_owner, 'Org: not owner'); + + let mut org_info=self.org_info.read(); + org_info.owner=to; + self.org_info.write(org_info); + } /// Adjusts the organization's committee by adding or removing members. /// @@ -68,9 +84,34 @@ pub mod OrganizationComponent { /// - `subtract`: An array of addresses to remove from the committee. fn adjust_committee( ref self: ComponentState, - add: Array, - subtract: Array, + add: Span, + subtract: Span, ) { // any one subtracted, power would be taken down to zero. + // only ADMIN or OWNER can modify committee + let caller = get_caller_address(); + let can_add = self.permission_manager.internal._has_permission(caller, Permission::ADD_MEMBER); + let can_remove = self.permission_manager.internal._has_permission(caller, Permission::REMOVE_MEMBER); + assert(can_add || can_remove, 'Org: not authorized'); + let mut add_iter = add.iter(); + loop { + match add_iter.next() { + Option::Some(addr) => { + self.commitee.write(*addr, 1); + }, + Option::None => { break; } + } + }; + + let mut subtract_iter = subtract.iter(); + loop { + match subtract_iter.next() { + Option::Some(addr) => { + self.commitee.write(*addr, 0); + }, + Option::None => { break; } + } + }; + } /// Updates the organization's configuration. @@ -82,7 +123,14 @@ pub mod OrganizationComponent { /// - `config`: The new organization configuration. fn update_organization_config( ref self: ComponentState, config: OrganizationConfig, - ) {} + ) { + let caller = get_caller_address(); + let can_update = self.permission_manager.internal._has_permission(caller, Permission::SET_SALARIES); + assert(can_update, 'Org: not authorized'); + + self.config.write(config.into()); + + } /// Retrieves the core details of the organization. /// diff --git a/src/components/permission_manager.cairo b/src/components/permission_manager.cairo new file mode 100644 index 0000000..2b6f1b0 --- /dev/null +++ b/src/components/permission_manager.cairo @@ -0,0 +1,143 @@ +/// ## A Starknet component for managing granular permissions. +/// +/// This component assigns specific permissions to accounts using a bitmask. +#[starknet::component] +pub mod PermissionManagerComponent { + use starknet::storage::{Map, StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::{ContractAddress, get_caller_address}; + use core::array::{ArrayTrait, array, SpanTrait}; + use core::option::OptionTrait; + use core::integer::u256; + + /// Permissions in the organization, represented as a bitmask. + #[derive(Copy, Drop, Serde, PartialEq)] + pub enum Permission { + ADD_MEMBER = 1, + REMOVE_MEMBER = 2, + SEND_INVITES = 4, + SET_SALARIES = 8, + SET_DISBURSEMENT = 16, + ADD_VAULT_TOKENS = 32, + VAULT_FUNCTIONS = 64, + GRANT_ADMIN = 128, + REVOKE_ADMIN = 256, + } + + /// Defines the storage layout for the `PermissionManagerComponent`. + #[storage] + pub struct Storage { + /// Maps an address to its bitmask of permissions. + pub permissions: Map, + } + + /// Events emitted by the `PermissionManagerComponent`. + #[event] + #[derive(Drop, starknet::Event)] + pub enum Event { + PermissionGranted: PermissionGranted, + PermissionRevoked: PermissionRevoked, + AllPermissionsGranted: AllPermissionsGranted, + AllPermissionsRevoked: AllPermissionsRevoked, + } + + #[derive(Drop, starknet::Event)] + pub struct PermissionGranted { + #[key] + pub account: ContractAddress, + pub permission: Permission, + } + + #[derive(Drop, starknet::Event)] + pub struct PermissionRevoked { + #[key] + pub account: ContractAddress, + pub permission: Permission, + } + + #[derive(Drop, starknet::Event)] + pub struct AllPermissionsGranted { + #[key] + pub account: ContractAddress, + } + + #[derive(Drop, starknet::Event)] + pub struct AllPermissionsRevoked { + #[key] + pub account: ContractAddress, + } + + /// # PermissionManagerComponent + /// + /// Public-facing API for permission management. + #[embeddable_as(PermissionManager)] + pub impl Permission< + TContractState, +HasComponent, +Drop, + > of crate::interfaces::ipermission::IPermission> { + /// Grants a specific permission to an account. + fn grant_permission(ref self: ComponentState, account: ContractAddress, permission: Permission) { + let caller = get_caller_address(); + let has_permission = self.internal._has_permission(caller, Permission::GRANT_ADMIN); + assert(has_permission, 'Perm: caller not admin'); + + let mut current_permissions = self.permissions.read(account); + current_permissions = current_permissions | (permission.into()); + self.permissions.write(account, current_permissions); + self.emit(Event::PermissionGranted(PermissionGranted { account, permission })); + } + + /// Revokes a specific permission from an account. + fn revoke_permission(ref self: ComponentState, account: ContractAddress, permission: Permission) { + let caller = get_caller_address(); + let has_permission = self.internal._has_permission(caller, Permission::REVOKE_ADMIN); + assert(has_permission, 'Perm: caller not admin'); + + let mut current_permissions = self.permissions.read(account); + current_permissions = current_permissions & (!(permission.into())); + self.permissions.write(account, current_permissions); + self.emit(Event::PermissionRevoked(PermissionRevoked { account, permission })); + } + + /// Grants all permissions to an account. + fn grant_all_permissions(ref self: ComponentState, account: ContractAddress) { + let caller = get_caller_address(); + let has_permission = self.internal._has_permission(caller, Permission::GRANT_ADMIN); + assert(has_permission, 'Perm: caller not admin'); + + // Set all bits to 1 (full permissions) + let all_perms = 511; // Sum of all enum values + self.permissions.write(account, all_perms); + self.emit(Event::AllPermissionsGranted(AllPermissionsGranted { account })); + } + + /// Revokes all permissions from an account. + fn revoke_all_permissions(ref self: ComponentState, account: ContractAddress) { + let caller = get_caller_address(); + let has_permission = self.internal._has_permission(caller, Permission::REVOKE_ADMIN); + assert(has_permission, 'Perm: caller not admin'); + + self.permissions.write(account, 0); + self.emit(Event::AllPermissionsRevoked(AllPermissionsRevoked { account })); + } + + /// Checks if an account has a specific permission. + fn has_permission(self: @ComponentState, account: ContractAddress, permission: Permission) -> bool { + self.internal._has_permission(account, permission) + } + } + + /// # InternalImpl + #[generate_trait] + pub impl InternalImpl, > of PermissionInternalTrait { + /// Initializes the permission manager with an owner who has all permissions. + fn _init(ref self: ComponentState, owner: ContractAddress) { + let all_perms = 511; // Sum of all enum values + self.permissions.write(owner, all_perms); + } + + /// Internal function to check if an account has a specific permission. + fn _has_permission(self: @ComponentState, account: ContractAddress, permission: Permission) -> bool { + let account_perms = self.permissions.read(account); + (account_perms & (permission.into())) != 0 + } + } +} \ No newline at end of file From d2c42d93c44c8eccbfa22eeef477a3469a196891 Mon Sep 17 00:00:00 2001 From: Dhara Pandya Date: Wed, 10 Sep 2025 14:54:26 +0530 Subject: [PATCH 2/2] feat: add PermissionManagerComponent --- src/components/member_manager.cairo | 20 +-- src/components/organization.cairo | 174 ++++++++++++++++++--------- src/contracts/core.cairo | 16 +-- src/interfaces/imember_manager.cairo | 1 + src/interfaces/iorganization.cairo | 35 +++++- src/structs/member_structs.cairo | 16 +-- src/structs/organization.cairo | 57 +++++++++ src/tests/test_contract.cairo | 2 - src/tests/test_disbursement.cairo | 8 +- 9 files changed, 238 insertions(+), 91 deletions(-) delete mode 100644 src/tests/test_contract.cairo diff --git a/src/components/member_manager.cairo b/src/components/member_manager.cairo index 6c48d6e..7cefa4d 100644 --- a/src/components/member_manager.cairo +++ b/src/components/member_manager.cairo @@ -106,8 +106,8 @@ pub mod MemberManagerComponent { member.details.write(details); member.member.write(new_member); member.reg_time.write(reg_time); - member.total_received.write(Option::Some(0)); - member.total_disbursements.write(Option::Some(0)); + member.total_received.write(0); + member.total_disbursements.write(0); self.member_count.write(id); let factory_dispatcher = IFactoryDispatcher { contract_address: self.factory.read() }; @@ -371,12 +371,12 @@ pub mod MemberManagerComponent { let mut member_node = self.members.entry(member_id); member_node .total_received - .write(Option::Some(member_node.total_received.read().unwrap() + 1)); + .write(member_node.total_received.read() + 1); member_node.no_of_payouts.write(member_node.no_of_payouts.read() + 1); - member_node.last_disbursement_timestamp.write(Option::Some(timestamp)); + member_node.last_disbursement_timestamp.write(timestamp); member_node .total_disbursements - .write(Option::Some(member_node.total_disbursements.read().unwrap() + 1)); + .write(member_node.total_disbursements.read() + 1); } /// Returns the address of the factory contract. @@ -388,6 +388,12 @@ pub mod MemberManagerComponent { fn get_core_org_address(self: @ComponentState) -> ContractAddress { self.core_org.read() } + fn is_admin( + self: @ComponentState, member_address: ContractAddress, + ) -> bool { + let caller = get_caller_address(); + self.admin_ca.entry(caller).read() + } } /// # InternalImpl @@ -434,8 +440,8 @@ pub mod MemberManagerComponent { new_admin_node.details.write(new_admin_details); new_admin_node.member.write(new_admin); new_admin_node.reg_time.write(reg_time); - new_admin_node.total_received.write(Option::Some(0)); - new_admin_node.total_disbursements.write(Option::Some(0)); + new_admin_node.total_received.write(0); + new_admin_node.total_disbursements.write(0); self.admin_ca.entry(caller).write(true); self.admin_ca.entry(owner).write(true); diff --git a/src/components/organization.cairo b/src/components/organization.cairo index eed01af..161c6ce 100644 --- a/src/components/organization.cairo +++ b/src/components/organization.cairo @@ -7,22 +7,15 @@ /// - Ownership transfers. #[starknet::component] pub mod OrganizationComponent { - use starknet::storage::{Map, StoragePointerReadAccess, StoragePointerWriteAccess}; + use starknet::storage::{Map, StoragePathEntry,StoragePointerReadAccess, StoragePointerWriteAccess}; use starknet::{ContractAddress, get_block_timestamp, get_caller_address}; // use crate::interfaces::icore::IConfig; use crate::interfaces::iorganization::IOrganization; // use crate::structs::member_structs::MemberTrait; use crate::structs::organization::{ - OrganizationConfig, OrganizationConfigNode, OrganizationInfo, OrganizationType, + Contract, ContractParties, ContractStatus, ContractType,OrganizationConfig, OrganizationConfigNode, OrganizationInfo, OrganizationType, }; use super::super::member_manager::MemberManagerComponent; - use core::array::{ArrayTrait, SpanTrait}; - use super::super::permission_manager::PermissionManagerComponent; - use super::super::permission_manager::PermissionManagerComponent::Role; - use super::super::permission_manager::PermissionManagerComponent::PermissionInternalTrait; - use core::option::OptionTrait; - use core::array::array; - use core::traits::Into; /// Defines the storage layout for the `OrganizationComponent`. #[storage] @@ -32,9 +25,13 @@ pub mod OrganizationComponent { /// Maps a committee member's address to their power level or rank. pub commitee: Map, // address -> level of power /// The configuration node for the organization. - pub config: Mutable, // refactor to OrganizationConfig + pub config: OrganizationConfigNode, // refactor to OrganizationConfig /// Struct containing the core information of the organization. - pub org_info: Mutable, + pub org_info: OrganizationInfo, + /// Maps an id to a contract + pub contracts: Map, + /// Contract counter + pub contract_counter: u64, } /// Events emitted by the `OrganizationComponent`. @@ -51,7 +48,6 @@ pub mod OrganizationComponent { +HasComponent, +Drop, impl Member: MemberManagerComponent::HasComponent, - impl Permission: PermissionManagerComponent::HasComponent, > of IOrganization> { /// Transfers the claim of the organization to a new address. /// @@ -62,16 +58,7 @@ pub mod OrganizationComponent { /// - `to`: The address of the new owner. fn transfer_organization_claim( ref self: ComponentState, to: ContractAddress, - ) { - // only OWNER can transfer Ownership - let caller = get_caller_address(); - let is_owner = self.permission_manager.internal._has_permission(caller, Permission::GRANT_ADMIN); - assert(is_owner, 'Org: not owner'); - - let mut org_info=self.org_info.read(); - org_info.owner=to; - self.org_info.write(org_info); - } + ) {} /// Adjusts the organization's committee by adding or removing members. /// @@ -84,33 +71,10 @@ pub mod OrganizationComponent { /// - `subtract`: An array of addresses to remove from the committee. fn adjust_committee( ref self: ComponentState, - add: Span, - subtract: Span, + add: Array, + subtract: Array, ) { // any one subtracted, power would be taken down to zero. - // only ADMIN or OWNER can modify committee - let caller = get_caller_address(); - let can_add = self.permission_manager.internal._has_permission(caller, Permission::ADD_MEMBER); - let can_remove = self.permission_manager.internal._has_permission(caller, Permission::REMOVE_MEMBER); - assert(can_add || can_remove, 'Org: not authorized'); - let mut add_iter = add.iter(); - loop { - match add_iter.next() { - Option::Some(addr) => { - self.commitee.write(*addr, 1); - }, - Option::None => { break; } - } - }; - - let mut subtract_iter = subtract.iter(); - loop { - match subtract_iter.next() { - Option::Some(addr) => { - self.commitee.write(*addr, 0); - }, - Option::None => { break; } - } - }; + } @@ -123,14 +87,7 @@ pub mod OrganizationComponent { /// - `config`: The new organization configuration. fn update_organization_config( ref self: ComponentState, config: OrganizationConfig, - ) { - let caller = get_caller_address(); - let can_update = self.permission_manager.internal._has_permission(caller, Permission::SET_SALARIES); - assert(can_update, 'Org: not authorized'); - - self.config.write(config.into()); - - } + ) {} /// Retrieves the core details of the organization. /// @@ -139,7 +96,112 @@ pub mod OrganizationComponent { fn get_organization_details(self: @ComponentState) -> OrganizationInfo { self.org_info.read() } + /// Creates an employee contract, to be given at hiring, or updated during employment + /// Show to employee at hiring + fn create_company_to_member_contract( + ref self: ComponentState, + contract_type: ContractType, + member_id: u256, + ipfs_hash: felt252, + expiry: Option, + ) { + let member_component = get_dep_component!(@self, Member); + let caller = get_caller_address(); + let is_admin = member_component.admin_ca.entry(caller).read(); + assert(is_admin, 'Caller Not Permitted'); + + let contract = Contract { + id: self.contract_counter.read().into(), + hash: ipfs_hash, + version: 1, + signed_time: 0, + contract_parties: ContractParties::COMPANY_MEMBER(member_id), + status: ContractStatus::PROPOSED, + expiry_time: Option::None, + }; + + self.contracts.entry(self.contract_counter.read().into()).write(contract); + self.contract_counter.write(self.contract_counter.read() + 1); + } + + /// Creates a contract between two companies using Littlefinger. Advanced features + /// Show to both companies + fn create_company_to_partner_contract( + ref self: ComponentState, + contract_type: ContractType, + partner_address: ContractAddress, + ipfs_hash: felt252, + expiry: Option, + ) { + let member_component = get_dep_component!(@self, Member); + let caller = get_caller_address(); + let is_admin = member_component.admin_ca.entry(caller).read(); + assert(is_admin, 'Caller Not Permitted'); + + let contract = Contract { + id: self.contract_counter.read().into(), + hash: ipfs_hash, + version: 1, + signed_time: 0, + contract_parties: ContractParties::COMPANY_COMPANY(partner_address), + status: ContractStatus::PROPOSED, + expiry_time: Option::None, + }; + + self.contracts.entry(self.contract_counter.read().into()).write(contract); + self.contract_counter.write(self.contract_counter.read() + 1); + } + + /// Used to accept a contract, by whoever is on the other side of the contract (recipeint) + /// Will implement Starknet message signing soon + fn sign_contract( + ref self: ComponentState, contract_id: u256, signature: Array, + ) { + // ecdsa::check_ecdsa_signature() + let mut contract = self.contracts.entry(contract_id).read(); + contract.status = ContractStatus::ACTIVE; + contract.signed_time = get_block_timestamp(); + + self.contracts.entry(contract_id).write(contract); + } + + /// Updates a contract ipfs hash and version + /// Show to employee at hiring + fn update_contract( + ref self: ComponentState, + contract_id: u256, + new_ipfs_hash: felt252, + expiry: Option, + ) { + let mut contract = self.contracts.entry(contract_id).read(); + contract.hash = new_ipfs_hash; + + if expiry.is_some() { + contract.expiry_time = Option::Some(expiry.unwrap()); + } + contract.version += 1; + + self.contracts.entry(contract_id).write(contract); + } + + /// Termminates a contract, can be used by employees or fellow companies + /// Mutual agreement between company and employee + fn terminate_contract( + ref self: ComponentState, contract_id: u256, signature: Array, + ) { + let mut contract = self.contracts.entry(contract_id).read(); + contract.status = ContractStatus::TERMINATED; + } + + /// Used to get a contract ipfs hash for access purpose + /// ### Returns + /// - Contract: all the important info of the contract suitable for storage onchain. + /// - The rest goes to IPFS + fn get_contract(self: @ComponentState, contract_id: u256) -> Contract { + self.contracts.entry(contract_id).read() + } } + /// # InternalImpl /// diff --git a/src/contracts/core.cairo b/src/contracts/core.cairo index 936cd44..cc53154 100644 --- a/src/contracts/core.cairo +++ b/src/contracts/core.cairo @@ -30,7 +30,7 @@ mod Core { use openzeppelin::upgrades::interface::IUpgradeable; use starknet::storage::StoragePointerWriteAccess; use starknet::{ - ClassHash, ContractAddress, get_block_timestamp, get_caller_address, get_contract_address, + ClassHash, ContractAddress, get_block_timestamp, get_contract_address, }; use crate::interfaces::imember_manager::IMemberManager; @@ -158,9 +158,6 @@ mod Core { deployer, organization_type, ); - // MemberManagerComponent::InternalImpl::_initialize( - // ref self.member, first_admin_fname, first_admin_lname, first_admin_alias - // ) self .member ._initialize( @@ -173,9 +170,6 @@ mod Core { ); self.vault_address.write(vault_address); self.disbursement._init(owner); - // self.disbursement._add_authorized_caller(deployer); - // let this_contract = get_contract_address(); - // self.disbursement._add_authorized_caller(this_contract); self.ownable.initializer(owner); } @@ -215,7 +209,6 @@ mod Core { fn initialize_disbursement_schedule( ref self: ContractState, schedule_type: u8, - //schedule_id: felt252, start: u64, //timestamp end: u64, interval: u64, @@ -235,7 +228,6 @@ mod Core { /// - If the payout is attempted before the required interval has passed since the last /// execution. fn schedule_payout(ref self: ContractState, token: ContractAddress) { - let caller = get_caller_address(); let members = self.member.get_members(); let no_of_members = members.len(); @@ -244,7 +236,6 @@ mod Core { let vault_dispatcher = IVaultDispatcher { contract_address: vault_address }; let total_bonus = vault_dispatcher.get_bonus_allocation(token); - let total_funds = vault_dispatcher.get_token_balance(token); let current_schedule = self.disbursement.get_current_schedule(); assert(current_schedule.status == ScheduleStatus::ACTIVE, 'Schedule not active'); @@ -260,8 +251,6 @@ mod Core { ); } - // let mut failed_disbursements = array![]; - // Everyone uses a base weight multiplier at the start, of 1 let mut total_weight: u16 = 0; for i in 0..no_of_members { @@ -278,12 +267,13 @@ mod Core { // role: current_member_response.role, // // base_pay: current_member_response.base_pay, // }; + let timestamp = get_block_timestamp(); let amount = self .disbursement .compute_renumeration(current_member_response, total_bonus, total_weight); let timestamp = get_block_timestamp(); vault_dispatcher.pay_member(token, current_member_response.address, amount); - // self.member.record_member_payment(current_member_response.id, amount, timestamp) + self.member.record_member_payment(current_member_response.id,amount,timestamp); } self.disbursement.update_current_schedule_last_execution(now); diff --git a/src/interfaces/imember_manager.cairo b/src/interfaces/imember_manager.cairo index fb3dafd..1d3d2d8 100644 --- a/src/interfaces/imember_manager.cairo +++ b/src/interfaces/imember_manager.cairo @@ -228,5 +228,6 @@ pub trait IMemberManager { // ROLE MANAGEMENT // ALLOCATION WEIGHT MANAGEMENT (PROMOTION & DEMOTION) + fn is_admin(self: @TContractState, member_address: ContractAddress) -> bool; } diff --git a/src/interfaces/iorganization.cairo b/src/interfaces/iorganization.cairo index 700a413..ed80ab7 100644 --- a/src/interfaces/iorganization.cairo +++ b/src/interfaces/iorganization.cairo @@ -1,5 +1,5 @@ use starknet::ContractAddress; -use crate::structs::organization::{OrganizationConfig, OrganizationInfo}; +use crate::structs::organization::{Contract, ContractType, OrganizationConfig, OrganizationInfo}; // Some functions here might require multiple signing to execute. /// # IOrganization @@ -55,4 +55,37 @@ pub trait IOrganization { /// /// An `OrganizationInfo` struct containing the organization's details. fn get_organization_details(self: @TContractState) -> OrganizationInfo; + // fn create_contract( + // ref self: TContractState, + // contract_type: ContractType, + // parties: Array, + // ipfs_hash: felt252, + // expiry: Option, + // ); + + fn create_company_to_member_contract( + ref self: TContractState, + contract_type: ContractType, + member_id: u256, + ipfs_hash: felt252, + expiry: Option, + ); + + fn create_company_to_partner_contract( + ref self: TContractState, + contract_type: ContractType, + partner_address: ContractAddress, + ipfs_hash: felt252, + expiry: Option, + ); + + fn sign_contract(ref self: TContractState, contract_id: u256, signature: Array); + + fn update_contract( + ref self: TContractState, contract_id: u256, new_ipfs_hash: felt252, expiry: Option, + ); + + fn terminate_contract(ref self: TContractState, contract_id: u256, signature: Array); + + fn get_contract(self: @TContractState, contract_id: u256) -> Contract; } diff --git a/src/structs/member_structs.cairo b/src/structs/member_structs.cairo index cbfa963..ba221a3 100644 --- a/src/structs/member_structs.cairo +++ b/src/structs/member_structs.cairo @@ -16,11 +16,11 @@ pub struct MemberResponse { // The base pay is agreed between the member and the company at the beginning of their work // together i.e. during registration pub base_pay: u256, - pub pending_allocations: Option, - pub total_received: Option, + pub pending_allocations: u256, + pub total_received: u256, pub no_of_payouts: u32, - pub last_disbursement_timestamp: Option, - pub total_disbursements: Option, + pub last_disbursement_timestamp: u64, + pub total_disbursements: u64, pub reg_time: u64, } @@ -63,11 +63,11 @@ pub struct MemberNode { pub details: MemberDetails, pub member: Member, pub base_pay: u256, - pub pending_allocations: Option, - pub total_received: Option, + pub pending_allocations: u256, + pub total_received: u256, pub no_of_payouts: u32, - pub last_disbursement_timestamp: Option, - pub total_disbursements: Option, + pub last_disbursement_timestamp: u64, + pub total_disbursements: u64, pub reg_time: u64, } diff --git a/src/structs/organization.cairo b/src/structs/organization.cairo index b49cd10..ef148c7 100644 --- a/src/structs/organization.cairo +++ b/src/structs/organization.cairo @@ -40,3 +40,60 @@ pub struct OrganizationConfig { pub struct OrganizationConfigNode { pub additional_data: Vec, } +#[allow(starknet::store_no_default_variant)] +#[derive(Copy, Drop, Serde, starknet::Store)] +pub enum ContractType { + EMPLOYMEE_AGREEMENT, + NON_DISCLOSURE_AGREEMENT, + CONTRACTOR_AGREEMENT, + EQUITY_AGREEMENT, + EXIT_CONTRACT, +} + +#[derive(Copy, Drop, Serde, Default, starknet::Store)] +pub enum ContractStatus { + #[default] + PROPOSED, + ACTIVE, + TERMINATED, + REVOKED, + SUSPENDED, + EXPIRED, +} + +// If third party for company_member is going to be added, it will be here +#[derive(Copy, Drop, Serde, Default, starknet::Store)] +pub enum ContractParties { + #[default] + COMPANY_MEMBER: u256, // member id + COMPANY_COMPANY: ContractAddress, // address of fellow org + ORG_ORG_THIRD_PARTY: (ContractAddress, ContractAddress), +} + +#[derive(Copy, Drop, Serde, Default, starknet::Store)] +pub struct Contract { + pub id: u256, + pub hash: felt252, + pub version: u64, + pub signed_time: u64, + pub contract_parties: ContractParties, + pub status: ContractStatus, + pub expiry_time: Option, +} + +#[generate_trait] +impl ContractImpl of ContractTrait { + fn default(ref self: Contract) -> Contract { + let default_contract = Contract { + id: 0, + hash: 0, + version: 0, + signed_time: 0, + contract_parties: ContractParties::COMPANY_MEMBER(0), + status: ContractStatus::PROPOSED, + expiry_time: Option::None, + }; + + default_contract + } +} \ No newline at end of file diff --git a/src/tests/test_contract.cairo b/src/tests/test_contract.cairo deleted file mode 100644 index 5ceabd6..0000000 --- a/src/tests/test_contract.cairo +++ /dev/null @@ -1,2 +0,0 @@ -use snforge_std::{ContractClassTrait, DeclareResultTrait, declare}; -use starknet::ContractAddress; diff --git a/src/tests/test_disbursement.cairo b/src/tests/test_disbursement.cairo index 4095723..2ba1260 100644 --- a/src/tests/test_disbursement.cairo +++ b/src/tests/test_disbursement.cairo @@ -45,11 +45,11 @@ fn create_test_member_response() -> MemberResponse { address: member(), status: MemberStatus::ACTIVE, base_pay: 50000, - pending_allocations: Option::Some(1000), - total_received: Option::Some(10000), + pending_allocations: 1000, + total_received: 10000, no_of_payouts: 2, - last_disbursement_timestamp: Option::Some(1500), - total_disbursements: Option::Some(5), + last_disbursement_timestamp: 1500, + total_disbursements: 5, reg_time: 1000, } }