diff --git a/Scarb.lock b/Scarb.lock index 08088cd..8f207c1 100644 --- a/Scarb.lock +++ b/Scarb.lock @@ -53,3 +53,11 @@ dependencies = [ "dojo", "dojo_cairo_test", ] + +[[package]] +name = "society" +version = "0.0.0" +dependencies = [ + "dojo", + "dojo_cairo_test", +] diff --git a/Scarb.toml b/Scarb.toml index 808b9a2..13ec574 100644 --- a/Scarb.toml +++ b/Scarb.toml @@ -1,9 +1,10 @@ [workspace] members = [ - "packages/trophy", - "packages/registry", - "packages/provider", "packages/controller", + "packages/provider", + "packages/registry", + "packages/society", + "packages/trophy", ] description = "Dojo achievement library" homepage = "https://github.com/cartridge-gg/arcade/" diff --git a/packages/society/README.md b/packages/society/README.md new file mode 100644 index 0000000..e69de29 diff --git a/packages/society/Scarb.toml b/packages/society/Scarb.toml new file mode 100644 index 0000000..bf4a6fc --- /dev/null +++ b/packages/society/Scarb.toml @@ -0,0 +1,9 @@ +[package] +name = "society" +version.workspace = true + +[dependencies] +dojo.workspace = true + +[dev-dependencies] +dojo_cairo_test.workspace = true diff --git a/packages/society/src/components/friendable.cairo b/packages/society/src/components/friendable.cairo new file mode 100644 index 0000000..fcf5b21 --- /dev/null +++ b/packages/society/src/components/friendable.cairo @@ -0,0 +1,26 @@ +#[starknet::component] +mod FriendableComponent { + // Dojo imports + + use dojo::world::WorldStorage; + + // Internal imports + + use society::store::{Store, StoreTrait}; + + // Storage + + #[storage] + struct Storage {} + + // Events + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait {} +} diff --git a/packages/society/src/components/guildable.cairo b/packages/society/src/components/guildable.cairo new file mode 100644 index 0000000..cb345f9 --- /dev/null +++ b/packages/society/src/components/guildable.cairo @@ -0,0 +1,27 @@ +#[starknet::component] +mod GuildableComponent { + // Dojo imports + + use dojo::world::WorldStorage; + + // Internal imports + + use controller::store::{Store, StoreTrait}; + use controller::models::member::{Member, MemberTrait, MemberAssert}; + + // Storage + + #[storage] + struct Storage {} + + // Events + + #[event] + #[derive(Drop, starknet::Event)] + enum Event {} + + #[generate_trait] + impl InternalImpl< + TContractState, +HasComponent + > of InternalTrait {} +} diff --git a/packages/society/src/constants.cairo b/packages/society/src/constants.cairo new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/packages/society/src/constants.cairo @@ -0,0 +1 @@ + diff --git a/packages/society/src/events/index.cairo b/packages/society/src/events/index.cairo new file mode 100644 index 0000000..f4e7f5f --- /dev/null +++ b/packages/society/src/events/index.cairo @@ -0,0 +1,10 @@ +//! Events + +#[derive(Copy, Drop, Serde)] +#[dojo::event] +pub struct Friend { + #[key] + account_id: felt252, + friend_id: felt252, + time: u64, +} diff --git a/packages/society/src/lib.cairo b/packages/society/src/lib.cairo new file mode 100644 index 0000000..8b687b5 --- /dev/null +++ b/packages/society/src/lib.cairo @@ -0,0 +1,16 @@ +mod constants; +mod store; + +mod types { + mod role; +} + +mod models { + mod index; + mod member; +} + +mod components { + mod friendable; + mod guildable; +} diff --git a/packages/society/src/models/index.cairo b/packages/society/src/models/index.cairo new file mode 100644 index 0000000..e56b22a --- /dev/null +++ b/packages/society/src/models/index.cairo @@ -0,0 +1,59 @@ +//! Models + +#[derive(Copy, Drop, Serde)] +#[dojo::model] +pub struct Member { + #[key] + id: felt252, + role: u8, + guild_id: u32, + request_count: u32, +} + +#[derive(Clone, Drop, Serde)] +#[dojo::model] +pub struct Guild { + #[key] + id: u32, + open: bool, + free: bool, + member_count: u32, + missive_count: u32, + alliance_id: u32, + owner: felt252, + metadata: ByteArray, + socials: ByteArray, +} + +#[derive(Clone, Drop, Serde)] +#[dojo::model] +pub struct Alliance { + #[key] + id: felt252, + open: bool, + free: bool, + guild_count: u32, + owner: u32, + 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 new file mode 100644 index 0000000..43badef --- /dev/null +++ b/packages/society/src/models/member.cairo @@ -0,0 +1,76 @@ +// Intenral imports + +use society::models::index::Member; +use society::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_ALREADY_HIRED: felt252 = 'Member: already hired'; +} + +#[generate_trait] +impl MemberImpl of MemberTrait { + #[inline] + fn new(account_id: felt252) -> Member { + // [Check] Inputs + MemberAssert::assert_valid_account_id(account_id); + // [Return] Member + let role = Role::None; + Member { id: account_id, role: role.into(), guild_id: 0, request_count: 0 } + } + + #[inline] + fn hire(ref self: Member, guild_id: u32) { + // [Check] Member can be hired + self.assert_is_hireable(); + // [Update] Member + self.guild_id = guild_id; + } +} + +#[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_is_hireable(self: @Member) { + assert(self.guild_id == @0, errors::MEMBER_ALREADY_HIRED); + } +} + +#[cfg(test)] +mod tests { + // Local imports + + use super::{Member, MemberTrait, MemberAssert, Role}; + + // Constants + + const ACCOUNT_ID: felt252 = 'ACCOUNT_ID'; + const GUILD_ID: u32 = 42; + + #[test] + fn test_deployment_new() { + let member = MemberTrait::new(ACCOUNT_ID); + assert_eq!(member.id, ACCOUNT_ID); + assert_eq!(member.guild_id, 0); + assert_eq!(member.role, Role::None.into()); + } +} diff --git a/packages/society/src/store.cairo b/packages/society/src/store.cairo new file mode 100644 index 0000000..96473e7 --- /dev/null +++ b/packages/society/src/store.cairo @@ -0,0 +1,81 @@ +//! Store struct and component management methods. + +// Starknet imports + +use starknet::SyscallResultTrait; + +// Dojo imports + +use dojo::world::WorldStorage; +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; + + +// Structs + +#[derive(Copy, Drop)] +struct Store { + world: WorldStorage, +} + +// Implementations + +#[generate_trait] +impl StoreImpl of StoreTrait { + #[inline] + fn new(world: WorldStorage) -> Store { + Store { world: world } + } + + #[inline] + fn get_account(self: Store, account_id: felt252) -> Account { + self.world.read_model(account_id) + } + + #[inline] + fn get_controller(self: Store, controller_id: felt252) -> Controller { + 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); + } + + #[inline] + 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/society/src/types/role.cairo b/packages/society/src/types/role.cairo new file mode 100644 index 0000000..bd64fb6 --- /dev/null +++ b/packages/society/src/types/role.cairo @@ -0,0 +1,34 @@ +#[derive(Copy, Drop, PartialEq)] +pub enum Role { + None, + Member, + Officer, + Master, +} + +// Implementations + +impl IntoRoleU8 of core::Into { + #[inline] + fn into(self: Role) -> u8 { + match self { + Role::None => 0, + Role::Member => 1, + Role::Officer => 2, + Role::Master => 3, + } + } +} + +impl IntoU8Role of core::Into { + #[inline] + fn into(self: u8) -> Role { + match self { + 0 => Role::None, + 1 => Role::Member, + 2 => Role::Officer, + 3 => Role::Master, + _ => Role::None, + } + } +}