Skip to content
Merged
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
3 changes: 3 additions & 0 deletions src/base/errors.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,8 @@ pub mod Errors {

// Throw Error when Insufficient balance
pub const INSUFFICIENT_BALANCE: felt252 = 'Error: Insufficient balance';

// Throw Error when Fee percent exceeds 100%
pub const PROTOCOL_FEE_PERCENTAGE_EXCEED: felt252 = 'Error: Fee percent exceeds 100%';
}

59 changes: 53 additions & 6 deletions src/campaign_donation.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ pub mod CampaignDonation {
CAMPAIGN_NOT_CANCELLED, CAMPAIGN_NOT_CLOSED, CAMPAIGN_NOT_FOUND, CAMPAIGN_REF_EMPTY,
CAMPAIGN_REF_EXISTS, CAMPAIGN_WITHDRAWN, CANNOT_DENOTE_ZERO_AMOUNT, DONATION_NOT_FOUND,
DOUBLE_WITHDRAWAL, INSUFFICIENT_ALLOWANCE, INSUFFICIENT_BALANCE, MORE_THAN_TARGET,
NFT_NOT_CONFIGURED, OPERATION_OVERFLOW, REFUND_ALREADY_CLAIMED, TARGET_NOT_REACHED,
TARGET_REACHED, WITHDRAWAL_FAILED, ZERO_ALLOWANCE, ZERO_AMOUNT,
NFT_NOT_CONFIGURED, OPERATION_OVERFLOW, PROTOCOL_FEE_ADDRESS_NOT_SET,
PROTOCOL_FEE_PERCENTAGE_EXCEED, REFUND_ALREADY_CLAIMED, TARGET_NOT_REACHED, TARGET_REACHED,
WITHDRAWAL_FAILED, ZERO_ALLOWANCE, ZERO_AMOUNT,
};
use crate::base::types::{Campaigns, DonationMetadata, Donations};

Expand Down Expand Up @@ -58,7 +59,10 @@ pub mod CampaignDonation {
refunds_claimed: Map<(u256, ContractAddress), bool>, // Map((campaign_id, donor), claimed)
donations_by_donor: Map<
(u256, ContractAddress), u256,
> // Map((campaign_id, donor), total_donation)
>, // Map((campaign_id, donor), total_donation)
/// Protocol fee percentage using 10000 basis points (e.g. 250 = 2.5%). Default is 0.
protocol_fee_percent: u256,
protocol_fee_address: ContractAddress,
}


Expand Down Expand Up @@ -146,9 +150,15 @@ pub mod CampaignDonation {
}

#[constructor]
fn constructor(ref self: ContractState, owner: ContractAddress, token: ContractAddress) {
fn constructor(
ref self: ContractState,
owner: ContractAddress,
token: ContractAddress,
protocol_fee_address: ContractAddress,
) {
self.ownable.initializer(owner);
self.donation_token.write(token);
self.protocol_fee_address.write(protocol_fee_address);
}

#[abi(embed_v0)]
Expand Down Expand Up @@ -429,6 +439,25 @@ pub mod CampaignDonation {
),
);
}

fn get_protocol_fee_percent(self: @ContractState) -> u256 {
self.protocol_fee_percent.read()
}

fn set_protocol_fee_percent(ref self: ContractState, new_fee_percent: u256) {
self.ownable.assert_only_owner();
assert(new_fee_percent <= 10000, PROTOCOL_FEE_PERCENTAGE_EXCEED);
self.protocol_fee_percent.write(new_fee_percent);
}

fn get_protocol_fee_address(self: @ContractState) -> ContractAddress {
self.protocol_fee_address.read()
}

fn set_protocol_fee_address(ref self: ContractState, new_fee_address: ContractAddress) {
self.ownable.assert_only_owner();
self.protocol_fee_address.write(new_fee_address);
}
}

#[generate_trait]
Expand Down Expand Up @@ -533,10 +562,21 @@ pub mod CampaignDonation {

let donation_token = self.donation_token.read();

let token = IERC20Dispatcher { contract_address: donation_token };
let token_dispatcher = IERC20Dispatcher { contract_address: donation_token };

let withdrawn_amount = campaign.current_balance;
let transfer_from = token.transfer(campaign_owner, withdrawn_amount);
let protocol_fee = self.calculate_protocol_fee(@withdrawn_amount);

// transfer protocol fee
if protocol_fee > 0 {
let protocol_address = self.protocol_fee_address.read();
assert(!protocol_address.is_zero(), PROTOCOL_FEE_ADDRESS_NOT_SET);
let protocol_transfer = token_dispatcher.transfer(protocol_address, protocol_fee);
assert(protocol_transfer, WITHDRAWAL_FAILED);
}

let amount_after_fee = withdrawn_amount - protocol_fee;
let transfer_from = token_dispatcher.transfer(campaign_owner, amount_after_fee);
assert(transfer_from, WITHDRAWAL_FAILED);

campaign.withdrawn_amount = campaign.withdrawn_amount + withdrawn_amount;
Expand All @@ -547,6 +587,13 @@ pub mod CampaignDonation {
withdrawn_amount
}

fn calculate_protocol_fee(self: @ContractState, total_amount: @u256) -> u256 {
let fee_percent = self.protocol_fee_percent.read();
let protocol_fee = (*total_amount * fee_percent) / 10000;
protocol_fee
}


fn get_asset_address(self: @ContractState, token_name: felt252) -> ContractAddress {
let mut token_address: ContractAddress = contract_address_const::<0>();
if token_name == 'USDC' || token_name == 'usdc' {
Expand Down
20 changes: 20 additions & 0 deletions src/interfaces/ICampaignDonation.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,26 @@ pub trait ICampaignDonation<TContractState> {
// / * Caller must have donated to the campaign
// / * Refund must not have been claimed already
fn claim_refund(ref self: TContractState, campaign_id: u256);

// *************************************************************************
// PROTOCOL FEES
// *************************************************************************

/// @notice Gets the current protocol fee percentage
/// @return The protocol fee percentage (100 = 1%)
fn get_protocol_fee_percent(self: @TContractState) -> u256;
Comment on lines +227 to +228

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent fee percentage documentation

The comment states "100 = 1%" but the implementation uses basis points where 100 = 1% would be represented as 100 basis points out of 10000. This should be clarified to avoid confusion.

Update the documentation to match the implementation:

-    /// @return The protocol fee percentage (100 = 1%)
+    /// @return The protocol fee percentage in basis points (100 = 1%, 10000 = 100%)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// @return The protocol fee percentage (100 = 1%)
fn get_protocol_fee_percent(self: @TContractState) -> u256;
/// @return The protocol fee percentage in basis points (100 = 1%, 10000 = 100%)
fn get_protocol_fee_percent(self: @TContractState) -> u256;
🤖 Prompt for AI Agents
In src/interfaces/ICampaignDonation.cairo at lines 227-228, the comment for
get_protocol_fee_percent incorrectly states "100 = 1%" which conflicts with the
basis points implementation. Update the comment to clarify that the fee
percentage is expressed in basis points, where 100 basis points equals 1%, to
align the documentation with the actual implementation.


/// @notice Sets a new protocol fee percentage
/// @param new_fee_percent The new fee percentage to set (100 = 1%)
fn set_protocol_fee_percent(ref self: TContractState, new_fee_percent: u256);
Comment on lines +231 to +232

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistent fee percentage documentation

The parameter documentation should also clarify the basis points format.

Update the documentation:

-    /// @param new_fee_percent The new fee percentage to set (100 = 1%)
+    /// @param new_fee_percent The new fee percentage to set in basis points (100 = 1%, 10000 = 100%)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// @param new_fee_percent The new fee percentage to set (100 = 1%)
fn set_protocol_fee_percent(ref self: TContractState, new_fee_percent: u256);
/// @param new_fee_percent The new fee percentage to set in basis points (100 = 1%, 10000 = 100%)
fn set_protocol_fee_percent(ref self: TContractState, new_fee_percent: u256);
🤖 Prompt for AI Agents
In src/interfaces/ICampaignDonation.cairo at lines 231-232, the documentation
for the parameter new_fee_percent is unclear about the format. Update the
comment to specify that the fee percentage is expressed in basis points, where
100 basis points equal 1%. This clarifies how to interpret the new_fee_percent
value.


/// @notice Gets the current protocol fee collection address
/// @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
// *************************************************************************
Expand Down
Loading
Loading