Skip to content
Closed
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
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -167,10 +167,14 @@ Found a bug? Have a feature request? Open an **issue** on GitHub:

## 10. Community & Support
### Join Our Channels:
- **Discord:** [Join Here](https://discord.gg/juUmBmwC3s)
* [Join Here](https://discord.gg/juUmBmwC3s)
- **GitHub Discussions:** [Boundless Discussions](https://github.com/0xdevcollins/boundless/discussions)
- **Twitter:** [Follow @boundless_fi](https://x.com/boundless_fi)

### Support
- **Benjamin:** [https://t.me/kitch_the_dev](https://t.me/kitch_the_dev)
- **Collins:** [https://t.me/devcollinss](https://t.me/devcollinss)

---

Thank you for contributing to Boundless! 🚀
Expand Down
93 changes: 86 additions & 7 deletions contracts/project_contract/src/create_project/create_project.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,98 @@
use soroban_sdk::{contract, contractimpl, Address, Env, String};
use crate::project::Project;

use super::{CreateProjectEvent, CreateProjectStorage};

use crate::project::Project;
use soroban_sdk::{contract, contractimpl, Address, Env, String};

#[contract]
pub struct CreateProject;

#[contractimpl]
impl CreateProject {
pub fn create_project(env: Env, project_id: String, creator: Address, metadata_uri: String, funding_target: u64, milestone_count: u32) {
let project = Project::new(&env, project_id.clone(), creator.clone(), metadata_uri, funding_target, milestone_count);

pub fn create_project(
env: Env,
project_id: String,
creator: Address,
metadata_uri: String,
funding_target: u64,
milestone_count: u32,
) {
if CreateProjectStorage::project_exists(&env, &project_id) {
panic!("Project ID already exists");
}
if funding_target == 0 {
panic!("Funding target must be greater than zero");
}
let project = Project::new(
&env,
project_id.clone(),
creator.clone(),
metadata_uri,
funding_target,
milestone_count,
);

CreateProjectStorage::save(&env, &project);

CreateProjectEvent::emit(&env, project_id, creator);
}

pub fn get_project(env: Env, project_id: String) -> Option<Project> {
CreateProjectStorage::fetch_project(&env, &project_id)
}
pub fn update_project_metadata(
env: Env,
project_id: String,
creator: Address,
new_metadata_uri: String,
) {
let mut project =
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project does not exist");
if project.creator != creator {
panic!("Only the project creator can modify metadata");
}

if project.is_closed {
panic!("Cannot modify a closed project");
}
project.metadata_uri = new_metadata_uri;

CreateProjectStorage::save(&env, &project);
}

pub fn modify_milestone(
env: Env,
project_id: String,
caller: Address,
new_milestone_count: u32,
) {
let mut project =
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project does not exist");

if project.creator != caller {
panic!("Only the project creator can modify milestones");
}

if project.is_closed {
panic!("Cannot modify milestones for a closed project");
}

project.milestone_count = new_milestone_count;

CreateProjectStorage::save(&env, &project);
}

pub fn close_project(env: Env, project_id: String, creator: Address) {
let mut project =
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project does not exist");

if project.creator != creator {
panic!("Only the project creator can close the project");
}

if project.is_closed {
panic!("Project is already closed");
}
project.is_closed = true;

CreateProjectStorage::save(&env, &project);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use soroban_sdk::{Address, Env, String, Symbol};
use soroban_sdk::{Env, Address, String, Symbol};

pub struct CreateProjectEvent;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,40 @@
use soroban_sdk::{Env, String};
use crate::project::Project;
use soroban_sdk::{symbol_short, Env, Map, String};

pub struct CreateProjectStorage;

impl CreateProjectStorage {
pub fn save(env: &Env, project: &Project) {
env.storage().instance().set(&project.project_id, project);
let mut projects: Map<String, Project> = env
.storage()
.persistent()
.get(&symbol_short!("projects"))
.unwrap_or(Map::new(&env));

projects.set(project.project_id.clone(), project.clone());

env.storage()
.persistent()
.set(&symbol_short!("projects"), &projects);
}

pub fn get(env: &Env, project_id: &String) -> Option<Project> {
env.storage().instance().get(project_id)

pub fn fetch_project(env: &Env, project_id: &String) -> Option<Project> {
let projects: Map<String, Project> = env
.storage()
.persistent()
.get(&symbol_short!("projects"))
.unwrap_or(Map::new(&env));

projects.get(project_id.clone())
}

pub fn project_exists(env: &Env, project_id: &String) -> bool {
let projects: Map<String, Project> = env
.storage()
.persistent()
.get(&symbol_short!("projects"))
.unwrap_or(Map::new(&env));

projects.contains_key(project_id.clone())
}
}
2 changes: 1 addition & 1 deletion contracts/project_contract/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

mod project;
mod create_project;
// mod vote_project;
// pub use crate::vote_project::
// mod fund_project;
// mod release_milestone;
// mod refund;
Expand Down
214 changes: 181 additions & 33 deletions contracts/project_contract/src/tests/create_project_test.rs
Original file line number Diff line number Diff line change
@@ -1,34 +1,182 @@
#[cfg(test)]
mod tests {
use soroban_sdk::{Env, Address, String};
use crate::create_project::{CreateProjectStorage, CreateProject, CreateProjectClient};

#[test]
fn test_create_project() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator_key = String::from_str(&env, "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF");
let creator = Address::from_string(&creator_key);
let project_id = String::from_str(&env, "project-1");
let meta_uri = String::from_str(&env, "ipfs://QmExample");

client.create_project(&project_id, &creator, &meta_uri, &10000, &5);

let project = env.as_contract(&contract_id, || {
CreateProjectStorage::get(&env, &project_id).expect("Project not found")
});

assert_eq!(project.project_id, project_id);
assert_eq!(project.creator, creator);
assert_eq!(project.metadata_uri, meta_uri);
assert_eq!(project.funding_target, 10000);
assert_eq!(project.milestone_count, 5);
assert_eq!(project.total_funded, 0);
assert_eq!(project.current_milestone, 0);
assert_eq!(project.validated, false);
assert_eq!(project.is_successful, false);
assert_eq!(project.is_closed, false);
}
#![cfg(test)]

use crate::create_project::{CreateProject, CreateProjectClient, CreateProjectStorage};
use soroban_sdk::{testutils::Address, Env, String};

#[test]
fn test_create_project_success() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

// let creator_key = String::from_str(
// &env,
// "GAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAWHF",
// );
let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");
let funding_target = 1000;
let milestone_count = 5;

client.create_project(
&project_id,
&creator,
&metadata_uri,
&funding_target,
&milestone_count,
);

let project = env.as_contract(&contract_id, || {
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project not found")
});

assert_eq!(project.project_id, project_id);
assert_eq!(project.creator, creator);
assert_eq!(project.metadata_uri, metadata_uri);
assert_eq!(project.funding_target, funding_target);
assert_eq!(project.milestone_count, milestone_count);
assert!(!project.is_closed);
}

#[test]
#[should_panic(expected = "Project ID already exists")]
fn test_create_project_duplicate_id_fails() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.create_project(&project_id, &creator, &metadata_uri, &2000, &3);
}

#[test]
#[should_panic(expected = "Funding target must be greater than zero")]
fn test_create_project_zero_funding_target_fails() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &0, &3);
}

#[test]
fn test_update_project_metadata_success() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");
let new_metadata_uri = String::from_str(&env, "ipfs://new-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.update_project_metadata(&project_id, &creator, &new_metadata_uri);

let project = env.as_contract(&contract_id, || {
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project not found")
});

assert_eq!(project.metadata_uri, new_metadata_uri);
}

#[test]
#[should_panic(expected = "Only the project creator can modify metadata")]
fn test_update_project_metadata_wrong_creator_fails() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let other_user = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.update_project_metadata(
&project_id,
&other_user,
&String::from_str(&env, "ipfs://new-metadata"),
);
}

#[test]
fn test_modify_milestone_success() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.modify_milestone(&project_id, &creator, &10);

let project = env.as_contract(&contract_id, || {
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project not found")
});

assert_eq!(project.milestone_count, 10);
}

#[test]
#[should_panic(expected = "Only the project creator can modify milestones")]
fn test_modify_milestone_wrong_caller_fails() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let other_user = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.modify_milestone(&project_id, &other_user, &10);
}

#[test]
fn test_close_project_success() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.close_project(&project_id, &creator);

let project = env.as_contract(&contract_id, || {
CreateProjectStorage::fetch_project(&env, &project_id).expect("Project not found")
});

assert!(project.is_closed);
}

#[test]
#[should_panic(expected = "Only the project creator can close the project")]
fn test_close_project_wrong_caller_fails() {
let env = Env::default();
let contract_id = env.register(CreateProject, ());
let client = CreateProjectClient::new(&env, &contract_id);

let creator = <soroban_sdk::Address as Address>::generate(&env);
let other_user = <soroban_sdk::Address as Address>::generate(&env);
let project_id = String::from_str(&env, "test_project");
let metadata_uri = String::from_str(&env, "ipfs://example-metadata");

client.create_project(&project_id, &creator, &metadata_uri, &1000, &5);
client.close_project(&project_id, &other_user);
}
Loading
Loading