Skip to content

Commit

Permalink
campleted the project
Browse files Browse the repository at this point in the history
  • Loading branch information
Daltonic committed Dec 20, 2024
1 parent ce30507 commit b72b031
Show file tree
Hide file tree
Showing 43 changed files with 1,890 additions and 493 deletions.
2 changes: 1 addition & 1 deletion anchor/Anchor.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ resolution = true
skip-lint = false

[programs.localnet]
fundus = "AWMHjJYpfc8iDrHRALDryZtw8X2FQubnwH5ztGnxSatu"
fundus = "8tfuvG2FVPUE41keGjgybaftAiw9UQBK3FGtYxMyzJ5W"

[registry]
url = "https://api.apr.dev"
Expand Down
1 change: 1 addition & 0 deletions anchor/programs/fundus/src/constants/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pub const ANCHOR_DISCRIMINATOR_SIZE: usize = 8;
33 changes: 33 additions & 0 deletions anchor/programs/fundus/src/errors/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
use anchor_lang::prelude::*;

#[error_code]
pub enum ErrorCode {
#[msg("Th program has already been initialied.")]
AlreadyInitialized,
#[msg("Title exceeds the maximum length of 64 characters.")]
TitleTooLong,
#[msg("Description exceeds the maximum length of 512 characters.")]
DescriptionTooLong,
#[msg("Image URL exceeds the maximum length of 256 characters.")]
ImageUrlTooLong,
#[msg("Invalid goal amount. Goal must be greater than zero.")]
InvalidGoalAmount,
#[msg("Unauthorized access.")]
Unauthorized,
#[msg("Campaign not found.")]
CampaignNotFound,
#[msg("Campaign is inactive.")]
InactiveCampaign,
#[msg("Donation amount must be at least 1 SOL.")]
InvalidDonationAmount,
#[msg("Campaign goal reached.")]
CampaignGoalActualized,
#[msg("Withdrawal amount must be at least 1 SOL.")]
InvalidWithdrawalAmount,
#[msg("Insufficient funds in the campaign.")]
InsufficientFund,
#[msg("The provided platform address is invalid.")]
InvalidPlatformAddress,
#[msg("Invalid platform fee percentage.")]
InvalidPlatformFee,
}
66 changes: 66 additions & 0 deletions anchor/programs/fundus/src/instructions/create_campaign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use crate::constants::ANCHOR_DISCRIMINATOR_SIZE;
use crate::errors::ErrorCode::*;
use crate::states::{Campaign, ProgramState};
use anchor_lang::prelude::*;

pub fn create_campaign(
ctx: Context<CreateCampaignCtx>,
title: String,
description: String,
image_url: String,
goal: u64,
) -> Result<()> {
let campaign = &mut ctx.accounts.campaign;
let state = &mut ctx.accounts.program_state;

if title.len() > 64 {
return Err(TitleTooLong.into());
}
if description.len() > 512 {
return Err(DescriptionTooLong.into());
}
if image_url.len() > 256 {
return Err(ImageUrlTooLong.into());
}
if goal < 1_000_000_000 {
return Err(InvalidGoalAmount.into());
}

state.campaign_count += 1;

campaign.cid = state.campaign_count;
campaign.creator = ctx.accounts.creator.key();
campaign.title = title;
campaign.description = description;
campaign.image_url = image_url;
campaign.goal = goal;
campaign.amount_raised = 0;
campaign.donors = 0;
campaign.withdrawals = 0;
campaign.timestamp = Clock::get()?.unix_timestamp as u64;
campaign.active = true;

Ok(())
}

#[derive(Accounts)]
pub struct CreateCampaignCtx<'info> {
#[account(mut)]
pub program_state: Account<'info, ProgramState>,

#[account(
init,
payer = creator,
space = ANCHOR_DISCRIMINATOR_SIZE + Campaign::INIT_SPACE,
seeds = [
b"campaign",
(program_state.campaign_count + 1).to_le_bytes().as_ref()
],
bump
)]
pub campaign: Account<'info, Campaign>,

#[account(mut)]
pub creator: Signer<'info>,
pub system_program: Program<'info, System>,
}
42 changes: 42 additions & 0 deletions anchor/programs/fundus/src/instructions/delete_campaign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use crate::errors::ErrorCode::*;
use crate::states::Campaign;
use anchor_lang::prelude::*;

pub fn delete_campaign(ctx: Context<DeleteCampaignCtx>, cid: u64) -> Result<()> {
let campaign = &mut ctx.accounts.campaign;
let creator = &mut ctx.accounts.creator;

if campaign.creator != creator.key() {
return Err(Unauthorized.into());
}

if campaign.cid != cid {
return Err(CampaignNotFound.into());
}

if !campaign.active {
return Err(InactiveCampaign.into());
}

campaign.active = false;

Ok(())
}

#[derive(Accounts)]
#[instruction(cid: u64)]
pub struct DeleteCampaignCtx<'info> {
#[account(
mut,
seeds = [
b"campaign",
cid.to_le_bytes().as_ref()
],
bump
)]
pub campaign: Account<'info, Campaign>,

#[account(mut)]
pub creator: Signer<'info>,
pub system_program: Program<'info, System>,
}
86 changes: 86 additions & 0 deletions anchor/programs/fundus/src/instructions/donate.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
use crate::constants::ANCHOR_DISCRIMINATOR_SIZE;
use crate::errors::ErrorCode::*;
use crate::states::{Campaign, Transaction};
use anchor_lang::prelude::*;

pub fn donate(ctx: Context<DonateCtx>, cid: u64, amount: u64) -> Result<()> {
let campaign = &mut ctx.accounts.campaign;
let donor = &mut ctx.accounts.donor;
let transaction = &mut ctx.accounts.transaction;

if campaign.cid != cid {
return Err(CampaignNotFound.into());
}

if !campaign.active {
return Err(InactiveCampaign.into());
}

if amount < 1_000_000_000 {
return Err(InvalidGoalAmount.into());
}

if campaign.amount_raised >= campaign.goal {
return Err(CampaignGoalActualized.into());
}

let tx_instruction = anchor_lang::solana_program::system_instruction::transfer(
&donor.key(),
&campaign.key(),
amount,
);

let result = anchor_lang::solana_program::program::invoke(
&tx_instruction,
&[donor.to_account_info(), campaign.to_account_info()],
);

if let Err(e) = result {
msg!("Donation transfer failed: {:?}", e);
return Err(e.into());
}

campaign.amount_raised += amount;
campaign.balance += amount;
campaign.donors += 1;

transaction.amount = amount;
transaction.cid = cid;
transaction.owner = donor.key();
transaction.timestamp = Clock::get()?.unix_timestamp as u64;
transaction.credited = true;

Ok(())
}

#[derive(Accounts)]
#[instruction(cid: u64)]
pub struct DonateCtx<'info> {
#[account(
mut,
seeds = [
b"campaign",
cid.to_le_bytes().as_ref()
],
bump
)]
pub campaign: Account<'info, Campaign>,

#[account(
init,
payer = donor,
space = ANCHOR_DISCRIMINATOR_SIZE + Transaction::INIT_SPACE,
seeds = [
b"donor",
donor.key().as_ref(),
cid.to_le_bytes().as_ref(),
(campaign.donors + 1).to_le_bytes().as_ref()
],
bump
)]
pub transaction: Account<'info, Transaction>,

#[account(mut)]
pub donor: Signer<'info>,
pub system_program: Program<'info, System>,
}
37 changes: 37 additions & 0 deletions anchor/programs/fundus/src/instructions/initialize.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use anchor_lang::prelude::*;

use crate::constants::ANCHOR_DISCRIMINATOR_SIZE;
use crate::errors::ErrorCode::AlreadyInitialized;
use crate::states::ProgramState;

pub fn initialize(ctx: Context<InitializeCtx>) -> Result<()> {
let state = &mut ctx.accounts.program_state;
let deployer = &ctx.accounts.deployer;

if state.initialized {
return Err(AlreadyInitialized.into());
}

state.campaign_count = 0;
state.platform_fee = 5;
state.platform_address = deployer.key();
state.initialized = true;

Ok(())
}

#[derive(Accounts)]
pub struct InitializeCtx<'info> {
#[account(
init,
payer = deployer,
space = ANCHOR_DISCRIMINATOR_SIZE + ProgramState::INIT_SPACE,
seeds = [b"program_state"],
bump
)]
pub program_state: Account<'info, ProgramState>,

#[account(mut)]
pub deployer: Signer<'info>,
pub system_program: Program<'info, System>,
}
15 changes: 15 additions & 0 deletions anchor/programs/fundus/src/instructions/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
pub mod create_campaign;
pub mod delete_campaign;
pub mod donate;
pub mod initialize;
pub mod update_campaign;
pub mod update_platform_settings;
pub mod withdraw;

pub use create_campaign::*;
pub use delete_campaign::*;
pub use donate::*;
pub use initialize::*;
pub use update_campaign::*;
pub use update_platform_settings::*;
pub use withdraw::*;
61 changes: 61 additions & 0 deletions anchor/programs/fundus/src/instructions/update_campaign.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use crate::errors::ErrorCode::*;
use crate::states::Campaign;
use anchor_lang::prelude::*;

pub fn update_campaign(
ctx: Context<UpdateCampaignCtx>,
cid: u64,
title: String,
description: String,
image_url: String,
goal: u64,
) -> Result<()> {
let campaign = &mut ctx.accounts.campaign;
let creator = &mut ctx.accounts.creator;

if campaign.creator != creator.key() {
return Err(Unauthorized.into());
}

if campaign.cid != cid {
return Err(CampaignNotFound.into());
}

if title.len() > 64 {
return Err(TitleTooLong.into());
}
if description.len() > 512 {
return Err(DescriptionTooLong.into());
}
if image_url.len() > 256 {
return Err(ImageUrlTooLong.into());
}
if goal < 1_000_000_000 {
return Err(InvalidGoalAmount.into());
}

campaign.title = title;
campaign.description = description;
campaign.image_url = image_url;
campaign.goal = goal;

Ok(())
}

#[derive(Accounts)]
#[instruction(cid: u64)]
pub struct UpdateCampaignCtx<'info> {
#[account(
mut,
seeds = [
b"campaign",
cid.to_le_bytes().as_ref()
],
bump
)]
pub campaign: Account<'info, Campaign>,

#[account(mut)]
pub creator: Signer<'info>,
pub system_program: Program<'info, System>,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use crate::errors::ErrorCode::*;
use crate::states::ProgramState;
use anchor_lang::prelude::*;

pub fn update_platform_settings(
ctx: Context<UpdatePlatformSettingsCtx>,
new_platform_fee: u64,
) -> Result<()> {
let state = &mut ctx.accounts.program_state;
let updater = &ctx.accounts.updater;

if updater.key() != state.platform_address {
return Err(Unauthorized.into());
}

if !(1..=15).contains(&new_platform_fee) {
return Err(InvalidPlatformFee.into());
}

state.platform_fee = new_platform_fee;

Ok(())
}

#[derive(Accounts)]
pub struct UpdatePlatformSettingsCtx<'info> {
#[account(mut)]
pub updater: Signer<'info>,

#[account(
mut,
seeds = [b"program_state"],
bump
)]
pub program_state: Account<'info, ProgramState>,
}
Loading

0 comments on commit b72b031

Please sign in to comment.