Skip to content
Merged
50 changes: 50 additions & 0 deletions src/campaign_donation.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ pub mod CampaignDonation {
/// Protocol fee percentage using 10000 basis points (e.g. 250 = 2.5%). Default is 0.
protocol_fee_percent: u256,
protocol_fee_address: ContractAddress,
unique_donors_count: Map<u256, u32>, // Number of unique donors per campaign
campaign_donors: Map<
(u256, ContractAddress), bool,
> // Track if an address has donated to a campaign
}


Expand Down Expand Up @@ -286,6 +290,43 @@ pub mod CampaignDonation {
let campaign: Campaigns = self.campaigns.read(campaign_id);
campaign
}

fn get_campaign_progress(self: @ContractState, campaign_id: u256) -> u8 {
// Validate campaign exists
assert(campaign_id > 0, CAMPAIGN_NOT_FOUND);
assert(campaign_id <= self.campaign_counts.read(), CAMPAIGN_NOT_FOUND);

let campaign: Campaigns = self.campaigns.read(campaign_id);
if campaign.target_amount == 0 {
return 0_u8;
}

let progress = (campaign.current_balance * 100) / campaign.target_amount;

// // Cap at 100% for overfunded campaigns
if progress > 100_u256 {
return 100_u8;
}

// Convert the calculated progress to u8 using try_into().
// Since we already capped at 100, this should always succeed
let progress_u8: u8 = match progress.try_into() {
Option::Some(val) => val,
Option::None => 100_u8 // Fallback to 100% if conversion fails
};

progress_u8
}


fn get_campaign_donor_count(self: @ContractState, campaign_id: u256) -> u32 {
// Validate campaign exists
assert(campaign_id > 0, CAMPAIGN_NOT_FOUND);
assert(campaign_id <= self.campaign_counts.read(), CAMPAIGN_NOT_FOUND);

// Simply return the stored count of unique donors
self.unique_donors_count.read(campaign_id)
}
fn set_donation_nft_address(
ref self: ContractState, donation_nft_address: ContractAddress,
) {
Expand Down Expand Up @@ -552,6 +593,15 @@ pub mod CampaignDonation {
// Save donation reference for the donor
self.donor_donations.entry(donor).push((campaign_id, donation_id));

// Update unique donor count if this is the first donation from this donor to this
// campaign
let has_donated_before = self.campaign_donors.read((campaign_id, donor));
if !has_donated_before {
self.campaign_donors.write((campaign_id, donor), true);
let current_unique_donors = self.unique_donors_count.read(campaign_id);
self.unique_donors_count.write(campaign_id, current_unique_donors + 1);
}

// Update the per-campaign donation count
let campaign_donation_count = self.donation_counts.read(campaign_id);
self.donation_counts.write(campaign_id, campaign_donation_count + 1);
Expand Down
41 changes: 25 additions & 16 deletions src/interfaces/ICampaignDonation.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ pub trait ICampaignDonation<TContractState> {
/// # Returns
/// * `Array<Donations>` - An array of all donations made to the campaign
fn get_campaign_donations(self: @TContractState, campaign_id: u256) -> Array<Donations>;

/// Sets the address of the donation NFT contract
///
/// # Arguments
Expand All @@ -122,6 +123,7 @@ pub trait ICampaignDonation<TContractState> {
/// # Returns
/// * `u256` - The token ID of the minted NFT
fn mint_donation_nft(ref self: TContractState, campaign_id: u256, donation_id: u256) -> u256;

// *************************************************************************
// USER EXPERIENCE ENHANCEMENTS
// *************************************************************************
Expand Down Expand Up @@ -161,19 +163,23 @@ pub trait ICampaignDonation<TContractState> {
///
/// # Returns
/// * `Array<(u256, Donations)>` - Array of tuples (campaign_id, donation)

fn get_donations_by_donor(
self: @TContractState, donor: ContractAddress,
) -> Array<(u256, Donations)>;


/// Gets the total amount donated by a specific address
///
/// # Arguments
/// * `donor` - The address of the donor
///
/// # Returns
/// * `u256` - Total amount donated across all campaigns

fn get_total_donated_by_donor(self: @TContractState, donor: ContractAddress) -> u256;


/// Checks if a donor has contributed to a specific campaign
///
/// # Arguments
Expand All @@ -182,6 +188,7 @@ pub trait ICampaignDonation<TContractState> {
///
/// # Returns
/// * `bool` - True if the donor has contributed, false otherwise

fn has_donated_to_campaign(
self: @TContractState, campaign_id: u256, donor: ContractAddress,
) -> bool;
Expand Down Expand Up @@ -239,31 +246,33 @@ pub trait ICampaignDonation<TContractState> {
/// @return The address where protocol fees are sent
fn get_protocol_fee_address(self: @TContractState) -> ContractAddress;


/// @notice Sets a new protocol fee collection address
/// @param new_fee_address The new address to collect protocol fees
fn set_protocol_fee_address(ref self: TContractState, new_fee_address: ContractAddress);
// *************************************************************************
// ANALYTICS & INSIGHTS
// *************************************************************************
// ANALYTICS & INSIGHTS
// *************************************************************************

/// Gets the progress percentage of a campaign
///
/// # Arguments
/// * `campaign_id` - The campaign ID
///
/// # Returns
/// * `u8` - Progress percentage (0-100)
// fn get_campaign_progress(self: @TContractState, campaign_id: u256) -> u8;
///
/// # Arguments
/// * `campaign_id` - The campaign ID
///
/// # Returns
/// * `u8` - Progress percentage (0-100)

fn get_campaign_progress(self: @TContractState, campaign_id: u256) -> u8;

/// Gets the number of unique donors for a campaign
///
/// # Arguments
/// * `campaign_id` - The campaign ID
///
/// # Returns
/// * `u32` - Number of unique donors
// fn get_campaign_donor_count(self: @TContractState, campaign_id: u256) -> u32;
///
/// # Arguments
/// * `campaign_id` - The campaign ID
///
/// # Returns
/// * `u32` - Number of unique donors

fn get_campaign_donor_count(self: @TContractState, campaign_id: u256) -> u32;
/// Gets campaigns close to reaching their goal
///
/// # Arguments
Expand Down
Loading