Skip to content
Merged
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
2 changes: 2 additions & 0 deletions contracts/maintenance-pool/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ pub enum Error {
ContractPaused = 13,
/// The deposit count has reached its maximum limit (issue #45).
DepositCountOverflow = 14,
/// The pool already holds `MAX_DEPOSITS` deposits (issue #94).
TooManyDeposits = 15,
}
14 changes: 14 additions & 0 deletions contracts/maintenance-pool/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ use mergefi_common::BPS_DENOMINATOR;
/// concept but applied per-deposit rather than per-pool.
pub const INACTIVITY_WINDOW: u64 = 90 * 24 * 60 * 60; // 90 days

/// Maximum number of deposits a single pool may record (issue #94).
/// `deposit` and `withdraw` refresh the TTL of every deposit sub-record in
/// a loop over `deposit_count`, so an unbounded count would make those
/// calls grow without limit, and Soroban caps a transaction footprint at
/// 100 ledger entries (a cap of 100 would already exceed it). Mirrors
/// `MAX_SPONSORS` in escrow/milestones.
pub const MAX_DEPOSITS: u32 = 50;

/// Current version of the storage schema. Incremented on breaking layout changes.
const CONTRACT_VERSION: u32 = 1;

Expand Down Expand Up @@ -123,6 +131,10 @@ mod contract {
return Err(Error::TokenMismatch);
}

if pool.deposit_count >= MAX_DEPOSITS {
return Err(Error::TooManyDeposits);
}

let token_client = token::Client::new(&env, &token);
token_client.transfer(&sponsor, env.current_contract_address(), &amount);

Expand Down Expand Up @@ -515,6 +527,8 @@ mod contract {
}
} // mod contract

pub use contract::*;

fn require_admin(env: &Env) -> Result<Address, Error> {
mergefi_common::require_admin::<DataKey>(env).ok_or(Error::NotInitialized)
}
Expand Down
35 changes: 31 additions & 4 deletions contracts/maintenance-pool/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,9 +770,33 @@ fn test_deposit_rejects_when_deposit_count_would_overflow() {
env.storage().persistent().set(&pkey, &pool);
});

// Calling deposit should now fail with DepositCountOverflow
// The MAX_DEPOSITS cap rejects long before u32 overflow is reachable.
let err = client.try_deposit(&10u64, &sponsor, &token_addr, &100i128);
assert_eq!(err, Err(Ok(Error::DepositCountOverflow)));
assert_eq!(err, Err(Ok(Error::TooManyDeposits)));
}

#[test]
fn test_deposit_rejects_beyond_max_deposits() {
let env = Env::default();
env.mock_all_auths();
let (_admin, _treasury, client) = setup(&env);

let token_admin = Address::generate(&env);
let (token_addr, asset_client, _token_client) = create_token(&env, &token_admin);
let sponsor = Address::generate(&env);
asset_client.mint(&sponsor, &1_000_000i128);

for _ in 0..crate::MAX_DEPOSITS {
client.deposit(&11u64, &sponsor, &token_addr, &1i128);
}
assert_eq!(client.get_pool(&11u64).deposit_count, crate::MAX_DEPOSITS);

let err = client.try_deposit(&11u64, &sponsor, &token_addr, &1i128);
assert_eq!(err, Err(Ok(Error::TooManyDeposits)));
assert_eq!(client.get_pool(&11u64).deposit_count, crate::MAX_DEPOSITS);

// Other pools are unaffected.
client.deposit(&12u64, &sponsor, &token_addr, &1i128);
}

// ─── upgrade (issue #246) ────────────────────────────────────────────────────
Expand Down Expand Up @@ -928,8 +952,11 @@ fn test_set_oracle_rotates_oracle_used_by_withdraw() {

// withdraw now requires the rotated oracle's authorization, not the old one's.
let auths = env.auths();
assert!(auths.iter().any(|(addr, _)| addr == new_oracle));
assert!(!auths.iter().any(|(addr, _)| addr == old_oracle));
assert!(auths.iter().any(|(addr, _)| *addr == new_oracle));
assert!(!auths.iter().any(|(addr, _)| *addr == old_oracle));
}

#[test]
fn test_set_treasury_requires_admin_auth() {
let env = Env::default();
env.mock_all_auths();
Expand Down