Skip to content

Commit a2be792

Browse files
Glam26Truphile
andauthored
safe commit (#1024)
Co-authored-by: Truphile <salidmonreal@gmail.com>
1 parent 735c9ea commit a2be792

3 files changed

Lines changed: 103 additions & 16 deletions

File tree

Cargo.lock

Lines changed: 43 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

contracts/settlement/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -809,7 +809,7 @@ impl CalloraSettlement {
809809
.persistent()
810810
.extend_ttl(&balance_key, 50000, 50000);
811811

812-
daily.amount = daily.amount.saturating_add(amount);
812+
daily.amount = daily.amount.checked_add(amount).ok_or(SettlementError::DailyWithdrawCapExceeded)?;
813813
env.storage().persistent().set(&today_key, &daily);
814814
env.storage()
815815
.persistent()

contracts/settlement/src/test_overflow_safe_math.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@
2020
//! `InsufficientDeveloperBalance` rather than wrapping.
2121
//!
2222
//! 6. **Normal (non-overflow) paths** still produce correct arithmetic results.
23+
//!
24+
//! 7. **`withdraw_developer_balance`** (daily accumulator) — `checked_add` raises
25+
//! `DailyWithdrawCapExceeded` rather than silently saturating on overflow.
2326
2427
extern crate std;
2528

@@ -82,6 +85,23 @@ fn poke_dev_balance(
8285
});
8386
}
8487

88+
/// Directly write a developer's `WithdrawalToday` state in persistent storage.
89+
fn poke_withdrawal_today(
90+
env: &Env,
91+
contract_id: &Address,
92+
developer: &Address,
93+
amount: i128,
94+
day: u64,
95+
) {
96+
use crate::types::DailyWithdrawState;
97+
env.as_contract(contract_id, || {
98+
env.storage().persistent().set(
99+
&StorageKey::WithdrawalToday(developer.clone()),
100+
&DailyWithdrawState { day, amount },
101+
);
102+
});
103+
}
104+
85105
/// Directly write the global pool's `total_balance` in instance storage.
86106
fn poke_global_pool_balance(env: &Env, contract_id: &Address, total_balance: i128) {
87107
use crate::types::GlobalPool;
@@ -304,6 +324,45 @@ fn withdraw_more_than_balance_returns_error() {
304324
);
305325
}
306326

327+
// ---------------------------------------------------------------------------
328+
// 6. withdraw_developer_balance — daily accumulator overflow -> DailyWithdrawCapExceeded
329+
// ---------------------------------------------------------------------------
330+
331+
/// When the developer has no cap set (0 = unlimited), the daily withdrawal
332+
/// accumulator still uses `checked_add` so that an i128 overflow fails
333+
/// loudly instead of silently saturating.
334+
#[test]
335+
fn withdraw_daily_amount_overflow_raises_error() {
336+
let (env, contract_id, _admin, vault, stored_usdc) = setup_with_usdc();
337+
let client = CalloraSettlementClient::new(&env, &contract_id);
338+
let developer = Address::generate(&env);
339+
340+
// Seed developer balance with a large amount so the withdrawal itself
341+
// does not fail with InsufficientDeveloperBalance.
342+
poke_dev_balance(&env, &contract_id, &developer, &stored_usdc, 1_000_000i128);
343+
344+
// Seed the same-day withdrawal accumulator to i128::MAX - 1 so the
345+
// next withdrawal's checked_add will overflow.
346+
let today = env.ledger().timestamp() / 86400;
347+
poke_withdrawal_today(&env, &contract_id, &developer, i128::MAX - 1, today);
348+
349+
// Mint enough USDC to the contract so the transfer can proceed
350+
// past the liquidity check.
351+
let token_admin_client = soroban_sdk::token::StellarAssetClient::new(&env, &stored_usdc);
352+
token_admin_client.mint(&contract_id, &1_000i128);
353+
354+
// Withdraw 1 micro-unit — must fail because daily.amount would overflow.
355+
let result = client.try_withdraw_developer_balance(&developer, &1i128, &None);
356+
assert!(
357+
result.is_err(),
358+
"daily accumulator overflow should fail, got {result:?}"
359+
);
360+
}
361+
362+
// ---------------------------------------------------------------------------
363+
// 7. Normal withdrawals still work correctly
364+
// ---------------------------------------------------------------------------
365+
307366
/// Exact-balance withdrawal succeeds and leaves the developer at zero.
308367
#[test]
309368
fn withdraw_exact_balance_succeeds() {

0 commit comments

Comments
 (0)