Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions contract/contract/src/base/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ pub enum CrowdfundingError {
CampaignCancelled = 50,
DeadlinePassed = 51,
VectorLimitExceeded = 52,
/// SchoolNotFound = 53.
SchoolNotFound = 53,
}

/// Documentation for this item.
Expand Down
21 changes: 21 additions & 0 deletions contract/contract/src/base/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,23 @@ pub struct ScholarshipApplication {
pub status: ApplicationStatus,
}

/// Represents a registered school with identity mapping to an accredited body.
///
/// Schools must be registered by the root Nevo Admin to establish their
/// official representation status on the Nevo platform.
#[contracttype]
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct School {
/// The school's official address (Soroban Address).
pub school_address: Address,
/// The metadata hash identifying the school details (32 bytes).
pub metadata_hash: BytesN<32>,
/// Timestamp when the school was registered.
pub registered_at: u64,
/// The admin who registered this school.
pub registered_by: Address,
}

/// Documentation for this item.
#[allow(missing_docs)]
#[contracttype]
Expand Down Expand Up @@ -565,6 +582,10 @@ pub enum StorageKey {
PoolBalance(u64),
// Sum of requested_amount for all Approved applications on a pool
PoolAllocated(u64),
/// School mapping by address for identity verification.
School(Address),
/// All registered schools (list of addresses).
AllSchools,
}

#[cfg(test)]
Expand Down
18 changes: 17 additions & 1 deletion contract/contract/src/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::base::{
errors::CrowdfundingError,
types::{
CampaignDetails, CampaignLifecycleStatus, PoolConfig, PoolContribution, PoolMetadata,
PoolState,
PoolState, School,
},
};
use crate::crowdfunding::CrowdfundingContract;
Expand Down Expand Up @@ -392,4 +392,20 @@ impl CrowdfundingTrait for FundEduContract {
) -> Result<(), CrowdfundingError> {
CrowdfundingContract::withdraw_unallocated(env, pool_id, sponsor, amount)
}

fn register_school(
env: Env,
school_addr: Address,
metadata_hash: BytesN<32>,
) -> Result<(), CrowdfundingError> {
CrowdfundingContract::register_school(env, school_addr, metadata_hash)
}

fn get_school(env: Env, school_addr: Address) -> Result<School, CrowdfundingError> {
CrowdfundingContract::get_school(env, school_addr)
}

fn get_all_schools(env: Env) -> Vec<Address> {
CrowdfundingContract::get_all_schools(env)
}
}
69 changes: 68 additions & 1 deletion contract/contract/src/crowdfunding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use crate::base::{
types::{
ApplicationStatus, CampaignDetails, CampaignLifecycleStatus, CampaignMetrics, Contribution,
EmergencyWithdrawal, EventDetails, EventMetrics, MultiSigConfig, PoolConfig,
PoolContribution, PoolMetadata, PoolMetrics, PoolState, ScholarshipApplication, StorageKey,
PoolContribution, PoolMetadata, PoolMetrics, PoolState, ScholarshipApplication, School, StorageKey,
MAX_DESCRIPTION_LENGTH, MAX_HASH_LENGTH, MAX_SINGLE_OP_ITEMS, MAX_STRING_LENGTH, MAX_URL_LENGTH,
},
};
Expand Down Expand Up @@ -2402,6 +2402,73 @@ impl CrowdfundingTrait for CrowdfundingContract {
.get(&app_key)
.ok_or(ValidationError::ApplicationNotFound)
}

fn register_school(
env: Env,
school_addr: Address,
metadata_hash: BytesN<32>,
) -> Result<(), CrowdfundingError> {
// Retrieve the admin from storage
let admin: Address = env
.storage()
.instance()
.get(&StorageKey::Admin)
.ok_or(CrowdfundingError::NotInitialized)?;

// Require admin authorization
admin.require_auth();

// Create the school entry with timestamp and admin
let now = env.ledger().timestamp();
let school = School {
school_address: school_addr.clone(),
metadata_hash,
registered_at: now,
registered_by: admin.clone(),
};

// Store the school mapping
let school_key = StorageKey::School(school_addr.clone());
env.storage().instance().set(&school_key, &school);

// Add to all schools list
let all_schools_key = StorageKey::AllSchools;
let mut schools: Vec<Address> = env
.storage()
.instance()
.get(&all_schools_key)
.unwrap_or_else(|| Vec::new(&env));

// Only add if not already registered
if !schools.contains(&school_addr) {
schools.push_back(school_addr.clone());
env.storage().instance().set(&all_schools_key, &schools);
}

// Emit event
events::school_registered(&env, school_addr);

Ok(())
}

fn get_school(
env: Env,
school_addr: Address,
) -> Result<School, CrowdfundingError> {
let school_key = StorageKey::School(school_addr);
env.storage()
.instance()
.get(&school_key)
.ok_or(CrowdfundingError::SchoolNotFound)
}

fn get_all_schools(env: Env) -> Vec<Address> {
let all_schools_key = StorageKey::AllSchools;
env.storage()
.instance()
.get(&all_schools_key)
.unwrap_or_else(|| Vec::new(&env))
}
}

#[contractimpl]
Expand Down
42 changes: 41 additions & 1 deletion contract/contract/src/interfaces/crowdfunding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::base::{
errors::{CrowdfundingError, ValidationError},
types::{
CampaignDetails, CampaignLifecycleStatus, PoolConfig, PoolContribution, PoolMetadata,
PoolState, ScholarshipApplication,
PoolState, School, ScholarshipApplication,
},
};

Expand Down Expand Up @@ -276,4 +276,44 @@ pub trait CrowdfundingTrait {
pool_id: u64,
applicant: Address,
) -> Result<ScholarshipApplication, ValidationError>;

/// Register a school with the Nevo platform via admin identity mapping.
///
/// Only the root protocol Admin can call this function to establish official
/// representation of accredited bodies.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `school_addr` - The Soroban Address of the school
/// * `metadata_hash` - A 32-byte hash containing school metadata
///
/// # Returns
/// Success or a CrowdfundingError if admin verification fails
fn register_school(
env: Env,
school_addr: Address,
metadata_hash: BytesN<32>,
) -> Result<(), CrowdfundingError>;

/// Retrieve a registered school by its address.
///
/// # Arguments
/// * `env` - The Soroban environment
/// * `school_addr` - The school's Soroban Address
///
/// # Returns
/// The School struct or a CrowdfundingError if not found
fn get_school(
env: Env,
school_addr: Address,
) -> Result<School, CrowdfundingError>;

/// Get all registered school addresses.
///
/// # Arguments
/// * `env` - The Soroban environment
///
/// # Returns
/// A vector of all registered school addresses
fn get_all_schools(env: Env) -> Vec<Address>;
}