From 22122354ee9ddabc274868c0ec65cc3df3c695d7 Mon Sep 17 00:00:00 2001 From: bal7hazar Date: Fri, 22 Nov 2024 19:05:25 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Create=20member,=20guild=20and=20al?= =?UTF-8?q?liance=20features?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Scarb.lock | 1 + .../src/components/controllable.cairo | 2 - packages/controller/src/lib.cairo | 3 - packages/controller/src/models/account.cairo | 2 +- .../controller/src/models/controller.cairo | 2 +- packages/controller/src/models/index.cairo | 19 -- packages/controller/src/models/member.cairo | 95 ------ packages/controller/src/models/signer.cairo | 2 +- packages/controller/src/store.cairo | 22 -- .../provider/src/components/deployable.cairo | 48 ++- .../provider/src/components/groupable.cairo | 87 ++++++ packages/provider/src/lib.cairo | 4 + packages/provider/src/models/deployment.cairo | 48 +-- packages/provider/src/models/factory.cairo | 2 +- packages/provider/src/models/index.cairo | 24 +- .../src/models/team.cairo | 51 +++- packages/provider/src/models/teammate.cairo | 120 ++++++++ packages/provider/src/store.cairo | 32 ++ .../src/types/role.cairo | 12 +- packages/registry/src/models/access.cairo | 2 +- .../registry/src/models/achievement.cairo | 2 +- packages/registry/src/models/game.cairo | 2 +- packages/society/Scarb.toml | 1 + .../society/src/components/allianceable.cairo | 274 ++++++++++++++++++ .../{friendable.cairo => followable.cairo} | 2 +- .../society/src/components/guildable.cairo | 256 +++++++++++++++- packages/society/src/constants.cairo | 3 + packages/society/src/events/index.cairo | 4 +- packages/society/src/lib.cairo | 5 +- packages/society/src/models/alliance.cairo | 132 +++++++++ packages/society/src/models/guild.cairo | 244 ++++++++++++++++ packages/society/src/models/index.cairo | 33 +-- packages/society/src/models/member.cairo | 157 ++++++++-- packages/society/src/store.cairo | 43 +-- 34 files changed, 1446 insertions(+), 290 deletions(-) delete mode 100644 packages/controller/src/models/member.cairo create mode 100644 packages/provider/src/components/groupable.cairo rename packages/{controller => provider}/src/models/team.cairo (54%) create mode 100644 packages/provider/src/models/teammate.cairo rename packages/{controller => provider}/src/types/role.cairo (80%) create mode 100644 packages/society/src/components/allianceable.cairo rename packages/society/src/components/{friendable.cairo => followable.cairo} (94%) create mode 100644 packages/society/src/models/alliance.cairo create mode 100644 packages/society/src/models/guild.cairo diff --git a/Scarb.lock b/Scarb.lock index 8f207c1..471d76f 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -60,4 +60,5 @@ version = "0.0.0" dependencies = [ "dojo", "dojo_cairo_test", + "registry", ] diff --git a/packages/controller/src/components/controllable.cairo b/packages/controller/src/components/controllable.cairo index 5854165..aaedd7e 100644 --- a/packages/controller/src/components/controllable.cairo +++ b/packages/controller/src/components/controllable.cairo @@ -9,9 +9,7 @@ mod ControllableComponent { use controller::store::{Store, StoreTrait}; use controller::models::account::{Account, AccountTrait, AccountAssert}; use controller::models::controller::{Controller, ControllerTrait, ControllerAssert}; - use controller::models::member::{Member, MemberTrait, MemberAssert}; use controller::models::signer::{Signer, SignerTrait, SignerAssert}; - use controller::models::team::{Team, TeamTrait, TeamAssert}; // Storage diff --git a/packages/controller/src/lib.cairo b/packages/controller/src/lib.cairo index c62d605..580f933 100644 --- a/packages/controller/src/lib.cairo +++ b/packages/controller/src/lib.cairo @@ -3,16 +3,13 @@ mod store; mod types { mod method; - mod role; } mod models { mod index; mod account; mod controller; - mod member; mod signer; - mod team; } mod components { diff --git a/packages/controller/src/models/account.cairo b/packages/controller/src/models/account.cairo index 0eac43e..0029ae7 100644 --- a/packages/controller/src/models/account.cairo +++ b/packages/controller/src/models/account.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use controller::models::index::Account; diff --git a/packages/controller/src/models/controller.cairo b/packages/controller/src/models/controller.cairo index cf43b40..e47bf02 100644 --- a/packages/controller/src/models/controller.cairo +++ b/packages/controller/src/models/controller.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use controller::models::index::Controller; diff --git a/packages/controller/src/models/index.cairo b/packages/controller/src/models/index.cairo index 19673c0..ac38ceb 100644 --- a/packages/controller/src/models/index.cairo +++ b/packages/controller/src/models/index.cairo @@ -25,16 +25,6 @@ pub struct Controller { constructor_calldata: ByteArray, } -#[derive(Copy, Drop, Serde)] -#[dojo::model] -pub struct Member { - #[key] - account_id: felt252, - #[key] - team_id: felt252, - role: u8, -} - #[derive(Clone, Drop, Serde)] #[dojo::model] pub struct Signer { @@ -45,12 +35,3 @@ pub struct Signer { method: u8, metadata: ByteArray, } - -#[derive(Clone, Drop, Serde)] -#[dojo::model] -pub struct Team { - #[key] - id: felt252, - name: felt252, - description: ByteArray, -} diff --git a/packages/controller/src/models/member.cairo b/packages/controller/src/models/member.cairo deleted file mode 100644 index 77c3010..0000000 --- a/packages/controller/src/models/member.cairo +++ /dev/null @@ -1,95 +0,0 @@ -// Intenral imports - -use controller::models::index::Member; -use controller::types::role::Role; - -// Errors - -pub mod errors { - pub const MEMBER_ALREADY_EXISTS: felt252 = 'Member: already exists'; - pub const MEMBER_NOT_EXIST: felt252 = 'Member: does not exist'; - pub const MEMBER_INVALID_ACCOUNT_ID: felt252 = 'Member: invalid account id'; - pub const MEMBER_INVALID_TEAM_ID: felt252 = 'Member: invalid team id'; - pub const MEMBER_INVALID_ROLE: felt252 = 'Member: invalid role'; -} - -#[generate_trait] -impl MemberImpl of MemberTrait { - #[inline] - fn new(account_id: felt252, team_id: felt252, role: Role) -> Member { - // [Check] Inputs - MemberAssert::assert_valid_account_id(account_id); - MemberAssert::assert_valid_team_id(team_id); - MemberAssert::assert_valid_role(role); - // [Return] Member - Member { account_id: account_id, team_id: team_id, role: role.into() } - } -} - -#[generate_trait] -impl MemberAssert of AssertTrait { - #[inline] - fn assert_does_not_exist(self: @Member) { - assert(self.role == @Role::None.into(), errors::MEMBER_ALREADY_EXISTS); - } - - #[inline] - fn assert_does_exist(self: @Member) { - assert(self.role != @Role::None.into(), errors::MEMBER_NOT_EXIST); - } - - #[inline] - fn assert_valid_account_id(account_id: felt252) { - assert(account_id != 0, errors::MEMBER_INVALID_ACCOUNT_ID); - } - - #[inline] - fn assert_valid_team_id(team_id: felt252) { - assert(team_id != 0, errors::MEMBER_INVALID_TEAM_ID); - } - - #[inline] - fn assert_valid_role(role: Role) { - assert(role != Role::None, errors::MEMBER_INVALID_ROLE); - } -} - -#[cfg(test)] -mod tests { - // Local imports - - use super::{Member, MemberTrait, MemberAssert, Role}; - - // Constants - - const ACCOUNT_ID: felt252 = 'ACCOUNT_ID'; - const TEAM_ID: felt252 = 'TEAM_ID'; - const ROLE: Role = Role::Admin; - - #[test] - fn test_deployment_new() { - let member = MemberTrait::new(ACCOUNT_ID, TEAM_ID, ROLE); - assert_eq!(member.account_id, ACCOUNT_ID); - assert_eq!(member.team_id, TEAM_ID); - assert_eq!(member.role, ROLE.into()); - } - - #[test] - fn test_deployment_assert_does_exist() { - let member = MemberTrait::new(ACCOUNT_ID, TEAM_ID, ROLE); - member.assert_does_exist(); - } - - #[test] - #[should_panic(expected: 'Member: already exists')] - fn test_deployment_revert_already_exists() { - let member = MemberTrait::new(ACCOUNT_ID, TEAM_ID, ROLE); - member.assert_does_not_exist(); - } - - #[test] - #[should_panic(expected: 'Member: invalid role')] - fn test_deployment_revert_invalid_role() { - MemberTrait::new(ACCOUNT_ID, TEAM_ID, Role::None); - } -} diff --git a/packages/controller/src/models/signer.cairo b/packages/controller/src/models/signer.cairo index bb76e27..59b7802 100644 --- a/packages/controller/src/models/signer.cairo +++ b/packages/controller/src/models/signer.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use controller::models::index::Signer; use controller::types::method::Method; diff --git a/packages/controller/src/store.cairo b/packages/controller/src/store.cairo index 96473e7..d652149 100644 --- a/packages/controller/src/store.cairo +++ b/packages/controller/src/store.cairo @@ -13,9 +13,7 @@ use dojo::model::ModelStorage; use controller::models::account::Account; use controller::models::controller::Controller; -use controller::models::member::Member; use controller::models::signer::Signer; -use controller::models::team::Team; // Structs @@ -44,21 +42,11 @@ impl StoreImpl of StoreTrait { self.world.read_model(controller_id) } - #[inline] - fn get_member(self: Store, account_id: felt252, team_id: felt252) -> Member { - self.world.read_model((account_id, team_id)) - } - #[inline] fn get_signer(self: Store, signer_id: felt252) -> Signer { self.world.read_model(signer_id) } - #[inline] - fn get_team(self: Store, team_id: felt252) -> Team { - self.world.read_model(team_id) - } - #[inline] fn set_account(ref self: Store, account: @Account) { self.world.write_model(account); @@ -68,14 +56,4 @@ impl StoreImpl of StoreTrait { fn set_controller(ref self: Store, controller: @Controller) { self.world.write_model(controller); } - - #[inline] - fn set_member(ref self: Store, member: @Member) { - self.world.write_model(member); - } - - #[inline] - fn set_team(ref self: Store, team: @Team) { - self.world.write_model(team); - } } diff --git a/packages/provider/src/components/deployable.cairo b/packages/provider/src/components/deployable.cairo index e2668fa..de04cdd 100644 --- a/packages/provider/src/components/deployable.cairo +++ b/packages/provider/src/components/deployable.cairo @@ -10,8 +10,11 @@ mod DeployableComponent { use provider::models::deployment::{Deployment, DeploymentTrait, DeploymentAssert}; use provider::models::factory::{Factory, FactoryTrait, FactoryAssert}; use provider::types::service::{Service, ServiceTrait, SERVICE_COUNT}; + use provider::models::team::{Team, TeamTrait, TeamAssert}; + use provider::models::teammate::{Teammate, TeammateTrait, TeammateAssert}; use provider::types::status::Status; use provider::types::tier::Tier; + use provider::types::role::Role; // Storage @@ -60,10 +63,30 @@ mod DeployableComponent { let deployment = store.get_deployment(service.into(), project); deployment.assert_does_not_exist(); + // [Check] Caller permission + let account_id: felt252 = starknet::get_caller_address().into(); + let mut team = store.get_team(project); + if team.exists() { + // [Check] Caller is at least an admin + let teammate = store.get_teammate(project, team.time, account_id); + teammate.assert_is_allowed(Role::Admin); + // [Effect] Increment deployment count + team.deploy(); + store.set_team(@team); + } else { + // [Effect] Create team + let time = starknet::get_block_timestamp(); + let mut team = TeamTrait::new(project, time, project, ""); + team.deploy(); + store.set_team(@team); + // [Effect] Create teammate + let teammate = TeammateTrait::new(project, time, account_id, Role::Owner); + store.set_teammate(@teammate); + } + // [Effect] Create deployment - let owner = starknet::get_caller_address().into(); let deployment = DeploymentTrait::new( - service: service, project: project, owner: owner, tier: tier, config: "", + service: service, project: project, tier: tier, config: "", ); store.set_deployment(@deployment); } @@ -85,12 +108,29 @@ mod DeployableComponent { let mut deployment = store.get_deployment(service.into(), project); deployment.assert_does_exist(); - // [Check] Caller is owner - deployment.assert_is_owner(starknet::get_caller_address().into()); + // [Check] Team exists + let mut team = store.get_team(project); + team.assert_does_exist(); + + // [Check] Caller is at least admin + let account_id: felt252 = starknet::get_caller_address().into(); + let teammate = store.get_teammate(project, team.time, account_id); + teammate.assert_is_allowed(Role::Admin); // [Effect] Delete deployment deployment.nullify(); store.delete_deployment(@deployment); + + // [Effect] Decrement deployment count + team.remove(); + + // [Effect] Delete team if no deployments left + if team.deployment_count == 0 { + team.nullify(); + store.delete_team(@team); + } else { + store.set_team(@team); + } } } } diff --git a/packages/provider/src/components/groupable.cairo b/packages/provider/src/components/groupable.cairo new file mode 100644 index 0000000..ccda4bd --- /dev/null +++ b/packages/provider/src/components/groupable.cairo @@ -0,0 +1,87 @@ +#[starknet::component] +mod GroupableComponent { + // Dojo imports + + use dojo::world::WorldStorage; + + // Internal imports + + use provider::store::{Store, StoreTrait}; + use provider::models::team::{Team, TeamTrait, TeamAssert}; + use provider::models::teammate::{Teammate, TeammateTrait, TeammateAssert}; + use provider::types::role::Role; + + // Storage + + #[storage] + struct Storage {} + + // Events + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait { + fn add( + self: @ComponentState, + world: WorldStorage, + name: felt252, + account_id: felt252, + role: Role, + ) { + // [Setup] Datastore + let mut store: Store = StoreTrait::new(world); + + // [Check] Team exists + let team = store.get_team(name); + team.assert_does_exist(); + + // [Check] Caller is at least admin + let caller_id: felt252 = starknet::get_caller_address().into(); + let callermate = store.get_teammate(name, team.time, caller_id); + callermate.assert_is_allowed(Role::Admin); + + // [Check] Teammate does not exist + let teammate = store.get_teammate(name, team.time, account_id); + teammate.assert_does_not_exist(); + + // [Effect] Create teammate + let teammate = TeammateTrait::new(name, team.time, account_id, role); + store.set_teammate(@teammate); + } + + fn remove( + self: @ComponentState, + world: WorldStorage, + name: felt252, + account_id: felt252, + ) { + // [Setup] Datastore + let mut store: Store = StoreTrait::new(world); + + // [Check] Team exists + let team = store.get_team(name); + team.assert_does_exist(); + + // [Check] Caller is at least admin + let caller_id: felt252 = starknet::get_caller_address().into(); + let callermate = store.get_teammate(name, team.time, caller_id); + callermate.assert_is_allowed(Role::Admin); + + // [Check] Teammate exists + let mut teammate = store.get_teammate(name, team.time, account_id); + teammate.assert_does_exist(); + + // [Check] Caller has greater role than teammate + callermate.assert_is_greater(teammate.role.into()); + + // [Effect] Delete teammate + teammate.nullify(); + store.delete_teammate(@teammate); + } + } +} diff --git a/packages/provider/src/lib.cairo b/packages/provider/src/lib.cairo index 8277531..3fbf36b 100644 --- a/packages/provider/src/lib.cairo +++ b/packages/provider/src/lib.cairo @@ -11,6 +11,7 @@ mod elements { } mod types { + mod role; mod tier; mod service; mod status; @@ -20,8 +21,11 @@ mod models { mod index; mod deployment; mod factory; + mod teammate; + mod team; } mod components { mod deployable; + mod groupable; } diff --git a/packages/provider/src/models/deployment.cairo b/packages/provider/src/models/deployment.cairo index d36653c..63f428b 100644 --- a/packages/provider/src/models/deployment.cairo +++ b/packages/provider/src/models/deployment.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use provider::models::index::Deployment; use provider::types::service::Service; @@ -12,28 +12,22 @@ pub mod errors { pub const DEPLOYMENT_NOT_EXIST: felt252 = 'Deployment: does not exist'; pub const DEPLOYMENT_INVALID_SERVICE: felt252 = 'Deployment: invalid service'; pub const DEPLOYMENT_INVALID_PROJECT: felt252 = 'Deployment: invalid project'; - pub const DEPLOYMENT_INVALID_OWNER: felt252 = 'Deployment: invalid owner'; pub const DEPLOYMENT_INVALID_STATUS: felt252 = 'Deployment: invalid status'; pub const DEPLOYMENT_INVALID_TIER: felt252 = 'Deployment: invalid tier'; - pub const DEPLOYMENT_NOT_OWNER: felt252 = 'Deployment: caller is not owner'; } #[generate_trait] impl DeploymentImpl of DeploymentTrait { #[inline] - fn new( - service: Service, project: felt252, owner: felt252, tier: Tier, config: ByteArray, - ) -> Deployment { + fn new(service: Service, project: felt252, tier: Tier, config: ByteArray,) -> Deployment { // [Check] Inputs DeploymentAssert::assert_valid_service(service); DeploymentAssert::assert_valid_project(project); - DeploymentAssert::assert_valid_owner(owner); DeploymentAssert::assert_valid_tier(tier); // [Return] Deployment Deployment { service: service.into(), project: project, - owner: owner, status: Status::Disabled.into(), tier: tier.into(), config: config, @@ -42,7 +36,7 @@ impl DeploymentImpl of DeploymentTrait { #[inline] fn nullify(ref self: Deployment) { - self.owner = 0; + self.project = 0; } } @@ -50,12 +44,12 @@ impl DeploymentImpl of DeploymentTrait { impl DeploymentAssert of AssertTrait { #[inline] fn assert_does_not_exist(self: @Deployment) { - assert(self.owner == @0, errors::DEPLOYMENT_ALREADY_EXISTS); + assert(self.project == @0, errors::DEPLOYMENT_ALREADY_EXISTS); } #[inline] fn assert_does_exist(self: @Deployment) { - assert(self.owner != @0, errors::DEPLOYMENT_NOT_EXIST); + assert(self.project != @0, errors::DEPLOYMENT_NOT_EXIST); } #[inline] @@ -68,11 +62,6 @@ impl DeploymentAssert of AssertTrait { assert(project != 0, errors::DEPLOYMENT_INVALID_PROJECT); } - #[inline] - fn assert_valid_owner(owner: felt252) { - assert(owner != 0, errors::DEPLOYMENT_INVALID_OWNER); - } - #[inline] fn assert_valid_status(status: Status) { assert(status != Status::None, errors::DEPLOYMENT_INVALID_STATUS); @@ -82,11 +71,6 @@ impl DeploymentAssert of AssertTrait { fn assert_valid_tier(tier: Tier) { assert(tier != Tier::None, errors::DEPLOYMENT_INVALID_TIER); } - - #[inline] - fn assert_is_owner(self: @Deployment, caller: felt252) { - assert(@caller == self.owner, errors::DEPLOYMENT_NOT_OWNER); - } } #[cfg(test)] @@ -99,43 +83,27 @@ mod tests { const SERVICE: Service = Service::Katana; const PROJECT: felt252 = 'PROJECT'; - const OWNER: felt252 = 'OWNER'; const TIER: Tier = Tier::Basic; - const NOT_OWNER: felt252 = 'NOT_OWNER'; #[test] fn test_deployment_new() { - let deployment = DeploymentTrait::new(SERVICE, PROJECT, OWNER, TIER, ""); + let deployment = DeploymentTrait::new(SERVICE, PROJECT, TIER, ""); assert_eq!(deployment.service, SERVICE.into()); assert_eq!(deployment.project, PROJECT); - assert_eq!(deployment.owner, OWNER); assert_eq!(deployment.tier, TIER.into()); assert_eq!(deployment.config, ""); } #[test] fn test_deployment_assert_does_exist() { - let deployment = DeploymentTrait::new(SERVICE, PROJECT, OWNER, TIER, ""); + let deployment = DeploymentTrait::new(SERVICE, PROJECT, TIER, ""); deployment.assert_does_exist(); } #[test] #[should_panic(expected: 'Deployment: already exists')] fn test_deployment_revert_already_exists() { - let mut deployment = DeploymentTrait::new(SERVICE, PROJECT, OWNER, TIER, ""); + let mut deployment = DeploymentTrait::new(SERVICE, PROJECT, TIER, ""); deployment.assert_does_not_exist(); } - - #[test] - fn test_deployment_assert_is_owner() { - let deployment = DeploymentTrait::new(SERVICE, PROJECT, OWNER, TIER, ""); - deployment.assert_is_owner(OWNER); - } - - #[test] - #[should_panic(expected: 'Deployment: caller is not owner')] - fn test_deployment_revert_not_owner() { - let deployment = DeploymentTrait::new(SERVICE, PROJECT, OWNER, TIER, ""); - deployment.assert_is_owner(NOT_OWNER); - } } diff --git a/packages/provider/src/models/factory.cairo b/packages/provider/src/models/factory.cairo index 985e54f..fccbe0f 100644 --- a/packages/provider/src/models/factory.cairo +++ b/packages/provider/src/models/factory.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use provider::models::index::Factory; use provider::types::service::Service; diff --git a/packages/provider/src/models/index.cairo b/packages/provider/src/models/index.cairo index f44d853..8bc95eb 100644 --- a/packages/provider/src/models/index.cairo +++ b/packages/provider/src/models/index.cairo @@ -7,7 +7,6 @@ pub struct Deployment { service: u8, #[key] project: felt252, - owner: felt252, status: u8, tier: u8, config: ByteArray, @@ -21,3 +20,26 @@ pub struct Factory { version: felt252, default_version: felt252, } + +#[derive(Clone, Drop, Serde)] +#[dojo::model] +pub struct Team { + #[key] + id: felt252, + deployment_count: u32, + time: u64, + name: felt252, + description: ByteArray, +} + +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Teammate { + #[key] + team_id: felt252, + #[key] + time: u64, + #[key] + account_id: felt252, + role: u8, +} diff --git a/packages/controller/src/models/team.cairo b/packages/provider/src/models/team.cairo similarity index 54% rename from packages/controller/src/models/team.cairo rename to packages/provider/src/models/team.cairo index 68586ef..211f4a6 100644 --- a/packages/controller/src/models/team.cairo +++ b/packages/provider/src/models/team.cairo @@ -1,6 +1,7 @@ -// Intenral imports +// Internal imports -use controller::models::index::Team; +use provider::models::index::Team; +use provider::types::role::Role; // Errors @@ -8,18 +9,41 @@ pub mod errors { pub const TEAM_ALREADY_EXISTS: felt252 = 'Team: already exists'; pub const TEAM_NOT_EXIST: felt252 = 'Team: does not exist'; pub const TEAM_INVALID_IDENTIFIER: felt252 = 'Team: invalid identifier'; + pub const TEAM_INVALID_TIME: felt252 = 'Team: invalid time'; pub const TEAM_INVALID_NAME: felt252 = 'Team: invalid name'; } #[generate_trait] impl TeamImpl of TeamTrait { #[inline] - fn new(id: felt252, name: felt252, description: ByteArray) -> Team { + fn new(id: felt252, time: u64, name: felt252, description: ByteArray) -> Team { // [Check] Inputs TeamAssert::assert_valid_identifier(id); + TeamAssert::assert_valid_time(time); TeamAssert::assert_valid_name(name); // [Return] Team - Team { id: id, name: name, description: description, } + Team { id: id, deployment_count: 0, time: time, name: name, description: description } + } + + #[inline] + fn exists(self: @Team) -> bool { + self.name != @0 && self.time != @0 + } + + #[inline] + fn nullify(ref self: Team) { + self.time = 0; + self.name = 0; + } + + #[inline] + fn deploy(ref self: Team) { + self.deployment_count += 1; + } + + #[inline] + fn remove(ref self: Team) { + self.deployment_count -= 1; } } @@ -27,12 +51,12 @@ impl TeamImpl of TeamTrait { impl TeamAssert of AssertTrait { #[inline] fn assert_does_not_exist(self: @Team) { - assert(self.name == @0, errors::TEAM_ALREADY_EXISTS); + assert(!self.exists(), errors::TEAM_ALREADY_EXISTS); } #[inline] fn assert_does_exist(self: @Team) { - assert(self.name != @0, errors::TEAM_NOT_EXIST); + assert(self.exists(), errors::TEAM_NOT_EXIST); } #[inline] @@ -40,6 +64,11 @@ impl TeamAssert of AssertTrait { assert(identifier != 0, errors::TEAM_INVALID_IDENTIFIER); } + #[inline] + fn assert_valid_time(time: u64) { + assert(time != 0, errors::TEAM_INVALID_TIME); + } + #[inline] fn assert_valid_name(name: felt252) { assert(name != 0, errors::TEAM_INVALID_NAME); @@ -56,25 +85,27 @@ mod tests { const IDENTIFIER: felt252 = 'ID'; const NAME: felt252 = 'NAME'; + const TIME: u64 = 1; #[test] fn test_deployment_new() { - let team = TeamTrait::new(IDENTIFIER, 'NAME', ""); + let team = TeamTrait::new(IDENTIFIER, TIME, NAME, ""); assert_eq!(team.id, IDENTIFIER); - assert_eq!(team.name, 'NAME'); + assert_eq!(team.time, TIME); + assert_eq!(team.name, NAME); assert_eq!(team.description, ""); } #[test] fn test_deployment_assert_does_exist() { - let team = TeamTrait::new(IDENTIFIER, 'NAME', ""); + let team = TeamTrait::new(IDENTIFIER, TIME, NAME, ""); team.assert_does_exist(); } #[test] #[should_panic(expected: 'Team: already exists')] fn test_deployment_revert_already_exists() { - let team = TeamTrait::new(IDENTIFIER, 'NAME', ""); + let team = TeamTrait::new(IDENTIFIER, TIME, NAME, ""); team.assert_does_not_exist(); } } diff --git a/packages/provider/src/models/teammate.cairo b/packages/provider/src/models/teammate.cairo new file mode 100644 index 0000000..ac06f91 --- /dev/null +++ b/packages/provider/src/models/teammate.cairo @@ -0,0 +1,120 @@ +// Internal imports + +use provider::models::index::Teammate; +use provider::types::role::Role; + +// Errors + +pub mod errors { + pub const TEAMMATE_ALREADY_EXISTS: felt252 = 'Teammate: already exists'; + pub const TEAMMATE_NOT_EXIST: felt252 = 'Teammate: does not exist'; + pub const TEAMMATE_INVALID_ACCOUNT_ID: felt252 = 'Teammate: invalid account id'; + pub const TEAMMATE_INVALID_TEAM_ID: felt252 = 'Teammate: invalid team id'; + pub const TEAMMATE_INVALID_TIME: felt252 = 'Teammate: invalid time'; + pub const TEAMMATE_INVALID_ROLE: felt252 = 'Teammate: invalid role'; + pub const TEAMMATE_NOT_ALLOWED: felt252 = 'Teammate: caller is not allowed'; + pub const TEAMMATE_NOT_ENOUGH_PERMISSION: felt252 = 'Teammate: not enough permission'; +} + +#[generate_trait] +impl TeammateImpl of TeammateTrait { + #[inline] + fn new(team_id: felt252, time: u64, account_id: felt252, role: Role) -> Teammate { + // [Check] Inputs + TeammateAssert::assert_valid_team_id(team_id); + TeammateAssert::assert_valid_time(time); + TeammateAssert::assert_valid_account_id(account_id); + TeammateAssert::assert_valid_role(role); + // [Return] Teammate + Teammate { team_id: team_id, time: time, account_id: account_id, role: role.into() } + } + + #[inline] + fn nullify(ref self: Teammate) { + self.role = Role::None.into(); + } +} + +#[generate_trait] +impl TeammateAssert of AssertTrait { + #[inline] + fn assert_does_not_exist(self: @Teammate) { + assert(self.role == @Role::None.into(), errors::TEAMMATE_ALREADY_EXISTS); + } + + #[inline] + fn assert_does_exist(self: @Teammate) { + assert(self.role != @Role::None.into(), errors::TEAMMATE_NOT_EXIST); + } + + #[inline] + fn assert_valid_account_id(account_id: felt252) { + assert(account_id != 0, errors::TEAMMATE_INVALID_ACCOUNT_ID); + } + + #[inline] + fn assert_valid_team_id(team_id: felt252) { + assert(team_id != 0, errors::TEAMMATE_INVALID_TEAM_ID); + } + + #[inline] + fn assert_valid_time(time: u64) { + assert(time != 0, errors::TEAMMATE_INVALID_TIME); + } + + #[inline] + fn assert_valid_role(role: Role) { + assert(role != Role::None, errors::TEAMMATE_INVALID_ROLE); + } + + #[inline] + fn assert_is_allowed(self: @Teammate, role: Role) { + assert(self.role >= @role.into(), errors::TEAMMATE_NOT_ALLOWED); + } + + #[inline] + fn assert_is_greater(self: @Teammate, role: Role) { + assert(self.role > @role.into(), errors::TEAMMATE_NOT_ENOUGH_PERMISSION); + } +} + +#[cfg(test)] +mod tests { + // Local imports + + use super::{Teammate, TeammateTrait, TeammateAssert, Role}; + + // Constants + + const TEAM_ID: felt252 = 'TEAM_ID'; + const TIME: u64 = 1; + const ACCOUNT_ID: felt252 = 'ACCOUNT_ID'; + const ROLE: Role = Role::Admin; + + #[test] + fn test_deployment_new() { + let member = TeammateTrait::new(TEAM_ID, TIME, ACCOUNT_ID, ROLE); + assert_eq!(member.account_id, ACCOUNT_ID); + assert_eq!(member.team_id, TEAM_ID); + assert_eq!(member.role, ROLE.into()); + } + + #[test] + fn test_deployment_assert_does_exist() { + let member = TeammateTrait::new(TEAM_ID, TIME, ACCOUNT_ID, ROLE); + member.assert_does_exist(); + } + + #[test] + #[should_panic(expected: 'Teammate: already exists')] + fn test_deployment_revert_already_exists() { + let member = TeammateTrait::new(TEAM_ID, TIME, ACCOUNT_ID, ROLE); + member.assert_does_not_exist(); + } + + #[test] + #[should_panic(expected: 'Teammate: invalid role')] + fn test_deployment_revert_invalid_role() { + TeammateTrait::new(TEAM_ID, TIME, ACCOUNT_ID, Role::None); + } +} diff --git a/packages/provider/src/store.cairo b/packages/provider/src/store.cairo index 0d53989..f6d2ba0 100644 --- a/packages/provider/src/store.cairo +++ b/packages/provider/src/store.cairo @@ -13,6 +13,8 @@ use dojo::model::ModelStorage; use provider::models::deployment::Deployment; use provider::models::factory::Factory; +use provider::models::index::Team; +use provider::models::index::Teammate; // Structs @@ -40,6 +42,16 @@ impl StoreImpl of StoreTrait { self.world.read_model(factory_id) } + #[inline] + fn get_team(self: Store, team_id: felt252) -> Team { + self.world.read_model(team_id) + } + + #[inline] + fn get_teammate(self: Store, team_id: felt252, time: u64, account_id: felt252) -> Teammate { + self.world.read_model((team_id, time, account_id)) + } + #[inline] fn set_deployment(ref self: Store, deployment: @Deployment) { self.world.write_model(deployment); @@ -50,8 +62,28 @@ impl StoreImpl of StoreTrait { self.world.write_model(factory); } + #[inline] + fn set_team(ref self: Store, team: @Team) { + self.world.write_model(team); + } + + #[inline] + fn set_teammate(ref self: Store, teammate: @Teammate) { + self.world.write_model(teammate); + } + #[inline] fn delete_deployment(ref self: Store, deployment: @Deployment) { self.world.erase_model(deployment); } + + #[inline] + fn delete_team(ref self: Store, team: @Team) { + self.world.erase_model(team); + } + + #[inline] + fn delete_teammate(ref self: Store, teammate: @Teammate) { + self.world.erase_model(teammate); + } } diff --git a/packages/controller/src/types/role.cairo b/packages/provider/src/types/role.cairo similarity index 80% rename from packages/controller/src/types/role.cairo rename to packages/provider/src/types/role.cairo index ae3b9c1..abf5492 100644 --- a/packages/controller/src/types/role.cairo +++ b/packages/provider/src/types/role.cairo @@ -1,9 +1,9 @@ #[derive(Copy, Drop, PartialEq)] pub enum Role { None, - Owner, - Admin, Member, + Admin, + Owner, } // Implementations @@ -13,9 +13,9 @@ impl IntoRoleU8 of core::Into { fn into(self: Role) -> u8 { match self { Role::None => 0, - Role::Owner => 1, + Role::Member => 1, Role::Admin => 2, - Role::Member => 3, + Role::Owner => 3, } } } @@ -25,9 +25,9 @@ impl IntoU8Role of core::Into { fn into(self: u8) -> Role { match self { 0 => Role::None, - 1 => Role::Owner, + 1 => Role::Member, 2 => Role::Admin, - 3 => Role::Member, + 3 => Role::Owner, _ => Role::None, } } diff --git a/packages/registry/src/models/access.cairo b/packages/registry/src/models/access.cairo index fb4c5ac..40e399b 100644 --- a/packages/registry/src/models/access.cairo +++ b/packages/registry/src/models/access.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use registry::constants; use registry::models::index::Access; diff --git a/packages/registry/src/models/achievement.cairo b/packages/registry/src/models/achievement.cairo index 902c84d..09aca09 100644 --- a/packages/registry/src/models/achievement.cairo +++ b/packages/registry/src/models/achievement.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use registry::models::index::Achievement; use registry::constants; diff --git a/packages/registry/src/models/game.cairo b/packages/registry/src/models/game.cairo index 6e87282..ae5c6d9 100644 --- a/packages/registry/src/models/game.cairo +++ b/packages/registry/src/models/game.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use registry::constants; use registry::models::index::Game; diff --git a/packages/society/Scarb.toml b/packages/society/Scarb.toml index bf4a6fc..3a8e2fe 100644 --- a/packages/society/Scarb.toml +++ b/packages/society/Scarb.toml @@ -3,6 +3,7 @@ name = "society" version.workspace = true [dependencies] +registry = { path = "../registry" } dojo.workspace = true [dev-dependencies] diff --git a/packages/society/src/components/allianceable.cairo b/packages/society/src/components/allianceable.cairo new file mode 100644 index 0000000..31658b8 --- /dev/null +++ b/packages/society/src/components/allianceable.cairo @@ -0,0 +1,274 @@ +#[starknet::component] +mod AllianceableComponent { + // Dojo imports + + use dojo::world::WorldStorage; + use dojo::world::IWorldDispatcherTrait; + + // External imports + + use registry::types::metadata::MetadataTrait; + use registry::types::socials::SocialsTrait; + + // Internal imports + + use society::store::{Store, StoreTrait}; + use society::models::alliance::{Alliance, AllianceTrait, AllianceAssert}; + use society::models::guild::{Guild, GuildTrait, GuildAssert}; + use society::models::member::{Member, MemberTrait, MemberAssert}; + use society::types::role::Role; + + // Storage + + #[storage] + struct Storage {} + + // Events + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait { + fn create( + self: @ComponentState, + world: WorldStorage, + color: Option, + name: Option, + description: Option, + image: Option, + banner: Option, + discord: Option, + telegram: Option, + twitter: Option, + youtube: Option, + website: Option + ) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Member is a guild master + let member_id: felt252 = starknet::get_caller_address().into(); + let member = store.get_member(member_id); + member.assert_is_allowed(Role::Master); + + // [Check] Guild can join an alliance + let mut guild = store.get_guild(member.guild_id); + guild.assert_can_join(); + + // [Effect] Create an alliance + let alliance_id = world.dispatcher.uuid(); + let metadata = MetadataTrait::new(color, name, description, image, banner); + let socials = SocialsTrait::new(discord, telegram, twitter, youtube, website); + let mut alliance = AllianceTrait::new(alliance_id, metadata, socials); + + // [Effect] Guild joins alliance + guild.join(alliance_id); + alliance.hire(); + + // [Effect] Guild becomes alliance master + guild.crown(); + + // [Effect] Store entities + store.set_guild(@guild); + store.set_alliance(@alliance); + } + + fn open(self: @ComponentState, world: WorldStorage, free: bool) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Guild exists and is the alliance master + let guild = store.get_guild(member.guild_id); + guild.assert_is_allowed(Role::Master); + + // [Effect] Alliance opens + let mut alliance = store.get_alliance(guild.alliance_id); + alliance.open(free); + + // [Effect] Store entities + store.set_alliance(@alliance); + } + + fn close(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Guild exists and is the alliance master + let guild = store.get_guild(member.guild_id); + guild.assert_is_allowed(Role::Master); + + // [Effect] Alliance closes + let mut alliance = store.get_alliance(guild.alliance_id); + alliance.close(); + + // [Effect] Store entities + store.set_alliance(@alliance); + } + + fn crown(self: @ComponentState, world: WorldStorage, guild_id: u32) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Guild exists and is the alliance master + let mut master = store.get_guild(member.guild_id); + master.assert_is_allowed(Role::Master); + + // [Check] Guild is in the same alliance + let mut guild = store.get_guild(guild_id); + guild.assert_same_alliance(master.alliance_id); + + // [Effect] Transfer the master role + master.uncrown(); + guild.crown(); + + // [Effect] Store entities + store.set_guild(@master); + store.set_guild(@guild); + } + + fn hire(self: @ComponentState, world: WorldStorage, guild_id: u32) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Guild exists and is the alliance master + let master = store.get_guild(member.guild_id); + master.assert_is_allowed(Role::Master); + + // [Check] Alliance is open + let mut alliance = store.get_alliance(master.alliance_id); + alliance.assert_is_open(); + + // [Effect] Member joins the guild and guild hires a member + let mut guild = store.get_guild(guild_id); + guild.join(alliance.id); + alliance.hire(); + + // [Effect] Store entities + store.set_alliance(@alliance); + store.set_guild(@guild); + } + + fn fire(self: @ComponentState, world: WorldStorage, guild_id: u32) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Guild exists and is the alliance master + let master = store.get_guild(member.guild_id); + master.assert_is_allowed(Role::Master); + + // [Check] Master has authority over the guild + let mut guild = store.get_guild(guild_id); + master.assert_has_authority(guild.role.into()); + + // [Check] Guilds are in the same alliance + guild.assert_same_alliance(master.alliance_id); + + // [Effect] Alliance fire a guild + let mut alliance = store.get_alliance(master.alliance_id); + alliance.fire(); + + // [Effect] Guild leaves the alliance + guild.leave(); + + // [Effect] Store entities + store.set_alliance(@alliance); + store.set_guild(@guild); + } + + fn request(self: @ComponentState, world: WorldStorage, alliance_id: u32) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Check] Alliance exists + let mut alliance = store.get_alliance(alliance_id); + alliance.assert_does_exist(); + + // [Check] Alliance is open + alliance.assert_is_open(); + + // [Effect] Guild requests to join the alliance + let mut guild = store.get_guild(member.guild_id); + guild.request(alliance_id); + + // [Effect] Guild joins the alliance if it is free + if alliance.free { + // [Effect] Guild joins the alliance + guild.join(alliance_id); + // [Effect] Alliance hires a guild + alliance.hire(); + store.set_alliance(@alliance); + }; + + // [Effect] Store entities + store.set_guild(@guild); + } + + fn cancel(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Effect] Guild cancels the request + let mut guild = store.get_guild(member.guild_id); + guild.cancel(); + + // [Effect] Store entities + store.set_guild(@guild); + } + + fn leave(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let member = store.get_member(caller); + member.assert_is_allowed(Role::Master); + + // [Effect] Guild leaves the alliance + let mut guild = store.get_guild(member.guild_id); + guild.leave(); + + // [Effect] Store entities + store.set_guild(@guild); + } + } +} diff --git a/packages/society/src/components/friendable.cairo b/packages/society/src/components/followable.cairo similarity index 94% rename from packages/society/src/components/friendable.cairo rename to packages/society/src/components/followable.cairo index fcf5b21..8c37e24 100644 --- a/packages/society/src/components/friendable.cairo +++ b/packages/society/src/components/followable.cairo @@ -1,5 +1,5 @@ #[starknet::component] -mod FriendableComponent { +mod FollowableComponent { // Dojo imports use dojo::world::WorldStorage; diff --git a/packages/society/src/components/guildable.cairo b/packages/society/src/components/guildable.cairo index cb345f9..338187c 100644 --- a/packages/society/src/components/guildable.cairo +++ b/packages/society/src/components/guildable.cairo @@ -3,11 +3,19 @@ mod GuildableComponent { // Dojo imports use dojo::world::WorldStorage; + use dojo::world::IWorldDispatcherTrait; + + // External imports + + use registry::types::metadata::MetadataTrait; + use registry::types::socials::SocialsTrait; // Internal imports - use controller::store::{Store, StoreTrait}; - use controller::models::member::{Member, MemberTrait, MemberAssert}; + use society::store::{Store, StoreTrait}; + use society::models::guild::{Guild, GuildTrait, GuildAssert}; + use society::models::member::{Member, MemberTrait, MemberAssert}; + use society::types::role::Role; // Storage @@ -23,5 +31,247 @@ mod GuildableComponent { #[generate_trait] impl InternalImpl< TContractState, +HasComponent - > of InternalTrait {} + > of InternalTrait { + fn create( + self: @ComponentState, + world: WorldStorage, + color: Option, + name: Option, + description: Option, + image: Option, + banner: Option, + discord: Option, + telegram: Option, + twitter: Option, + youtube: Option, + website: Option + ) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Member does not belong to a guild + let member_id: felt252 = starknet::get_caller_address().into(); + let mut member = store.get_member(member_id); + member.assert_can_join(); + + // [Effect] Create a guild + let guild_id = world.dispatcher.uuid(); + let metadata = MetadataTrait::new(color, name, description, image, banner); + let socials = SocialsTrait::new(discord, telegram, twitter, youtube, website); + let mut guild = GuildTrait::new(guild_id, metadata, socials); + + // [Effect] Member joins guild + member.join(guild_id); + guild.hire(); + + // [Effect] Member becomes guild master + member.crown(); + + // [Effect] Store entities + store.set_member(@member); + store.set_guild(@guild); + } + + fn open(self: @ComponentState, world: WorldStorage, free: bool) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let admin = store.get_member(caller); + admin.assert_is_allowed(Role::Officer); + + // [Effect] Guild opens + let mut guild = store.get_guild(admin.guild_id); + guild.open(free); + + // [Effect] Store entities + store.set_guild(@guild); + } + + fn close(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let admin = store.get_member(caller); + admin.assert_is_allowed(Role::Officer); + + // [Effect] Guild closes + let mut guild = store.get_guild(admin.guild_id); + guild.close(); + + // [Effect] Store entities + store.set_guild(@guild); + } + + fn crown(self: @ComponentState, world: WorldStorage, member_id: felt252) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let mut master = store.get_member(caller); + master.assert_is_allowed(Role::Master); + + // [Check] Member is in the same guild + let mut member = store.get_member(member_id); + member.assert_same_guild(master.guild_id); + + // [Effect] Transfer the master role + master.uncrown(); + member.crown(); + + // [Effect] Store entities + store.set_member(@master); + store.set_member(@member); + } + + fn promote(self: @ComponentState, world: WorldStorage, member_id: felt252) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let mut admin = store.get_member(caller); + admin.assert_is_allowed(Role::Officer); + + // [Check] Member is in the same guild + let mut member = store.get_member(member_id); + member.assert_same_guild(admin.guild_id); + + // [Effect] Guild promotes a member + member.promote(); + + // [Effect] Store entities + store.set_member(@member); + } + + fn demote(self: @ComponentState, world: WorldStorage, member_id: felt252) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let mut master = store.get_member(caller); + master.assert_is_allowed(Role::Master); + + // [Check] Member is in the same guild + let mut member = store.get_member(member_id); + member.assert_same_guild(master.guild_id); + + // [Effect] Guild demotes a member + member.demote(); + + // [Effect] Store entities + store.set_member(@member); + } + + fn hire(self: @ComponentState, world: WorldStorage, member_id: felt252) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let mut admin = store.get_member(caller); + admin.assert_is_allowed(Role::Officer); + + // [Check] Guild is open + let mut guild = store.get_guild(admin.guild_id); + guild.assert_is_open(); + + // [Effect] Member joins the guild and guild hires a member + let mut member = store.get_member(member_id); + member.join(guild.id); + guild.hire(); + + // [Effect] Store entities + store.set_guild(@guild); + store.set_member(@member); + } + + fn fire(self: @ComponentState, world: WorldStorage, member_id: felt252) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Caller exists and is allowed + let caller = starknet::get_caller_address().into(); + let mut admin = store.get_member(caller); + admin.assert_is_allowed(Role::Officer); + + // [Check] Admin has authority over the member + let mut member = store.get_member(member_id); + admin.assert_has_authority(member.role.into()); + + // [Check] Members are in the same guild + member.assert_same_guild(admin.guild_id); + + // [Effect] Guild fire a member + let mut guild = store.get_guild(admin.guild_id); + guild.fire(); + + // [Effect] Member leaves the guild + member.leave(); + + // [Effect] Store entities + store.set_guild(@guild); + store.set_member(@member); + } + + fn request(self: @ComponentState, world: WorldStorage, guild_id: u32) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Check] Guild exists + let mut guild = store.get_guild(guild_id); + guild.assert_does_exist(); + + // [Check] Guild is open + guild.assert_is_open(); + + // [Effect] Member requests to join the guild + let caller = starknet::get_caller_address().into(); + let mut member = store.get_member(caller); + member.request(guild_id); + + // [Effect] Member joins the guild if it is free + if guild.free { + // [Effect] Member joins the guild + member.join(guild_id); + // [Effect] Guild hires a member + guild.hire(); + store.set_guild(@guild); + }; + + // [Effect] Store entities + store.set_member(@member); + } + + fn cancel(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Effect] Member cancels the request + let caller = starknet::get_caller_address().into(); + let mut member = store.get_member(caller); + member.cancel(); + + // [Effect] Store entities + store.set_member(@member); + } + + fn leave(self: @ComponentState, world: WorldStorage) { + // [Setup] Datastore + let mut store = StoreTrait::new(world); + + // [Effect] Member leaves the guild + let caller = starknet::get_caller_address().into(); + let mut member = store.get_member(caller); + member.leave(); + + // [Effect] Store entities + store.set_member(@member); + } + } } diff --git a/packages/society/src/constants.cairo b/packages/society/src/constants.cairo index 8b13789..3259fe6 100644 --- a/packages/society/src/constants.cairo +++ b/packages/society/src/constants.cairo @@ -1 +1,4 @@ +// Guilds and Alliances +pub const MAX_MEMBER_COUNT: u8 = 100; +pub const MAX_GUILD_COUNT: u8 = 10; diff --git a/packages/society/src/events/index.cairo b/packages/society/src/events/index.cairo index f4e7f5f..c10279e 100644 --- a/packages/society/src/events/index.cairo +++ b/packages/society/src/events/index.cairo @@ -2,9 +2,9 @@ #[derive(Copy, Drop, Serde)] #[dojo::event] -pub struct Friend { +pub struct Follow { #[key] account_id: felt252, - friend_id: felt252, + follower_id: felt252, time: u64, } diff --git a/packages/society/src/lib.cairo b/packages/society/src/lib.cairo index 8b687b5..2a85d21 100644 --- a/packages/society/src/lib.cairo +++ b/packages/society/src/lib.cairo @@ -8,9 +8,12 @@ mod types { mod models { mod index; mod member; + mod guild; + mod alliance; } mod components { - mod friendable; + mod followable; mod guildable; + mod allianceable; } diff --git a/packages/society/src/models/alliance.cairo b/packages/society/src/models/alliance.cairo new file mode 100644 index 0000000..aae5456 --- /dev/null +++ b/packages/society/src/models/alliance.cairo @@ -0,0 +1,132 @@ +// External imports + +use registry::types::metadata::Metadata; +use registry::types::socials::Socials; +use registry::helpers::json::JsonifiableTrait; + +// Internal imports + +use society::constants::MAX_GUILD_COUNT; +use society::models::index::Alliance; +use society::types::role::Role; + +// Errors + +pub mod errors { + pub const ALLIANCE_ALREADY_EXISTS: felt252 = 'Alliance: already exists'; + pub const ALLIANCE_NOT_EXIST: felt252 = 'Alliance: does not exist'; + pub const ALLIANCE_CANNOT_HIRE: felt252 = 'Alliance: cannot hire'; + pub const ALLIANCE_CANNOT_FIRE: felt252 = 'Alliance: cannot fire'; + pub const ALLIANCE_IS_OPEN: felt252 = 'Alliance: is open'; + pub const ALLIANCE_IS_CLOSE: felt252 = 'Alliance: is close'; + pub const ALLIANCE_CANNOT_JOIN: felt252 = 'Alliance: cannot join'; + pub const ALLIANCE_CANNOT_LEAVE: felt252 = 'Alliance: cannot leave'; + pub const ALLIANCE_CANNOT_CROWN: felt252 = 'Alliance: cannot crown'; + pub const ALLIANCE_CANNOT_UNCROWN: felt252 = 'Alliance: cannot un-crown'; + pub const ALLIANCE_CANNOT_REQUEST: felt252 = 'Alliance: cannot request'; + pub const ALLIANCE_CANNOT_CANCEL: felt252 = 'Alliance: cannot cancel'; +} + +#[generate_trait] +impl AllianceImpl of AllianceTrait { + #[inline] + fn new(id: u32, metadata: Metadata, socials: Socials) -> Alliance { + Alliance { + id: id, + open: false, + free: false, + guild_count: 0, + metadata: metadata.jsonify(), + socials: socials.jsonify(), + } + } + + #[inline] + fn open(ref self: Alliance, free: bool) { + // [Check] Alliance can be opened + self.assert_is_close(); + // [Update] Alliance + self.open = true; + self.free = free; + } + + #[inline] + fn close(ref self: Alliance) { + // [Check] Alliance can be closed + self.assert_is_open(); + // [Update] Alliance + self.open = false; + self.free = false; + } + + #[inline] + fn hire(ref self: Alliance) { + // [Check] Alliance can be hired + self.assert_can_hire(); + // [Update] Alliance + self.guild_count += 1; + } + + #[inline] + fn fire(ref self: Alliance) { + // [Check] Alliance can be fired + self.assert_can_fire(); + // [Update] Alliance + self.guild_count -= 1; + } +} + +#[generate_trait] +impl AllianceAssert of AssertTrait { + #[inline] + fn assert_does_not_exist(self: @Alliance) { + assert(*self.guild_count == 0, errors::ALLIANCE_ALREADY_EXISTS); + } + + #[inline] + fn assert_does_exist(self: @Alliance) { + assert(*self.guild_count != 0, errors::ALLIANCE_NOT_EXIST); + } + + #[inline] + fn assert_is_open(self: @Alliance) { + assert(*self.open, errors::ALLIANCE_IS_CLOSE); + } + + #[inline] + fn assert_is_close(self: @Alliance) { + assert(!*self.open, errors::ALLIANCE_IS_OPEN); + } + + #[inline] + fn assert_can_hire(self: @Alliance) { + assert(*self.guild_count < MAX_GUILD_COUNT, errors::ALLIANCE_CANNOT_HIRE); + } + + #[inline] + fn assert_can_fire(self: @Alliance) { + assert(*self.guild_count > 0, errors::ALLIANCE_CANNOT_FIRE); + } +} + +#[cfg(test)] +mod tests { + // Local imports + + use super::{Alliance, AllianceTrait, AllianceAssert, Role, Metadata, Socials}; + + // Constants + + const ALLIANCE_ID: u32 = 42; + + #[test] + fn test_alliance_new() { + let metadata = core::Default::default(); + let socials = core::Default::default(); + let guild = AllianceTrait::new(ALLIANCE_ID, metadata, socials); + assert_eq!(guild.id, ALLIANCE_ID); + assert_eq!(guild.open, false); + assert_eq!(guild.free, false); + assert_eq!(guild.guild_count, 0); + } +} diff --git a/packages/society/src/models/guild.cairo b/packages/society/src/models/guild.cairo new file mode 100644 index 0000000..25ebeef --- /dev/null +++ b/packages/society/src/models/guild.cairo @@ -0,0 +1,244 @@ +// External imports + +use registry::types::metadata::Metadata; +use registry::types::socials::Socials; +use registry::helpers::json::JsonifiableTrait; + +// Internal imports + +use society::constants::MAX_MEMBER_COUNT; +use society::models::index::Guild; +use society::types::role::Role; + +// Errors + +pub mod errors { + pub const GUILD_ALREADY_EXISTS: felt252 = 'Guild: already exists'; + pub const GUILD_NOT_EXIST: felt252 = 'Guild: does not exist'; + pub const GUILD_CANNOT_HIRE: felt252 = 'Guild: cannot hire'; + pub const GUILD_CANNOT_FIRE: felt252 = 'Guild: cannot fire'; + pub const GUILD_IS_OPEN: felt252 = 'Guild: is open'; + pub const GUILD_IS_CLOSE: felt252 = 'Guild: is close'; + pub const GUILD_CANNOT_JOIN: felt252 = 'Guild: cannot join'; + pub const GUILD_CANNOT_LEAVE: felt252 = 'Guild: cannot leave'; + pub const GUILD_CANNOT_CROWN: felt252 = 'Guild: cannot crown'; + pub const GUILD_CANNOT_UNCROWN: felt252 = 'Guild: cannot un-crown'; + pub const GUILD_CANNOT_REQUEST: felt252 = 'Guild: cannot request'; + pub const GUILD_CANNOT_CANCEL: felt252 = 'Guild: cannot cancel'; + pub const GUILD_NOT_A_REQUESTER: felt252 = 'Guild: not a requester'; + pub const GUILD_NOT_IN_ALLIANCE: felt252 = 'Guild: not in alliance'; + pub const GUILD_NOT_ALLOWED: felt252 = 'Guild: not allowed'; + pub const GUILD_NOT_AUTHORIZED: felt252 = 'Guild: not authorized'; +} + +#[generate_trait] +impl GuildImpl of GuildTrait { + #[inline] + fn new(id: u32, metadata: Metadata, socials: Socials) -> Guild { + Guild { + id: id, + open: false, + free: false, + role: Role::None.into(), + member_count: 0, + alliance_id: 0, + pending_alliance_id: 0, + metadata: metadata.jsonify(), + socials: socials.jsonify(), + } + } + + #[inline] + fn open(ref self: Guild, free: bool) { + // [Check] Guild can be opened + self.assert_is_close(); + // [Update] Guild + self.open = true; + self.free = free; + } + + #[inline] + fn close(ref self: Guild) { + // [Check] Guild can be closed + self.assert_is_open(); + // [Update] Guild + self.open = false; + self.free = false; + } + + #[inline] + fn hire(ref self: Guild) { + // [Check] Guild can be hired + self.assert_can_hire(); + // [Update] Guild + self.member_count += 1; + } + + #[inline] + fn fire(ref self: Guild) { + // [Check] Guild can be fired + self.assert_can_fire(); + // [Update] Guild + self.member_count -= 1; + } + + #[inline] + fn join(ref self: Guild, alliance_id: u32) { + // [Check] Guild can join + self.assert_can_join(); + self.assert_is_requester(alliance_id); + // [Update] Guild + self.alliance_id = alliance_id; + self.pending_alliance_id = 0; + self.role = Role::Member.into(); + } + + #[inline] + fn leave(ref self: Guild) { + // [Check] Guild can leave + self.assert_can_leave(); + // [Update] Guild + self.alliance_id = 0; + self.role = Role::None.into(); + } + + #[inline] + fn crown(ref self: Guild) { + // [Check] Guild can be crowned + self.assert_is_crownable(); + // [Update] Guild + self.role = Role::Master.into(); + } + + #[inline] + fn uncrown(ref self: Guild) { + // [Check] Guild can be un-crowned + self.assert_is_uncrownable(); + // [Update] Guild + self.role = Role::Member.into(); + } + + #[inline] + fn request(ref self: Guild, alliance_id: u32) { + // [Check] Guild can request + self.assert_can_request(); + // [Update] Guild + self.pending_alliance_id = alliance_id; + } + + #[inline] + fn cancel(ref self: Guild) { + // [Check] Guild can cancel + self.assert_can_cancel(); + // [Update] Guild + self.pending_alliance_id = 0; + } +} + +#[generate_trait] +impl GuildAssert of AssertTrait { + #[inline] + fn assert_does_not_exist(self: @Guild) { + assert(*self.member_count == 0, errors::GUILD_ALREADY_EXISTS); + } + + #[inline] + fn assert_does_exist(self: @Guild) { + assert(*self.member_count != 0, errors::GUILD_NOT_EXIST); + } + + #[inline] + fn assert_is_open(self: @Guild) { + assert(*self.open, errors::GUILD_IS_CLOSE); + } + + #[inline] + fn assert_is_close(self: @Guild) { + assert(!*self.open, errors::GUILD_IS_OPEN); + } + + #[inline] + fn assert_can_hire(self: @Guild) { + assert(*self.member_count < MAX_MEMBER_COUNT, errors::GUILD_CANNOT_HIRE); + } + + #[inline] + fn assert_can_fire(self: @Guild) { + assert(*self.member_count > 0, errors::GUILD_CANNOT_FIRE); + } + + #[inline] + fn assert_can_join(self: @Guild) { + assert(self.alliance_id == @0, errors::GUILD_CANNOT_JOIN); + } + + #[inline] + fn assert_can_leave(self: @Guild) { + assert(self.alliance_id == @0, errors::GUILD_CANNOT_LEAVE); + } + + #[inline] + fn assert_is_crownable(self: @Guild) { + assert(self.role == @Role::Member.into(), errors::GUILD_CANNOT_CROWN); + } + + #[inline] + fn assert_is_uncrownable(self: @Guild) { + assert(self.role == @Role::Master.into(), errors::GUILD_CANNOT_UNCROWN); + } + + #[inline] + fn assert_can_request(self: @Guild) { + assert(*self.pending_alliance_id + *self.alliance_id == 0, errors::GUILD_CANNOT_REQUEST); + } + + #[inline] + fn assert_is_requester(self: @Guild, alliance_id: u32) { + assert(*self.pending_alliance_id == alliance_id, errors::GUILD_NOT_A_REQUESTER); + } + + #[inline] + fn assert_can_cancel(self: @Guild) { + assert(*self.pending_alliance_id != 0, errors::GUILD_CANNOT_CANCEL); + } + + #[inline] + fn assert_same_alliance(self: @Guild, alliance_id: u32) { + assert(*self.alliance_id == alliance_id, errors::GUILD_NOT_IN_ALLIANCE); + } + + #[inline] + fn assert_is_allowed(self: @Guild, role: Role) { + assert(*self.role >= role.into(), errors::GUILD_NOT_ALLOWED); + } + + #[inline] + fn assert_has_authority(self: @Guild, role: Role) { + assert(*self.role > role.into(), errors::GUILD_NOT_AUTHORIZED); + } +} + +#[cfg(test)] +mod tests { + // Local imports + + use super::{Guild, GuildTrait, GuildAssert, Role, Metadata, Socials}; + + // Constants + + const GUILD_ID: u32 = 42; + + #[test] + fn test_guild_new() { + let metadata = core::Default::default(); + let socials = core::Default::default(); + let guild = GuildTrait::new(GUILD_ID, metadata, socials); + assert_eq!(guild.id, GUILD_ID); + assert_eq!(guild.open, false); + assert_eq!(guild.free, false); + assert_eq!(guild.role, Role::None.into()); + assert_eq!(guild.member_count, 0); + assert_eq!(guild.alliance_id, 0); + assert_eq!(guild.pending_alliance_id, 0); + } +} diff --git a/packages/society/src/models/index.cairo b/packages/society/src/models/index.cairo index e56b22a..bde920a 100644 --- a/packages/society/src/models/index.cairo +++ b/packages/society/src/models/index.cairo @@ -7,7 +7,7 @@ pub struct Member { id: felt252, role: u8, guild_id: u32, - request_count: u32, + pending_guild_id: u32, } #[derive(Clone, Drop, Serde)] @@ -17,10 +17,10 @@ pub struct Guild { id: u32, open: bool, free: bool, - member_count: u32, - missive_count: u32, + role: u8, + member_count: u8, alliance_id: u32, - owner: felt252, + pending_alliance_id: u32, metadata: ByteArray, socials: ByteArray, } @@ -29,31 +29,10 @@ pub struct Guild { #[dojo::model] pub struct Alliance { #[key] - id: felt252, + id: u32, open: bool, free: bool, - guild_count: u32, - owner: u32, + guild_count: u8, metadata: ByteArray, socials: ByteArray, } - -#[derive(Clone, Drop, Serde)] -#[dojo::model] -pub struct Missive { - #[key] - id: u32, - guild_id: u32, - alliance_id: u32, - content: ByteArray, -} - -#[derive(Clone, Drop, Serde)] -#[dojo::model] -pub struct Request { - #[key] - id: u32, - account_id: felt252, - guild_id: u32, - content: ByteArray, -} diff --git a/packages/society/src/models/member.cairo b/packages/society/src/models/member.cairo index 43badef..e3bc9f7 100644 --- a/packages/society/src/models/member.cairo +++ b/packages/society/src/models/member.cairo @@ -1,4 +1,4 @@ -// Intenral imports +// Internal imports use society::models::index::Member; use society::types::role::Role; @@ -8,27 +8,95 @@ use society::types::role::Role; pub mod errors { pub const MEMBER_ALREADY_EXISTS: felt252 = 'Member: already exists'; pub const MEMBER_NOT_EXIST: felt252 = 'Member: does not exist'; - pub const MEMBER_INVALID_ACCOUNT_ID: felt252 = 'Member: invalid account id'; - pub const MEMBER_ALREADY_HIRED: felt252 = 'Member: already hired'; + pub const MEMBER_CANNOT_JOIN: felt252 = 'Member: cannot join'; + pub const MEMBER_CANNOT_LEAVE: felt252 = 'Member: cannot leave'; + pub const MEMBER_CANNOT_PROMOTE: felt252 = 'Member: cannot be promoted'; + pub const MEMBER_CANNOT_DEMOTE: felt252 = 'Member: cannot be demoted'; + pub const MEMBER_CANNOT_CROWN: felt252 = 'Member: cannot be crowned'; + pub const MEMBER_CANNOT_UNCROWN: felt252 = 'Member: cannot be un-crowned'; + pub const MEMBER_CANNOT_REQUEST: felt252 = 'Member: cannot request'; + pub const MEMBER_CANNOT_CANCEL: felt252 = 'Member: cannot cancel'; + pub const MEMBER_NOT_ALLOWED: felt252 = 'Member: not allowed'; + pub const MEMBER_NOT_A_REQUESTER: felt252 = 'Member: not a requester'; + pub const MEMBER_NOT_AUTHORIZED: felt252 = 'Member: not authorized'; + pub const MEMBER_NOT_IN_GUILD: felt252 = 'Member: not in guild'; } #[generate_trait] impl MemberImpl of MemberTrait { #[inline] - fn new(account_id: felt252) -> Member { - // [Check] Inputs - MemberAssert::assert_valid_account_id(account_id); + fn new(id: felt252) -> Member { // [Return] Member let role = Role::None; - Member { id: account_id, role: role.into(), guild_id: 0, request_count: 0 } + Member { id: id, role: role.into(), guild_id: 0, pending_guild_id: 0 } } #[inline] - fn hire(ref self: Member, guild_id: u32) { - // [Check] Member can be hired - self.assert_is_hireable(); + fn join(ref self: Member, guild_id: u32) { + // [Check] Member can join + self.assert_can_join(); + self.assert_is_requester(guild_id); // [Update] Member self.guild_id = guild_id; + self.pending_guild_id = 0; + self.role = Role::Member.into(); + } + + #[inline] + fn leave(ref self: Member) { + // [Check] Member can leave + self.assert_can_leave(); + // [Update] Member + self.guild_id = 0; + self.role = Role::None.into(); + } + + #[inline] + fn crown(ref self: Member) { + // [Check] Member can be crowned + self.assert_is_crownable(); + // [Update] Member + self.role = Role::Master.into(); + } + + #[inline] + fn uncrown(ref self: Member) { + // [Check] Member can be un-crowned + self.assert_is_uncrownable(); + // [Update] Member + self.role = Role::Officer.into(); + } + + #[inline] + fn promote(ref self: Member) { + // [Check] Member can be promoted + self.assert_is_promotable(); + // [Update] Member + self.role = Role::Officer.into(); + } + + #[inline] + fn demote(ref self: Member) { + // [Check] Member can be demoted + self.assert_is_demotable(); + // [Update] Member + self.role = Role::Member.into(); + } + + #[inline] + fn request(ref self: Member, guild_id: u32) { + // [Check] Member can request + self.assert_can_request(); + // [Update] Member + self.pending_guild_id = guild_id; + } + + #[inline] + fn cancel(ref self: Member) { + // [Check] Member can cancel + self.assert_can_cancel(); + // [Update] Member + self.pending_guild_id = 0; } } @@ -36,22 +104,77 @@ impl MemberImpl of MemberTrait { impl MemberAssert of AssertTrait { #[inline] fn assert_does_not_exist(self: @Member) { - assert(self.role == @Role::None.into(), errors::MEMBER_ALREADY_EXISTS); + assert(*self.guild_id + *self.pending_guild_id == 0, errors::MEMBER_ALREADY_EXISTS); } #[inline] fn assert_does_exist(self: @Member) { - assert(self.role != @Role::None.into(), errors::MEMBER_NOT_EXIST); + assert(*self.guild_id + *self.pending_guild_id != 0, errors::MEMBER_NOT_EXIST); + } + + #[inline] + fn assert_can_join(self: @Member) { + assert(*self.guild_id == 0 && *self.pending_guild_id != 0, errors::MEMBER_CANNOT_JOIN); + } + + #[inline] + fn assert_can_leave(self: @Member) { + assert( + *self.guild_id != 0 && *self.role != Role::Master.into(), errors::MEMBER_CANNOT_LEAVE + ); + } + + #[inline] + fn assert_is_promotable(self: @Member) { + assert(self.role == @Role::Member.into(), errors::MEMBER_CANNOT_PROMOTE); + } + + #[inline] + fn assert_is_demotable(self: @Member) { + assert(self.role == @Role::Officer.into(), errors::MEMBER_CANNOT_DEMOTE); + } + + #[inline] + fn assert_is_crownable(self: @Member) { + assert( + self.role == @Role::Member.into() || self.role == @Role::Officer.into(), + errors::MEMBER_CANNOT_CROWN + ); + } + + #[inline] + fn assert_is_uncrownable(self: @Member) { + assert(self.role == @Role::Master.into(), errors::MEMBER_CANNOT_UNCROWN); + } + + #[inline] + fn assert_can_request(self: @Member) { + assert(*self.pending_guild_id + *self.guild_id == 0, errors::MEMBER_CANNOT_REQUEST); + } + + #[inline] + fn assert_can_cancel(self: @Member) { + assert(*self.pending_guild_id != 0, errors::MEMBER_CANNOT_CANCEL); + } + + #[inline] + fn assert_is_allowed(self: @Member, role: Role) { + assert(*self.role >= role.into(), errors::MEMBER_NOT_ALLOWED); + } + + #[inline] + fn assert_is_requester(self: @Member, guild_id: u32) { + assert(*self.pending_guild_id != guild_id, errors::MEMBER_NOT_A_REQUESTER); } #[inline] - fn assert_valid_account_id(account_id: felt252) { - assert(account_id != 0, errors::MEMBER_INVALID_ACCOUNT_ID); + fn assert_has_authority(self: @Member, role: Role) { + assert(*self.role > role.into(), errors::MEMBER_NOT_AUTHORIZED); } #[inline] - fn assert_is_hireable(self: @Member) { - assert(self.guild_id == @0, errors::MEMBER_ALREADY_HIRED); + fn assert_same_guild(self: @Member, guild_id: u32) { + assert(*self.guild_id == guild_id, errors::MEMBER_NOT_IN_GUILD); } } @@ -67,7 +190,7 @@ mod tests { const GUILD_ID: u32 = 42; #[test] - fn test_deployment_new() { + fn test_member_new() { let member = MemberTrait::new(ACCOUNT_ID); assert_eq!(member.id, ACCOUNT_ID); assert_eq!(member.guild_id, 0); diff --git a/packages/society/src/store.cairo b/packages/society/src/store.cairo index 96473e7..9573421 100644 --- a/packages/society/src/store.cairo +++ b/packages/society/src/store.cairo @@ -11,11 +11,9 @@ use dojo::model::ModelStorage; // Models imports -use controller::models::account::Account; -use controller::models::controller::Controller; -use controller::models::member::Member; -use controller::models::signer::Signer; -use controller::models::team::Team; +use society::models::alliance::Alliance; +use society::models::guild::Guild; +use society::models::member::Member; // Structs @@ -35,47 +33,32 @@ impl StoreImpl of StoreTrait { } #[inline] - fn get_account(self: Store, account_id: felt252) -> Account { - self.world.read_model(account_id) + fn get_alliance(self: Store, alliance_id: u32) -> Alliance { + self.world.read_model(alliance_id) } #[inline] - fn get_controller(self: Store, controller_id: felt252) -> Controller { - self.world.read_model(controller_id) + fn get_guild(self: Store, guild_id: u32) -> Guild { + self.world.read_model(guild_id) } #[inline] - fn get_member(self: Store, account_id: felt252, team_id: felt252) -> Member { - self.world.read_model((account_id, team_id)) + fn get_member(self: Store, member_id: felt252) -> Member { + self.world.read_model(member_id) } #[inline] - fn get_signer(self: Store, signer_id: felt252) -> Signer { - self.world.read_model(signer_id) + fn set_alliance(ref self: Store, alliance: @Alliance) { + self.world.write_model(alliance); } #[inline] - fn get_team(self: Store, team_id: felt252) -> Team { - self.world.read_model(team_id) - } - - #[inline] - fn set_account(ref self: Store, account: @Account) { - self.world.write_model(account); - } - - #[inline] - fn set_controller(ref self: Store, controller: @Controller) { - self.world.write_model(controller); + fn set_guild(ref self: Store, guild: @Guild) { + self.world.write_model(guild); } #[inline] fn set_member(ref self: Store, member: @Member) { self.world.write_model(member); } - - #[inline] - fn set_team(ref self: Store, team: @Team) { - self.world.write_model(team); - } }