Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev #17

Merged
merged 2 commits into from
Feb 29, 2024
Merged

Dev #17

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
52 changes: 17 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,49 +29,20 @@
</p>

## 📝 About

Exploring the development of a crowdfunding smart contract on the Solana blockchain, leveraging the Rust programming language and the Anchor framework. Crowdfunding has emerged as a popular method for raising funds for various projects, initiatives, and charitable causes, and blockchain technology introduces new possibilities for enhancing transparency, security, and efficiency in this process.

## 🏛️ Architecture

<p align="center">
<br>
<img src="./public/system-design.png" alt="system design"/>
<br>
</p>

```sh
- User Interface
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ Frontend │ │ Frontend │ │ Frontend │
└───────┬───────┘ └───────┬───────┘ └───────┬───────┘
│ │ │
▼ ▼ ▼
┌───────────────┐ ┌───────────────┐ ┌───────────────┐
│ User Wallet │ │ User Wallet │ │ User Wallet │
└───────────────┘ └───────────────┘ └───────────────┘
│ │ │
└───────────────┬──────────┼───────────────┬──────────┼───────────────┐
▼ │ ▼ │ │
┌───────────────┐ │ ┌───────────────┐ │ Smart │
│ Solana RPC │ │ │ Solana RPC │ │ Contracts │
└───────────────┘ │ └───────────────┘ │ │
│ │ │ │ │
└──────────┼──────────────┼──────────┼───────────────┘
▼ ▼ │
┌─────────────────────────┐ │
│ Anchor Lang Program │◀──────┘
└─────────────────────────┘
│ │ │
│ │ │
┌────────────┘ │ └────────────┐
│ │ │
┌────────────────┐┌────────────────┐┌────────────────┐
│ Create ││ Withdraw ││ Donate │
│ Function ││ Function ││ Function │
└────────────────┘└────────────────┘└────────────────┘
```

## 🎯 Features
The crowdfunding smart contract implemented using the Anchor framework on the Solana blockchain offers a range of features designed to enhance the fundraising experience while ensuring transparency, security, and efficiency.

The crowdfunding smart contract implemented using the Anchor framework on the Solana blockchain offers a range of features designed to enhance the fundraising experience while ensuring transparency, security, and efficiency.

1. Campaign Creation:

Expand Down Expand Up @@ -122,6 +93,17 @@ category : "which type of campaign"
8. Immutable Record Keeping:

- Transactional data and campaign details recorded on the Solana blockchain are immutable and tamper-proof, providing a permanent and verifiable record of fundraising activities.
Immutable records enhance auditability, mitigating the risk of fraud or data manipulation and fostering trust among stakeholders.
Immutable records enhance auditability, mitigating the risk of fraud or data manipulation and fostering trust among stakeholders.

##
## 🗂️ Folder Structure.

```sh
CrowdFund/
├── programs/smart-contracts/
├ ├── src/
├ ├── lib.rs (contains smart contracts code)
├── tests/
├── smart-contracts.ts (contains test cases for smart contracts)


```
206 changes: 121 additions & 85 deletions programs/smart-contracts/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,111 +1,147 @@
use anchor_lang::prelude::*;
use anchor_lang::solana_program::entrypoint::ProgramResult;

declare_id!("HYCWfDk8ZU8SF5oF9CE2CyChgBU6yndn82Umo4iE1Q9T");
declare_id!("E4fXqx7ybioeqZsHWNndNnqnpQ93DnV99gKnnxWXjvdu");

#[program]
pub mod smart_contracts {
use super::*;

//creates a campaign
pub fn create(
ctx: Context<Create>,
name: String,
description: String,
target_amount: u64,
project_url: String,
progress_update_url: String,
project_image_url: String,
category: String
) -> ProgramResult {
let campaign = &mut ctx.accounts.campaign;
campaign.name = name;
campaign.description = description;
campaign.target_amount = target_amount;
campaign.project_url = project_url;
campaign.progress_update_url = progress_update_url;
campaign.project_image_url = project_image_url;
campaign.category = category;
campaign.amount_donated = 0;
campaign.amount_withdrawn = 0;
campaign.admin = *ctx.accounts.user.key;
Ok(())
use super::*;

//creates a campaign
pub fn create(
ctx: Context<Create>,
name: String,
description: String,
target_amount: u64,
project_url: String,
progress_update_url: String,
project_image_url: String,
category: String
) -> ProgramResult {
let campaign = &mut ctx.accounts.campaign;
campaign.name = name;
campaign.description = description;
campaign.target_amount = target_amount;
campaign.project_url = project_url;
campaign.progress_update_url = progress_update_url;
campaign.project_image_url = project_image_url;
campaign.category = category;
campaign.amount_donated = 0;
campaign.amount_withdrawn = 0;
campaign.admin = *ctx.accounts.user.key;
Ok(())
}



//Withdraw from a campaign
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> ProgramResult {
let campaign = &mut ctx.accounts.campaign;
let user = &mut ctx.accounts.user;
//restricts Withdrawal to campaign admin
if campaign.admin != *user.key {
return Err(ProgramError::IncorrectProgramId);
}
//Withdraw from a campaign
pub fn withdraw(ctx: Context<Withdraw>, amount: u64) -> ProgramResult {
let campaign = &mut ctx.accounts.campaign;
let user = &mut ctx.accounts.user;
//restricts Withdrawal to campaign admin
if campaign.admin != *user.key {
return Err(ProgramError::IncorrectProgramId);
}
let rent_balance = Rent::get()?.minimum_balance(campaign.to_account_info().data_len());
if **campaign.to_account_info().lamports.borrow() - rent_balance < amount {
return Err(ProgramError::InsufficientFunds);
}
**campaign.to_account_info().try_borrow_mut_lamports()? -= amount;
**user.to_account_info().try_borrow_mut_lamports()? += amount;
(&mut ctx.accounts.campaign).amount_withdrawn += amount;
Ok(())
let rent_balance = Rent::get()?.minimum_balance(campaign.to_account_info().data_len());
if **campaign.to_account_info().lamports.borrow() - rent_balance < amount {
return Err(ProgramError::InsufficientFunds);
}
//Donate to a campaign
pub fn donate(ctx: Context<Donate>, amount: u64) -> ProgramResult {
let ix = anchor_lang::solana_program::system_instruction::transfer(
&ctx.accounts.user.key(),
&ctx.accounts.campaign.key(),
amount
);
anchor_lang::solana_program::program::invoke(
&ix,
&[ctx.accounts.user.to_account_info(), ctx.accounts.campaign.to_account_info()]
);
(&mut ctx.accounts.campaign).amount_donated += amount;
Ok(())
**campaign.to_account_info().try_borrow_mut_lamports()? -= amount;
**user.to_account_info().try_borrow_mut_lamports()? += amount;
(&mut ctx.accounts.campaign).amount_withdrawn += amount;
Ok(())
}



//Donate to a campaign
pub fn donate(ctx: Context<Donate>, amount: u64) -> ProgramResult {
let ix = anchor_lang::solana_program::system_instruction::transfer(
&ctx.accounts.user.key(),
&ctx.accounts.campaign.key(),
amount
);
// Store the result of the invoke function call
let result = anchor_lang::solana_program::program::invoke(
&ix,
&[ctx.accounts.user.to_account_info(), ctx.accounts.campaign.to_account_info()]
);
// Check if the invoke operation was successful
if let Err(e) = result {
return Err(e.into()); // Convert the error to a ProgramResult
}
// Proceed with the rest of the function
(&mut ctx.accounts.campaign).amount_donated += amount;
Ok(())
}

//Get the campaign
pub fn get_campaign(ctx: Context<GetCampaign>) -> ProgramResult {
let campaign = &ctx.accounts.campaign;
let user = &ctx.accounts.user;
if campaign.admin != *user.key {
return Err(ProgramError::IncorrectProgramId);
}
Ok(())
}


}

#[derive(Accounts)]
pub struct Create<'info> {
#[account(
init,
payer = user,
space = 9000,
seeds = [b"CROWDFUND".as_ref(), user.key().as_ref()],
bump
)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(
init,
payer = user,
space = 9000,
seeds = [b"CROWDFUND".as_ref(), user.key().as_ref()],
bump
)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}


#[derive(Accounts)]
pub struct Withdraw<'info> {
#[account(mut)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
#[account(mut)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
}



#[derive(Accounts)]
pub struct Donate<'info> {
#[account(mut)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
#[account(mut)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
pub system_program: Program<'info, System>,
}

#[derive(Accounts)]
pub struct GetCampaign<'info> {
#[account(mut)]
pub campaign: Account<'info, Campaign>,
#[account(mut)]
pub user: Signer<'info>,
}


#[account]
pub struct Campaign {
pub admin: Pubkey,
pub name: String,
pub description: String,
pub target_amount: u64,
pub project_url: String,
pub progress_update_url: String,
pub project_image_url: String,
pub category: String,
pub amount_donated: u64,
pub amount_withdrawn: u64,
pub admin: Pubkey,
pub name: String,
pub description: String,
pub target_amount: u64,
pub project_url: String,
pub progress_update_url: String,
pub project_image_url: String,
pub category: String,
pub amount_donated: u64,
pub amount_withdrawn: u64,
}
Binary file modified public/system-design.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading