Skip to content

Commit e54dc37

Browse files
committed
perf(contracts): optimize on-chain storage usage to reduce gas costs
Refactors Soroban contract storage patterns across the CrowdPass contracts to minimize ledger entry count and storage rent costs. - Replace string-based keys with symbol_short! equivalents - Consolidate related data fields into packed storage structs - Migrate transient data from persistent to temporary storage - Add instance storage for frequently-read contract configuration - Remove redundant storage reads via local variable caching - Add TTL extension calls to prevent unintended entry expiration Closes #203
1 parent 10f5078 commit e54dc37

8 files changed

Lines changed: 324 additions & 233 deletions

File tree

soroban-contract/contracts/dao_governance/src/lib.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ pub struct DaoConfig {
9898
pub max_members: u32,
9999
}
100100

101+
// STORAGE: removed the redundant `Votes(Address, u32)` per-voter entry —
102+
// it duplicated data already stored in `ProposalVotes(proposal_id)` (a Map
103+
// keyed by voter). One write per vote saved, scaling with participation.
101104
#[contracttype]
102105
#[derive(Clone, Debug, Eq, PartialEq)]
103106
pub enum DataKey {
@@ -107,7 +110,6 @@ pub enum DataKey {
107110
Member(Address),
108111
AllMembers,
109112
ProposalCount,
110-
Votes(Address, u32), // (voter, proposal_id)
111113
}
112114

113115
#[contract]
@@ -323,20 +325,28 @@ impl DaoGovernance {
323325
return Err(DaoError::ProposalNotActive);
324326
}
325327

326-
// Check if already voted
327-
if env.storage().persistent().has(&DataKey::Votes(voter.clone(), proposal_id)) {
328+
// STORAGE: read the per-proposal vote map once, use it both for the
329+
// "already voted?" guard AND to record the new vote. Previously each
330+
// vote also wrote a redundant `Votes(voter, proposal_id)` entry that
331+
// duplicated data already stored in `ProposalVotes(proposal_id)` —
332+
// pure rent overhead per voter, scaling with proposal participation.
333+
let config = Self::get_config(&env);
334+
let mut votes: Map<Address, Vote> = env
335+
.storage()
336+
.persistent()
337+
.get(&DataKey::ProposalVotes(proposal_id))
338+
.unwrap_or(Map::new(&env));
339+
340+
if votes.contains_key(voter.clone()) {
328341
return Err(DaoError::AlreadyVoted);
329342
}
330343

331-
// Get voting power
332-
let config = Self::get_config(&env);
333344
let voting_power = Self::get_voting_power(&env, &voter, &config.voting_token);
334345

335346
if voting_power == 0 {
336347
return Err(DaoError::InsufficientVotingPower);
337348
}
338349

339-
// Record vote
340350
let vote = Vote {
341351
voter: voter.clone(),
342352
proposal_id,
@@ -345,15 +355,6 @@ impl DaoGovernance {
345355
timestamp: current_time,
346356
};
347357

348-
env.storage().persistent().set(&DataKey::Votes(voter.clone(), proposal_id), &vote);
349-
350-
// Update proposal votes
351-
let mut votes: Map<Address, Vote> = env
352-
.storage()
353-
.persistent()
354-
.get(&DataKey::ProposalVotes(proposal_id))
355-
.unwrap_or(Map::new(&env));
356-
357358
votes.set(voter.clone(), vote);
358359
env.storage().persistent().set(&DataKey::ProposalVotes(proposal_id), &votes);
359360

0 commit comments

Comments
 (0)