Files
pezkuwi-sdk/pezcumulus/teyrchains/pezpallets/tiki/src/lib.rs
T
pezkuwichain 4c8f281051 style: Migrate to stable-only rustfmt configuration
- Remove nightly-only features from .rustfmt.toml and vendor/ss58-registry/rustfmt.toml
- Removed features: imports_granularity, wrap_comments, comment_width,
  reorder_impl_items, spaces_around_ranges, binop_separator,
  match_arm_blocks, trailing_semicolon, trailing_comma
- Format all 898 affected files with stable rustfmt
- Ensures long-term reliability without nightly toolchain dependency
2025-12-23 09:37:11 +03:00

867 lines
26 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#![cfg_attr(not(feature = "std"), no_std)]
//! # Tiki (Role) Pezpallet
//!
//! A pezpallet for managing citizenship and role-based NFTs with automated and governance-driven
//! assignment.
//!
//! ## Overview
//!
//! The Tiki pezpallet implements a comprehensive role management system using non-transferable NFTs
//! to represent citizenship status and various roles within the ecosystem. Each role grants
//! specific permissions, rights, and social standing.
//!
//! ## Core Concepts
//!
//! ### Citizenship NFT
//! - Automatically minted upon KYC approval
//! - Represents "Welati" (Citizen) status
//! - Non-transferable and permanent
//! - Required prerequisite for all other roles
//!
//! ### Role Types (Tiki)
//!
//! Roles are assigned through different mechanisms:
//!
//! 1. **Automatic** - System-assigned upon conditions (e.g., Citizenship after KYC)
//! 2. **Appointed** - Admin-assigned governmental positions (e.g., Ministers, Judges)
//! 3. **Elected** - Community-voted positions (e.g., Parliament members)
//! 4. **Earned** - Achievement-based roles (e.g., Educator, Expert)
//!
//! ### Role Categories
//!
//! - **Governance**: Serok (President), SerokWeziran (Prime Minister), Ministers
//! - **Judicial**: Dadger (Judge), Dozger (Prosecutor), Hiquqnas (Lawyer)
//! - **Administrative**: Qeydkar (Registrar), Xezinedar (Treasurer), OperatorêTorê (Network
//! Operator)
//! - **Educational**: Mamoste (Teacher), Perwerdekar (Educator), Rewsenbîr (Intellectual)
//! - **Economic**: Bazargan (Merchant), Navbeynkar (Mediator)
//! - **Community**: Parlementer (Parliament Member), ModeratorêCivakê (Community Moderator)
//! - **Expert**: Axa (Elder/Expert), Pêseng (Pioneer), Hekem (Wise), Sêwirmend (Counselor)
//!
//! ## NFT Implementation
//!
//! - Built on top of `pezpallet-nfts` for standard NFT functionality
//! - All Tiki NFTs are non-transferable (soulbound)
//! - Transfer attempts are blocked automatically via hooks
//! - Each role is represented by a unique NFT item in the TikiCollectionId
//!
//! ## Role Management
//!
//! ### Granting Roles
//! - Some roles are unique (only one holder at a time)
//! - Users can hold multiple compatible roles
//! - Maximum roles per user is configurable
//! - Trust score requirements for certain roles
//!
//! ### Revoking Roles
//! - Admin can revoke appointed roles
//! - Automatic revocation on condition changes
//! - Role history maintained for governance transparency
//!
//! ## Interface
//!
//! ### Extrinsics
//!
//! - `grant_tiki(who, tiki, assignment_type)` - Assign a role to a user (admin)
//! - `revoke_tiki(who, tiki)` - Remove a role from a user (admin)
//! - `force_mint_citizen_nft(who)` - Manually mint citizenship NFT (admin)
//!
//! ### Storage
//!
//! - `CitizenNft` - Mapping of accounts to their citizenship NFT IDs
//! - `UserTikis` - List of roles held by each user
//! - `TikiHolder` - Reverse mapping for unique roles to their holders
//! - `NextItemId` - Counter for NFT item ID generation
//!
//! ### Hooks
//!
//! - `on_initialize` - Automatic citizenship NFT minting for newly approved KYC users
//! - NFT transfer blocking for all Tiki NFTs
//!
//! ## Dependencies
//!
//! This pezpallet requires integration with:
//! - `pezpallet-identity-kyc` - KYC status and approval notifications
//! - `pezpallet-nfts` - Underlying NFT infrastructure
//! - `pezpallet-trust` - Trust score verification for role eligibility
//!
//! ## Runtime Integration Example
//!
//! ```ignore
//! impl pezpallet_tiki::Config for Runtime {
//! type RuntimeEvent = RuntimeEvent;
//! type AdminOrigin = EnsureRoot<AccountId>;
//! type WeightInfo = pezpallet_tiki::weights::BizinikiwiWeight<Runtime>;
//! type TikiCollectionId = ConstU32<1>; // Tiki collection ID
//! type MaxTikisPerUser = ConstU32<20>; // Max 20 roles per user
//! type Tiki = pezpallet_tiki::Tiki;
//! }
//! ```
extern crate alloc;
pub use pezpallet::*;
use alloc::{format, vec::Vec};
use pezframe_support::pezpallet_prelude::{MaybeSerializeDeserialize, Parameter, RuntimeDebug};
use pezsp_runtime::DispatchError;
use scale_info::TypeInfo;
use serde::{Deserialize, Serialize};
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
#[cfg(test)]
mod mock;
#[cfg(test)]
mod tests;
pub mod weights;
pub use weights::*;
pub mod ensure;
pub mod migrations; // Storage migrations // For origin validation
#[pezframe_support::pezpallet]
pub mod pezpallet {
use super::*;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
use pezsp_runtime::traits::StaticLookup;
#[pezpallet::pezpallet]
#[pezpallet::storage_version(migrations::STORAGE_VERSION)]
pub struct Pezpallet<T>(_);
#[pezpallet::config]
pub trait Config:
pezframe_system::Config<RuntimeEvent: From<Event<Self>>>
+ pezpallet_nfts::Config<ItemId = u32>
+ pezpallet_identity_kyc::Config
{
type AdminOrigin: EnsureOrigin<Self::RuntimeOrigin>;
type WeightInfo: weights::WeightInfo;
/// Collection ID holding Tiki (Role) NFTs.
#[pezpallet::constant]
type TikiCollectionId: Get<Self::CollectionId>;
/// Technical upper limit for maximum number of Tikis (roles) a user can hold.
#[pezpallet::constant]
type MaxTikisPerUser: Get<u32>;
/// Tiki enum type to be used within the pezpallet.
type Tiki: Parameter
+ From<Tiki>
+ Into<u32>
+ MaxEncodedLen
+ TypeInfo
+ Copy
+ MaybeSerializeDeserialize
+ 'static;
}
#[derive(
Serialize,
Deserialize,
Encode,
Decode,
DecodeWithMemTracking,
Clone,
Eq,
PartialEq,
RuntimeDebug,
TypeInfo,
MaxEncodedLen,
Copy,
)]
pub enum RoleAssignmentType {
/// Automatically assigned roles (like Welati after KYC)
Automatic,
/// Admin-assigned roles (like Wezir, Dadger)
Appointed,
/// Community-elected roles (like Parlementer) - assigned by pezpallet-voting
Elected,
/// Earned roles (Axa, roles obtained through exams)
Earned,
}
#[derive(
Serialize,
Deserialize,
Encode,
Decode,
DecodeWithMemTracking,
Clone,
Eq,
PartialEq,
RuntimeDebug,
TypeInfo,
MaxEncodedLen,
Copy,
)]
#[repr(u32)]
pub enum Tiki {
Welati,
Parlementer,
SerokiMeclise,
Serok,
Wezir,
EndameDiwane,
Dadger,
Dozger,
Hiquqnas,
Noter,
Xezinedar,
Bacgir,
GerinendeyeCavkaniye,
OperatorêTorê,
PisporêEwlehiyaSîber,
GerinendeyeDaneye,
Berdevk,
Qeydkar,
Balyoz,
Navbeynkar,
ParêzvaneÇandî,
Mufetîs,
KalîteKontrolker,
Mela,
Feqî,
Perwerdekar,
Rewsenbîr,
RêveberêProjeyê,
SerokêKomele,
ModeratorêCivakê,
Axa,
Pêseng,
Sêwirmend,
Hekem,
Mamoste,
// Newly added economic roles
Bazargan,
// Government roles
SerokWeziran,
WezireDarayiye,
WezireParez,
WezireDad,
WezireBelaw,
WezireTend,
WezireAva,
WezireCand,
}
impl From<Tiki> for u32 {
fn from(val: Tiki) -> Self {
val as u32
}
}
/// Holds citizenship NFT ID for each user
#[pezpallet::storage]
#[pezpallet::getter(fn citizen_nft)]
pub type CitizenNft<T: Config> =
StorageMap<_, Blake2_128Concat, T::AccountId, u32, OptionQuery>;
/// List of Tikis (roles) owned by each user
#[pezpallet::storage]
#[pezpallet::getter(fn user_tikis)]
pub type UserTikis<T: Config> = StorageMap<
_,
Blake2_128Concat,
T::AccountId,
BoundedVec<Tiki, T::MaxTikisPerUser>,
ValueQuery,
>;
/// Shows which user a specific Tiki belongs to (for unique roles)
#[pezpallet::storage]
#[pezpallet::getter(fn tiki_holder)]
pub type TikiHolder<T: Config> =
StorageMap<_, Blake2_128Concat, Tiki, T::AccountId, OptionQuery>;
/// Item ID to be used for next NFT
#[pezpallet::storage]
#[pezpallet::getter(fn next_item_id)]
pub type NextItemId<T: Config> = StorageValue<_, u32, ValueQuery>;
#[pezpallet::error]
pub enum Error<T> {
/// Role already belongs to someone else
RoleAlreadyTaken,
/// Specified person is not the holder of this role
NotTheHolder,
/// Role not assigned
RoleNotAssigned,
/// A user has reached maximum role count
ExceedsMaxRolesPerUser,
/// KYC not completed
KycNotCompleted,
/// Citizenship NFT already exists
CitizenNftAlreadyExists,
/// Citizenship NFT not found
CitizenNftNotFound,
/// User already has this role
UserAlreadyHasRole,
/// Insufficient Trust Score
InsufficientTrustScore,
/// This role type cannot be assigned with this method
InvalidRoleAssignmentMethod,
}
#[pezpallet::event]
#[pezpallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// New citizenship NFT minted
CitizenNftMinted { who: T::AccountId, nft_id: u32 },
/// New Tiki (role) granted
TikiGranted { who: T::AccountId, tiki: Tiki },
/// Tiki (role) revoked
TikiRevoked { who: T::AccountId, tiki: Tiki },
/// NFT transfer blocked
TransferBlocked {
collection_id: T::CollectionId,
item_id: u32,
from: T::AccountId,
to: T::AccountId,
},
}
#[pezpallet::hooks]
impl<T: Config> Hooks<BlockNumberFor<T>> for Pezpallet<T> {
fn on_initialize(_block_number: BlockNumberFor<T>) -> Weight {
// Check newly KYC-approved users and mint citizenship NFT
Self::check_and_mint_citizen_nfts();
T::DbWeight::get().reads_writes(10, 5)
}
}
#[pezpallet::call]
impl<T: Config> Pezpallet<T> {
/// Admin tarafından belirli bir kullanıcıya Tiki (rol) verme
#[pezpallet::call_index(0)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn grant_tiki(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
tiki: Tiki,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
let dest_account = T::Lookup::lookup(dest)?;
// Check if the role can be appointed
ensure!(
Self::can_grant_role_type(&tiki, &RoleAssignmentType::Appointed),
Error::<T>::InvalidRoleAssignmentMethod
);
Self::internal_grant_role(&dest_account, tiki)?;
Ok(())
}
/// Admin tarafından belirli bir kullanıcıdan Tiki (rol) alma
#[pezpallet::call_index(1)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::revoke_tiki())]
pub fn revoke_tiki(
origin: OriginFor<T>,
target: <T::Lookup as StaticLookup>::Source,
tiki: Tiki,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
let target_account = T::Lookup::lookup(target)?;
Self::internal_revoke_role(&target_account, tiki)?;
Ok(())
}
/// Manually mint citizenship NFT (for testing/emergency)
#[pezpallet::call_index(2)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn force_mint_citizen_nft(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?;
let dest_account = T::Lookup::lookup(dest)?;
Self::mint_citizen_nft_for_user(&dest_account)?;
Ok(())
}
/// Grant role through election system (called from pezpallet-voting)
#[pezpallet::call_index(3)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn grant_elected_role(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
tiki: Tiki,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?; // pezpallet-voting will call with Root origin
let dest_account = T::Lookup::lookup(dest)?;
// Check if the role can be granted through election
ensure!(
Self::can_grant_role_type(&tiki, &RoleAssignmentType::Elected),
Error::<T>::InvalidRoleAssignmentMethod
);
Self::internal_grant_role(&dest_account, tiki)?;
Ok(())
}
/// Grant role through exam/test system
#[pezpallet::call_index(4)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn grant_earned_role(
origin: OriginFor<T>,
dest: <T::Lookup as StaticLookup>::Source,
tiki: Tiki,
) -> DispatchResult {
T::AdminOrigin::ensure_origin(origin)?; // For now admin, later exam pezpallet
let dest_account = T::Lookup::lookup(dest)?;
// Check if the role can be earned
ensure!(
Self::can_grant_role_type(&tiki, &RoleAssignmentType::Earned),
Error::<T>::InvalidRoleAssignmentMethod
);
Self::internal_grant_role(&dest_account, tiki)?;
Ok(())
}
/// Apply for citizenship after KYC completion
#[pezpallet::call_index(5)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn apply_for_citizenship(origin: OriginFor<T>) -> DispatchResult {
let who = ensure_signed(origin)?;
// Check if user's KYC is approved
let kyc_status = pezpallet_identity_kyc::Pezpallet::<T>::kyc_status_of(&who);
ensure!(
kyc_status == pezpallet_identity_kyc::types::KycLevel::Approved,
Error::<T>::KycNotCompleted
);
// Mint citizenship NFT
Self::mint_citizen_nft_for_user(&who)?;
Ok(())
}
/// Check NFT transfer for transfer blocking system
#[pezpallet::call_index(6)]
#[pezpallet::weight(<T as crate::pezpallet::Config>::WeightInfo::grant_tiki())]
pub fn check_transfer_permission(
_origin: OriginFor<T>,
collection_id: T::CollectionId,
item_id: u32,
from: T::AccountId,
to: T::AccountId,
) -> DispatchResult {
// Tiki NFT koleksiyonu ise transfer'e izin verme
if collection_id == T::TikiCollectionId::get() {
Self::deposit_event(Event::TransferBlocked { collection_id, item_id, from, to });
return Err(DispatchError::Other("Citizen NFTs are non-transferable"));
}
Ok(())
}
}
// Pezpallet's helper functions
impl<T: Config> Pezpallet<T> {
/// Checks newly KYC-completed users and mints citizenship NFT
fn check_and_mint_citizen_nfts() {
// Check all approved users in KYC pezpallet
for (account, kyc_status) in pezpallet_identity_kyc::KycStatuses::<T>::iter() {
// Check if KYC is approved
if kyc_status == pezpallet_identity_kyc::types::KycLevel::Approved {
// Check if citizenship NFT exists
if Self::citizen_nft(&account).is_none() {
// Mint NFT (log error but continue on failure)
if Self::mint_citizen_nft_for_user(&account).is_err() {
log::warn!("Failed to mint citizen NFT for account: {account:?}");
}
}
}
}
}
/// Mints citizenship NFT for specific user
pub fn mint_citizen_nft_for_user(user: &T::AccountId) -> DispatchResult {
// Check if NFT already exists
ensure!(Self::citizen_nft(user).is_none(), Error::<T>::CitizenNftAlreadyExists);
let collection_id = T::TikiCollectionId::get();
let next_id_u32 = Self::next_item_id();
// Mint the NFT - use force_mint in benchmarks to bypass balance/origin requirements
#[cfg(feature = "runtime-benchmarks")]
pezpallet_nfts::Pezpallet::<T>::force_mint(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Root),
collection_id,
next_id_u32,
T::Lookup::unlookup(user.clone()),
Default::default(),
)?;
#[cfg(not(feature = "runtime-benchmarks"))]
pezpallet_nfts::Pezpallet::<T>::force_mint(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Root),
collection_id,
next_id_u32,
T::Lookup::unlookup(user.clone()),
Default::default(),
)?;
// Make NFT non-transferable
Self::lock_nft_transfer(&collection_id, &next_id_u32)?;
// Update storage
CitizenNft::<T>::insert(user, next_id_u32);
NextItemId::<T>::put(next_id_u32.saturating_add(1));
// Automatically add Welati role
UserTikis::<T>::mutate(user, |tikis| {
let _ = tikis.try_push(Tiki::Welati);
});
// Set NFT metadata
Self::update_nft_metadata(user)?;
Self::deposit_event(Event::CitizenNftMinted { who: user.clone(), nft_id: next_id_u32 });
Ok(())
}
/// Internal role granting function (to avoid code duplication)
pub fn internal_grant_role(dest_account: &T::AccountId, tiki: Tiki) -> DispatchResult {
// Check if citizenship NFT exists
ensure!(Self::citizen_nft(dest_account).is_some(), Error::<T>::CitizenNftNotFound);
// If this role is unique (can belong to only one person), check
if Self::is_unique_role(&tiki) {
ensure!(Self::tiki_holder(tiki).is_none(), Error::<T>::RoleAlreadyTaken);
}
// Check if user already has this role
let user_tikis = Self::user_tikis(dest_account);
ensure!(!user_tikis.contains(&tiki), Error::<T>::UserAlreadyHasRole);
// Add to user's Tiki list
UserTikis::<T>::try_mutate(dest_account, |tikis| {
tikis.try_push(tiki).map_err(|_| Error::<T>::ExceedsMaxRolesPerUser)
})?;
// If unique role, also add to TikiHolder
if Self::is_unique_role(&tiki) {
TikiHolder::<T>::insert(tiki, dest_account);
}
// Update NFT metadata
Self::update_nft_metadata(dest_account)?;
Self::deposit_event(Event::TikiGranted { who: dest_account.clone(), tiki });
Ok(())
}
/// Internal role revocation function
pub fn internal_revoke_role(target_account: &T::AccountId, tiki: Tiki) -> DispatchResult {
// Check if user has this role
let user_tikis = Self::user_tikis(target_account);
let _position =
user_tikis.iter().position(|&r| r == tiki).ok_or(Error::<T>::RoleNotAssigned)?;
// Welati role cannot be removed
ensure!(tiki != Tiki::Welati, Error::<T>::RoleNotAssigned);
// Remove from user's Tiki list
UserTikis::<T>::mutate(target_account, |tikis| {
if let Some(pos) = tikis.iter().position(|&r| r == tiki) {
tikis.swap_remove(pos);
}
});
// If unique role, also remove from TikiHolder
if Self::is_unique_role(&tiki) {
TikiHolder::<T>::remove(tiki);
}
// Update NFT metadata
Self::update_nft_metadata(target_account)?;
Self::deposit_event(Event::TikiRevoked { who: target_account.clone(), tiki });
Ok(())
}
/// Makes NFT non-transferable
fn lock_nft_transfer(collection_id: &T::CollectionId, item_id: &u32) -> DispatchResult {
// Mark NFT with lock attribute - use force_set_attribute in benchmarks to bypass
// deposits
#[cfg(feature = "runtime-benchmarks")]
let _ = pezpallet_nfts::Pezpallet::<T>::force_set_attribute(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Root),
None,
*collection_id,
Some(*item_id),
pezpallet_nfts::AttributeNamespace::Pezpallet,
b"locked"
.to_vec()
.try_into()
.map_err(|_| DispatchError::Other("Key too long"))?,
b"true"
.to_vec()
.try_into()
.map_err(|_| DispatchError::Other("Value too long"))?,
);
#[cfg(not(feature = "runtime-benchmarks"))]
let _ = pezpallet_nfts::Pezpallet::<T>::set_attribute(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Root),
*collection_id,
Some(*item_id),
pezpallet_nfts::AttributeNamespace::Pezpallet,
b"locked"
.to_vec()
.try_into()
.map_err(|_| DispatchError::Other("Key too long"))?,
b"true"
.to_vec()
.try_into()
.map_err(|_| DispatchError::Other("Value too long"))?,
);
Ok(())
}
/// Updates NFT metadata based on user's roles
fn update_nft_metadata(user: &T::AccountId) -> DispatchResult {
let nft_id_u32 = Self::citizen_nft(user).ok_or(Error::<T>::CitizenNftNotFound)?;
let collection_id = T::TikiCollectionId::get();
let user_tikis = Self::user_tikis(user);
let total_score = Self::get_tiki_score(user);
// Short metadata - only basic information
let metadata = format!(
r#"{{"citizen":true,"roles":{},"score":{}}}"#,
user_tikis.len(),
total_score
);
// Set metadata - log error but don't crash
if pezpallet_nfts::Pezpallet::<T>::set_metadata(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Root),
collection_id,
nft_id_u32,
metadata
.as_bytes()
.to_vec()
.try_into()
.map_err(|_| DispatchError::Other("Metadata too long"))?,
)
.is_err()
{
log::warn!("Failed to set metadata for NFT: {nft_id_u32:?}");
}
Ok(())
}
/// Checks if a specific role is unique (can belong to only one person)
pub fn is_unique_role(tiki: &Tiki) -> bool {
matches!(tiki, Tiki::Serok | Tiki::SerokiMeclise | Tiki::Xezinedar | Tiki::Balyoz)
}
/// Returns the assignment type of a specific role
pub fn get_role_assignment_type(tiki: &Tiki) -> RoleAssignmentType {
match tiki {
// Automatic roles
Tiki::Welati => RoleAssignmentType::Automatic,
// Elected roles
Tiki::Parlementer | Tiki::SerokiMeclise | Tiki::Serok => {
RoleAssignmentType::Elected
},
// Earned roles (automatically given by pezpallet-referral)
Tiki::Axa
| Tiki::Mamoste
| Tiki::Rewsenbîr
| Tiki::SerokêKomele
| Tiki::ModeratorêCivakê => RoleAssignmentType::Earned,
// Appointed roles (default)
_ => RoleAssignmentType::Appointed,
}
}
/// Checks the granting method of a specific role
pub fn can_grant_role_type(tiki: &Tiki, assignment_type: &RoleAssignmentType) -> bool {
let required_type = Self::get_role_assignment_type(tiki);
match (&required_type, assignment_type) {
// Automatic roles can only be given by the system
(RoleAssignmentType::Automatic, RoleAssignmentType::Automatic) => true,
// Appointed roles can be given by admin
(RoleAssignmentType::Appointed, RoleAssignmentType::Appointed) => true,
// Elected roles can be given by election system
(RoleAssignmentType::Elected, RoleAssignmentType::Elected) => true,
// Earned roles can be given by exam/test system
(RoleAssignmentType::Earned, RoleAssignmentType::Earned) => true,
_ => false,
}
}
/// KYC sonrası otomatik Welati rolü verme
pub fn auto_grant_citizenship(account: &T::AccountId) -> DispatchResult {
// KYC kontrolü
let kyc_status = pezpallet_identity_kyc::Pezpallet::<T>::kyc_status_of(account);
if kyc_status == pezpallet_identity_kyc::types::KycLevel::Approved {
// Vatandaşlık NFT'si yoksa bas
if Self::citizen_nft(account).is_none() {
Self::mint_citizen_nft_for_user(account)?;
}
}
Ok(())
}
/// Kullanıcının belirli bir Tiki'ye sahip olup olmadığını kontrol eder
pub fn has_tiki(who: &T::AccountId, tiki: &Tiki) -> bool {
Self::user_tikis(who).contains(tiki)
}
/// Kullanıcının vatandaş olup olmadığını kontrol eder
pub fn is_citizen(who: &T::AccountId) -> bool {
Self::citizen_nft(who).is_some()
}
}
}
/// Diğer paletlerin, bu paletten Tiki puanlarını sorgulaması için kullanılacak trait
pub trait TikiScoreProvider<AccountId> {
fn get_tiki_score(who: &AccountId) -> u32;
}
/// Diğer paletlerin, Tiki sahipliğini sorgulaması için kullanılacak trait
pub trait TikiProvider<AccountId> {
fn has_tiki(who: &AccountId, tiki: &Tiki) -> bool;
fn get_user_tikis(who: &AccountId) -> Vec<Tiki>;
fn is_citizen(who: &AccountId) -> bool;
}
/// Trait implementasyonları
impl<T: Config> TikiScoreProvider<T::AccountId> for Pezpallet<T> {
fn get_tiki_score(who: &T::AccountId) -> u32 {
let tikis = Self::user_tikis(who);
tikis.iter().map(Self::get_bonus_for_tiki).sum()
}
}
impl<T: Config> TikiProvider<T::AccountId> for Pezpallet<T> {
fn has_tiki(who: &T::AccountId, tiki: &Tiki) -> bool {
Self::has_tiki(who, tiki)
}
fn get_user_tikis(who: &T::AccountId) -> Vec<Tiki> {
Self::user_tikis(who).into_inner()
}
fn is_citizen(who: &T::AccountId) -> bool {
Self::is_citizen(who)
}
}
// Puanlama mantığını ayrı bir impl bloğunda tutarak kodu daha düzenli hale getiriyoruz.
impl<T: Config> Pezpallet<T> {
/// Belirli bir Tiki'nin Trust Puanı'na olan katkısını döndürür.
pub fn get_bonus_for_tiki(tiki: &Tiki) -> u32 {
match tiki {
// Anayasa v5.0'da Belirlenen Özel Puanlar
Tiki::Axa => 250,
Tiki::RêveberêProjeyê => 250,
Tiki::ModeratorêCivakê => 200,
Tiki::SerokêKomele => 100,
Tiki::Mela => 50,
Tiki::Feqî => 50,
// Hiyerarşik Devlet Puanları
// Yargı
Tiki::EndameDiwane => 175,
Tiki::Dadger => 150,
Tiki::Dozger => 120,
Tiki::Hiquqnas => 75,
// Yürütme
Tiki::Serok => 200,
Tiki::Wezir => 100,
Tiki::SerokWeziran => 125,
Tiki::WezireDarayiye => 100,
Tiki::WezireParez => 100,
Tiki::WezireDad => 100,
Tiki::WezireBelaw => 100,
Tiki::WezireTend => 100,
Tiki::WezireAva => 100,
Tiki::WezireCand => 100,
// Yasama
Tiki::SerokiMeclise => 150,
Tiki::Parlementer => 100,
// Atanmış Üst Düzey Memurlar
Tiki::Xezinedar => 100,
Tiki::PisporêEwlehiyaSîber => 100,
Tiki::Mufetîs => 90,
Tiki::Balyoz => 80,
Tiki::Berdevk => 70,
// Diğer Memurlar ve Uzmanlar
Tiki::Mamoste => 70,
Tiki::OperatorêTorê => 60,
Tiki::Noter => 50,
Tiki::Bacgir => 50,
Tiki::Perwerdekar => 40,
Tiki::Rewsenbîr => 40,
Tiki::GerinendeyeCavkaniye => 40,
Tiki::GerinendeyeDaneye => 40,
Tiki::KalîteKontrolker => 30,
Tiki::Navbeynkar => 30,
Tiki::Hekem => 30,
Tiki::Qeydkar => 25,
Tiki::ParêzvaneÇandî => 25,
Tiki::Sêwirmend => 20,
Tiki::Bazargan => 60, // Yeni eklenen ekonomik rol
// Temel Vatandaşlık ve Diğerleri
Tiki::Welati => 10,
// Yukarıdaki listede olmayan diğer tüm roller 5 puan alır.
_ => 5,
}
}
}
// CitizenNftProvider trait implementation for pezpallet-identity-kyc integration
impl<T: Config> pezpallet_identity_kyc::types::CitizenNftProvider<T::AccountId> for Pezpallet<T> {
fn mint_citizen_nft(who: &T::AccountId) -> pezsp_runtime::DispatchResult {
Self::mint_citizen_nft_for_user(who)
}
fn mint_citizen_nft_confirmed(who: &T::AccountId) -> pezsp_runtime::DispatchResult {
// For self-confirmation, we use the same mint function with force_mint
Self::mint_citizen_nft_for_user(who)
}
fn burn_citizen_nft(who: &T::AccountId) -> pezsp_runtime::DispatchResult {
use pezframe_support::traits::Get;
// Get the citizen NFT item ID
let item_id = Self::citizen_nft(who).ok_or(Error::<T>::CitizenNftNotFound)?;
let collection_id = T::TikiCollectionId::get();
// Burn the NFT using pezpallet_nfts burn function
pezpallet_nfts::Pezpallet::<T>::burn(
T::RuntimeOrigin::from(pezframe_system::RawOrigin::Signed(who.clone())),
collection_id,
item_id,
)?;
// Remove from our storage
CitizenNft::<T>::remove(who);
Ok(())
}
}