Skip to content

Commit 7625ea1

Browse files
authored
Merge pull request #153 from Mmesolove/feature/pool-contributions-pagination
feat: implement get_pool_contributions_paginated function
2 parents 183ff2d + 05c6949 commit 7625ea1

File tree

9 files changed

+274
-117
lines changed

9 files changed

+274
-117
lines changed

contract/contract/src/base/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,7 @@ pub enum StorageKey {
224224
CampaignDonor(BytesN<32>, Address),
225225
Contribution(BytesN<32>, Address),
226226
PoolContribution(u64, Address),
227+
PoolContributors(u64),
227228

228229
NextPoolId,
229230
IsPaused,

contract/contract/src/crowdfunding.rs

Lines changed: 47 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -95,11 +95,6 @@ impl CrowdfundingTrait for CrowdfundingContract {
9595
}
9696
creator.require_auth();
9797

98-
// Check if creator is blacklisted
99-
if Self::is_blacklisted(env.clone(), creator.clone()) {
100-
return Err(CrowdfundingError::UserBlacklisted);
101-
}
102-
10398
if title.is_empty() {
10499
return Err(CrowdfundingError::InvalidTitle);
105100
}
@@ -438,11 +433,6 @@ impl CrowdfundingTrait for CrowdfundingContract {
438433
return Err(CrowdfundingError::CampaignCancelled);
439434
}
440435

441-
// Check if donor is blacklisted
442-
if Self::is_blacklisted(env.clone(), donor.clone()) {
443-
return Err(CrowdfundingError::UserBlacklisted);
444-
}
445-
446436
// Validate donation amount
447437
if amount <= 0 {
448438
return Err(CrowdfundingError::InvalidDonationAmount);
@@ -1146,6 +1136,20 @@ impl CrowdfundingTrait for CrowdfundingContract {
11461136
.instance()
11471137
.set(&contributor_key, &updated_contribution);
11481138

1139+
// Track contributor in the list for pagination
1140+
if existing_contribution.amount == 0 {
1141+
let contributors_key = StorageKey::PoolContributors(pool_id);
1142+
let mut contributors: Vec<Address> = env
1143+
.storage()
1144+
.instance()
1145+
.get(&contributors_key)
1146+
.unwrap_or(Vec::new(&env));
1147+
contributors.push_back(contributor.clone());
1148+
env.storage()
1149+
.instance()
1150+
.set(&contributors_key, &contributors);
1151+
}
1152+
11491153
// Emit event
11501154
events::contribution(
11511155
&env,
@@ -1577,101 +1581,53 @@ impl CrowdfundingTrait for CrowdfundingContract {
15771581
String::from_str(&env, "1.2.0")
15781582
}
15791583

1580-
fn blacklist_address(env: Env, address: Address) -> Result<(), CrowdfundingError> {
1581-
let admin: Address = env
1582-
.storage()
1583-
.instance()
1584-
.get(&StorageKey::Admin)
1585-
.ok_or(CrowdfundingError::NotInitialized)?;
1586-
admin.require_auth();
1587-
1588-
let blacklist_key = StorageKey::Blacklist(address.clone());
1589-
env.storage().persistent().set(&blacklist_key, &true);
1590-
1591-
events::address_blacklisted(&env, admin, address);
1592-
1593-
Ok(())
1594-
}
1595-
1596-
fn unblacklist_address(env: Env, address: Address) -> Result<(), CrowdfundingError> {
1597-
let admin: Address = env
1598-
.storage()
1599-
.instance()
1600-
.get(&StorageKey::Admin)
1601-
.ok_or(CrowdfundingError::NotInitialized)?;
1602-
admin.require_auth();
1603-
1604-
let blacklist_key = StorageKey::Blacklist(address.clone());
1605-
env.storage().persistent().remove(&blacklist_key);
1606-
1607-
events::address_unblacklisted(&env, admin, address);
1608-
1609-
Ok(())
1610-
}
1611-
1612-
fn is_blacklisted(env: Env, address: Address) -> bool {
1613-
let blacklist_key = StorageKey::Blacklist(address);
1614-
env.storage()
1615-
.persistent()
1616-
.get(&blacklist_key)
1617-
.unwrap_or(false)
1618-
}
1619-
1620-
fn update_pool_metadata_hash(
1584+
fn get_pool_contributions_paginated(
16211585
env: Env,
16221586
pool_id: u64,
1623-
caller: Address,
1624-
new_metadata_hash: String,
1625-
) -> Result<(), CrowdfundingError> {
1626-
if CrowdfundingContract::is_paused(env.clone()) {
1627-
return Err(CrowdfundingError::ContractPaused);
1628-
}
1629-
caller.require_auth();
1630-
1631-
// Validate metadata hash length
1632-
if new_metadata_hash.len() > MAX_HASH_LENGTH {
1633-
return Err(CrowdfundingError::InvalidMetadata);
1634-
}
1635-
1587+
offset: u32,
1588+
limit: u32,
1589+
) -> Result<Vec<PoolContribution>, CrowdfundingError> {
1590+
// Validate pool exist
16361591
// Check if pool exists
16371592
let pool_key = StorageKey::Pool(pool_id);
16381593
if !env.storage().instance().has(&pool_key) {
16391594
return Err(CrowdfundingError::PoolNotFound);
16401595
}
16411596

1642-
// Verify caller is the pool creator
1643-
let creator_key = StorageKey::PoolCreator(pool_id);
1644-
let creator: Address = env
1597+
// Get the list of contributors
1598+
let contributors_key = StorageKey::PoolContributors(pool_id);
1599+
let contributors: Vec<Address> = env
16451600
.storage()
16461601
.instance()
1647-
.get(&creator_key)
1648-
.ok_or(CrowdfundingError::PoolNotFound)?;
1649-
1650-
if caller != creator {
1651-
return Err(CrowdfundingError::Unauthorized);
1652-
}
1602+
.get(&contributors_key)
1603+
.unwrap_or(Vec::new(&env));
16531604

1654-
// Get existing metadata
1655-
let metadata_key = StorageKey::PoolMetadata(pool_id);
1656-
let mut metadata: PoolMetadata =
1657-
env.storage()
1658-
.persistent()
1659-
.get(&metadata_key)
1660-
.unwrap_or(PoolMetadata {
1661-
description: String::from_str(&env, ""),
1662-
external_url: String::from_str(&env, ""),
1663-
image_hash: String::from_str(&env, ""),
1664-
});
1605+
let total_contributors = contributors.len();
16651606

1666-
// Update the image hash
1667-
metadata.image_hash = new_metadata_hash.clone();
1607+
// Validate offset
1608+
if offset >= total_contributors {
1609+
return Ok(Vec::new(&env));
1610+
}
16681611

1669-
// Save updated metadata
1670-
env.storage().persistent().set(&metadata_key, &metadata);
1612+
// Calculate the end index
1613+
let end = (offset + limit).min(total_contributors);
16711614

1672-
// Emit event
1673-
events::pool_metadata_updated(&env, pool_id, caller, new_metadata_hash);
1615+
// Collect contributions for the requested range
1616+
let mut result = Vec::new(&env);
1617+
for i in offset..end {
1618+
if let Some(contributor_addr) = contributors.get(i) {
1619+
let contribution_key =
1620+
StorageKey::PoolContribution(pool_id, contributor_addr.clone());
1621+
if let Some(contribution) = env
1622+
.storage()
1623+
.instance()
1624+
.get::<StorageKey, PoolContribution>(&contribution_key)
1625+
{
1626+
result.push_back(contribution);
1627+
}
1628+
}
1629+
}
16741630

1675-
Ok(())
1631+
Ok(result)
16761632
}
16771633
}

contract/contract/src/interfaces/crowdfunding.rs

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ use soroban_sdk::{Address, BytesN, Env, String, Vec};
22

33
use crate::base::{
44
errors::CrowdfundingError,
5-
types::{CampaignDetails, CampaignLifecycleStatus, PoolConfig, PoolMetadata, PoolState},
5+
types::{
6+
CampaignDetails, CampaignLifecycleStatus, PoolConfig, PoolContribution, PoolMetadata,
7+
PoolState,
8+
},
69
};
710

811
pub trait CrowdfundingTrait {
@@ -174,18 +177,12 @@ pub trait CrowdfundingTrait {
174177

175178
fn get_contract_version(env: Env) -> String;
176179

177-
fn blacklist_address(env: Env, address: Address) -> Result<(), CrowdfundingError>;
178-
179-
fn unblacklist_address(env: Env, address: Address) -> Result<(), CrowdfundingError>;
180-
181-
fn is_blacklisted(env: Env, address: Address) -> bool;
182-
183-
fn update_pool_metadata_hash(
180+
fn get_pool_contributions_paginated(
184181
env: Env,
185182
pool_id: u64,
186-
caller: Address,
187-
new_metadata_hash: String,
188-
) -> Result<(), CrowdfundingError>;
183+
offset: u32,
184+
limit: u32,
185+
) -> Result<Vec<PoolContribution>, CrowdfundingError>;
189186

190187
fn get_pool_remaining_time(env: Env, pool_id: u64) -> Result<u64, CrowdfundingError>;
191188
}

contract/contract/test/close_pool_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ fn test_close_pool_nonexistent() {
164164
#[test]
165165
fn test_close_pool_unauthorized() {
166166
let env = Env::default();
167-
let (client, admin, _) = setup_test(&env);
167+
let (client, _admin, _) = setup_test(&env);
168168

169169
let creator = Address::generate(&env);
170170
let pool_id = create_test_pool(&client, &env, &creator);

contract/contract/test/crowdfunding_test.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ fn test_extend_campaign_deadline_invalid_auth() {
321321
let (client, _, token_address) = setup_test(&env);
322322

323323
let creator = Address::generate(&env);
324-
let malicious = Address::generate(&env);
324+
let _malicious = Address::generate(&env);
325325
let campaign_id = create_test_campaign_id(&env, 13);
326326
let title = String::from_str(&env, "Extend Auth");
327327
let goal = 1_000_000i128;
@@ -336,7 +336,7 @@ fn test_extend_campaign_deadline_invalid_auth() {
336336
&token_address,
337337
);
338338

339-
let new_deadline = env.ledger().timestamp() + 2 * 86400;
339+
let _new_deadline = env.ledger().timestamp() + 2 * 86400;
340340

341341
// mock_auths to malicious address simulating a fail
342342
// However, in mock_auths, if the caller requires the auth of creator, it will panic
@@ -382,11 +382,11 @@ fn test_extend_campaign_too_long() {
382382
#[test]
383383
fn test_get_campaign_fee_history() {
384384
let env = Env::default();
385-
let (client, admin, token_address) = setup_test(&env);
385+
let (client, _admin, token_address) = setup_test(&env);
386386

387387
// Using token_admin pattern from other tests
388-
let token_admin = Address::generate(&env);
389-
let token_client = token::Client::new(&env, &token_address);
388+
let _token_admin = Address::generate(&env);
389+
let _token_client = token::Client::new(&env, &token_address);
390390
let token_admin_client = token::StellarAssetClient::new(&env, &token_address);
391391

392392
let creator = Address::generate(&env);
@@ -3422,7 +3422,7 @@ fn test_withdraw_platform_fees_insufficient_fees() {
34223422
#[test]
34233423
fn test_set_emergency_contact_success() {
34243424
let env = Env::default();
3425-
let (client, admin, _) = setup_test(&env);
3425+
let (client, _admin, _) = setup_test(&env);
34263426

34273427
let emergency_contact = Address::generate(&env);
34283428

@@ -3436,7 +3436,7 @@ fn test_set_emergency_contact_success() {
34363436
#[test]
34373437
fn test_set_emergency_contact_updates_existing() {
34383438
let env = Env::default();
3439-
let (client, admin, _) = setup_test(&env);
3439+
let (client, _admin, _) = setup_test(&env);
34403440

34413441
let contact1 = Address::generate(&env);
34423442
client.set_emergency_contact(&contact1);

0 commit comments

Comments
 (0)