diff --git a/contracts/liquid_staking/src/lib.rs b/contracts/liquid_staking/src/lib.rs index f6170fc..e7c484b 100644 --- a/contracts/liquid_staking/src/lib.rs +++ b/contracts/liquid_staking/src/lib.rs @@ -75,7 +75,9 @@ impl LiquidStaking { .instance() .get(&DataKey::RewardPerTokenStored) .unwrap_or(0); - rpt += amount * PRECISION / total_staked; + rpt = rpt.checked_add( + amount.checked_mul(PRECISION).expect("rpt overflow") / total_staked + ).expect("rpt overflow"); env.storage() .instance() .set(&DataKey::RewardPerTokenStored, &rpt); @@ -116,7 +118,7 @@ impl LiquidStaking { ); env.storage().persistent().set(&DataKey::StakeAmount(token_id), &amount); - let lock_time = env.ledger().timestamp() + lock_duration; + let lock_time = env.ledger().timestamp().checked_add(lock_duration).expect("lock time overflow"); env.storage().persistent().set(&DataKey::StakeLockTime(token_id), &lock_time); let rpt: i128 = env.storage().instance().get(&DataKey::RewardPerTokenStored).unwrap_or(0); @@ -124,7 +126,7 @@ impl LiquidStaking { env.storage().persistent().set(&DataKey::NftRewards(token_id), &0_i128); let total: i128 = env.storage().instance().get(&DataKey::TotalStaked).unwrap_or(0); - env.storage().instance().set(&DataKey::TotalStaked, &(total + amount)); + env.storage().instance().set(&DataKey::TotalStaked, &total.checked_add(amount).expect("total staked overflow")); env.events().publish((symbol_short!("staked"), user), (token_id, amount, lock_time)); @@ -161,7 +163,7 @@ impl LiquidStaking { } let total: i128 = env.storage().instance().get(&DataKey::TotalStaked).unwrap_or(0); - env.storage().instance().set(&DataKey::TotalStaked, &(total - amount)); + env.storage().instance().set(&DataKey::TotalStaked, &total.checked_sub(amount).expect("total staked underflow")); let stake_token: Address = env.storage().instance().get(&DataKey::StakeToken).unwrap(); token::Client::new(&env, &stake_token).transfer( @@ -222,7 +224,9 @@ impl LiquidStaking { let amount: i128 = env.storage().persistent().get(&DataKey::StakeAmount(token_id)).unwrap_or(0); let accrued: i128 = env.storage().persistent().get(&DataKey::NftRewards(token_id)).unwrap_or(0); - let pending = accrued + amount * (rpt - nft_rpt) / PRECISION; + let pending = accrued.checked_add( + amount.checked_mul(rpt - nft_rpt).expect("rewards overflow") / PRECISION + ).expect("rewards overflow"); let lock_time: u64 = env.storage().persistent().get(&DataKey::StakeLockTime(token_id)).unwrap_or(0); StakeInfo { @@ -237,11 +241,11 @@ impl LiquidStaking { let rpt: i128 = env.storage().instance().get(&DataKey::RewardPerTokenStored).unwrap_or(0); let nft_rpt: i128 = env.storage().persistent().get(&DataKey::NftRewardPerTokenPaid(token_id)).unwrap_or(0); let amount: i128 = env.storage().persistent().get(&DataKey::StakeAmount(token_id)).unwrap_or(0); - let earned = amount * (rpt - nft_rpt) / PRECISION; + let earned = amount.checked_mul(rpt - nft_rpt).expect("rewards overflow") / PRECISION; if earned > 0 { let prev: i128 = env.storage().persistent().get(&DataKey::NftRewards(token_id)).unwrap_or(0); - env.storage().persistent().set(&DataKey::NftRewards(token_id), &(prev + earned)); + env.storage().persistent().set(&DataKey::NftRewards(token_id), &prev.checked_add(earned).expect("rewards overflow")); } env.storage().persistent().set(&DataKey::NftRewardPerTokenPaid(token_id), &rpt); diff --git a/contracts/nft_metadata/src/lib.rs b/contracts/nft_metadata/src/lib.rs index b1a1093..7916b73 100644 --- a/contracts/nft_metadata/src/lib.rs +++ b/contracts/nft_metadata/src/lib.rs @@ -223,7 +223,7 @@ impl NftMetadataContract { .instance() .get(&DataKey::TokenCounter) .unwrap_or(0); - let token_id = counter + 1; + let token_id = counter.checked_add(1).expect("token id overflow"); let metadata = NftMetadata { token_id, @@ -285,7 +285,7 @@ impl NftMetadataContract { .instance() .get(&DataKey::TokenCounter) .unwrap_or(0); - let token_id = counter + 1; + let token_id = counter.checked_add(1).expect("token id overflow"); let mut final_metadata = metadata; final_metadata.token_id = token_id; @@ -741,7 +741,8 @@ impl NftMetadataContract { .get(&DataKey::NftMetadata(token_id)) .expect("token not found"); - let royalty_amount = (sale_price * metadata.royalty_percentage as i128) / 10000; + let royalty_amount = sale_price + .checked_mul(metadata.royalty_percentage as i128).expect("royalty overflow") / 10000; (metadata.royalty_recipient, royalty_amount) } diff --git a/contracts/staking/src/lib.rs b/contracts/staking/src/lib.rs index f2a65ab..4184357 100644 --- a/contracts/staking/src/lib.rs +++ b/contracts/staking/src/lib.rs @@ -117,7 +117,9 @@ impl MultiTokenStaking { .get(&DataKey::RewardPerTokenStored(reward_token.clone())) .unwrap_or(0); - rpt += amount * PRECISION / total_staked; + rpt = rpt.checked_add( + amount.checked_mul(PRECISION).expect("rpt overflow") / total_staked + ).expect("rpt overflow"); env.storage() .instance() .set(&DataKey::RewardPerTokenStored(reward_token), &rpt); @@ -147,7 +149,7 @@ impl MultiTokenStaking { let prev: i128 = Self::_stake_of(&env, &user); env.storage() .persistent() - .set(&DataKey::Stake(user.clone()), &(prev + amount)); + .set(&DataKey::Stake(user.clone()), &prev.checked_add(amount).expect("stake overflow")); let total: i128 = env .storage() @@ -156,7 +158,7 @@ impl MultiTokenStaking { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TotalStaked, &(total + amount)); + .set(&DataKey::TotalStaked, &total.checked_add(amount).expect("total staked overflow")); env.events().publish((symbol_short!("staked"), user), amount); } @@ -172,7 +174,7 @@ impl MultiTokenStaking { env.storage() .persistent() - .set(&DataKey::Stake(user.clone()), &(prev - amount)); + .set(&DataKey::Stake(user.clone()), &prev.checked_sub(amount).expect("stake underflow")); let total: i128 = env .storage() @@ -181,7 +183,7 @@ impl MultiTokenStaking { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TotalStaked, &(total - amount)); + .set(&DataKey::TotalStaked, &total.checked_sub(amount).expect("total staked underflow")); let stake_token: Address = env.storage().instance().get(&DataKey::StakeToken).unwrap(); token::Client::new(&env, &stake_token).transfer( @@ -276,7 +278,7 @@ impl MultiTokenStaking { .get(&DataKey::Rewards(user, reward_token)) .unwrap_or(0); - accrued + stake * (rpt - user_rpt) / PRECISION + accrued + stake.checked_mul(rpt - user_rpt).expect("rewards overflow") / PRECISION } pub fn total_staked(env: Env) -> i128 { @@ -322,7 +324,7 @@ impl MultiTokenStaking { .unwrap_or(0); let stake = Self::_stake_of(env, user); - let earned = stake * (rpt - user_rpt) / PRECISION; + let earned = stake.checked_mul(rpt - user_rpt).expect("rewards overflow") / PRECISION; if earned > 0 { let prev: i128 = env @@ -332,7 +334,7 @@ impl MultiTokenStaking { .unwrap_or(0); env.storage() .persistent() - .set(&DataKey::Rewards(user.clone(), reward_token.clone()), &(prev + earned)); + .set(&DataKey::Rewards(user.clone(), reward_token.clone()), &prev.checked_add(earned).expect("rewards overflow")); } // Snapshot current global rate for this user diff --git a/contracts/yield/src/lib.rs b/contracts/yield/src/lib.rs index b3dc699..6fff43f 100644 --- a/contracts/yield/src/lib.rs +++ b/contracts/yield/src/lib.rs @@ -116,7 +116,9 @@ impl YieldDistribution { .get(&DataKey::RewardPerTokenStored) .unwrap_or(0); // Δ reward_per_token = amount * PRECISION / total_staked - rpt += amount * PRECISION / total_staked; + rpt = rpt.checked_add( + amount.checked_mul(PRECISION).expect("rpt overflow") / total_staked + ).expect("rpt overflow"); env.storage() .instance() .set(&DataKey::RewardPerTokenStored, &rpt); @@ -154,7 +156,7 @@ impl YieldDistribution { let prev: i128 = Self::_stake_of(&env, &user); env.storage() .persistent() - .set(&DataKey::Stake(user.clone()), &(prev + amount)); + .set(&DataKey::Stake(user.clone()), &prev.checked_add(amount).expect("stake overflow")); let total: i128 = env .storage() @@ -163,7 +165,7 @@ impl YieldDistribution { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TotalStaked, &(total + amount)); + .set(&DataKey::TotalStaked, &total.checked_add(amount).expect("total staked overflow")); env.events() .publish((symbol_short!("staked"), user), amount); @@ -189,7 +191,7 @@ impl YieldDistribution { env.storage() .persistent() - .set(&DataKey::Stake(user.clone()), &(prev - amount)); + .set(&DataKey::Stake(user.clone()), &prev.checked_sub(amount).expect("stake underflow")); let total: i128 = env .storage() @@ -198,7 +200,7 @@ impl YieldDistribution { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TotalStaked, &(total - amount)); + .set(&DataKey::TotalStaked, &total.checked_sub(amount).expect("total staked underflow")); let stake_token: Address = env.storage().instance().get(&DataKey::StakeToken).unwrap(); token::Client::new(&env, &stake_token).transfer( @@ -273,7 +275,7 @@ impl YieldDistribution { .get(&DataKey::Rewards(user)) .unwrap_or(0); - accrued + stake * (rpt - user_rpt) / PRECISION + accrued + stake.checked_mul(rpt - user_rpt).expect("rewards overflow") / PRECISION } pub fn total_staked(env: Env) -> i128 { @@ -306,7 +308,7 @@ impl YieldDistribution { .unwrap_or(0); let stake = Self::_stake_of(env, user); - let earned = stake * (rpt - user_rpt) / PRECISION; + let earned = stake.checked_mul(rpt - user_rpt).expect("rewards overflow") / PRECISION; if earned > 0 { let prev: i128 = env @@ -316,7 +318,7 @@ impl YieldDistribution { .unwrap_or(0); env.storage() .persistent() - .set(&DataKey::Rewards(user.clone()), &(prev + earned)); + .set(&DataKey::Rewards(user.clone()), &prev.checked_add(earned).expect("rewards overflow")); } // Snapshot current global rate for this user diff --git a/src/amm/lib.rs b/src/amm/lib.rs index 297f7be..669bfe9 100644 --- a/src/amm/lib.rs +++ b/src/amm/lib.rs @@ -71,11 +71,11 @@ impl AMM { // Calculate shares to mint let shares = if total_shares == 0 { // Initial liquidity = geometric mean - sqrt(amount_a * amount_b) + sqrt(amount_a.checked_mul(amount_b).expect("deposit overflow")) } else { // Proportional liquidity: min(amount_a/reserve_a, amount_b/reserve_b) * total_shares - let shares_a = (amount_a * total_shares) / reserve_a; - let shares_b = (amount_b * total_shares) / reserve_b; + let shares_a = amount_a.checked_mul(total_shares).expect("deposit overflow") / reserve_a; + let shares_b = amount_b.checked_mul(total_shares).expect("deposit overflow") / reserve_b; if shares_a < shares_b { shares_a } else { @@ -106,13 +106,13 @@ impl AMM { // Update state env.storage() .instance() - .set(&DataKey::ReserveA, &(reserve_a + amount_a)); + .set(&DataKey::ReserveA, &reserve_a.checked_add(amount_a).expect("reserve overflow")); env.storage() .instance() - .set(&DataKey::ReserveB, &(reserve_b + amount_b)); + .set(&DataKey::ReserveB, &reserve_b.checked_add(amount_b).expect("reserve overflow")); env.storage() .instance() - .set(&DataKey::TotalShares, &(total_shares + shares)); + .set(&DataKey::TotalShares, &total_shares.checked_add(shares).expect("shares overflow")); let old_shares: i128 = env .storage() @@ -121,7 +121,7 @@ impl AMM { .unwrap_or(0); env.storage() .persistent() - .set(&DataKey::Shares(from.clone()), &(old_shares + shares)); + .set(&DataKey::Shares(from.clone()), &old_shares.checked_add(shares).expect("shares overflow")); env.events().publish( (symbol_short!("deposit"), from), @@ -171,9 +171,9 @@ impl AMM { ); // Constant product formula with 0.3% fee: dy = (reserve_out * dx * 997) / (reserve_in * 1000 + dx * 997) - let amount_in_with_fee = amount_in * 997; - let numerator = amount_in_with_fee * reserve_out; - let denominator = (reserve_in * 1000) + amount_in_with_fee; + let amount_in_with_fee = amount_in.checked_mul(997).expect("swap overflow"); + let numerator = amount_in_with_fee.checked_mul(reserve_out).expect("swap overflow"); + let denominator = reserve_in.checked_mul(1000).expect("swap overflow").checked_add(amount_in_with_fee).expect("swap overflow"); let amount_out = numerator / denominator; if amount_out < min_amount_out { @@ -182,11 +182,11 @@ impl AMM { // Update state if token_in == token_a { - reserve_a += amount_in; - reserve_b -= amount_out; + reserve_a = reserve_a.checked_add(amount_in).expect("reserve overflow"); + reserve_b = reserve_b.checked_sub(amount_out).expect("reserve underflow"); } else { - reserve_b += amount_in; - reserve_a -= amount_out; + reserve_b = reserve_b.checked_add(amount_in).expect("reserve overflow"); + reserve_a = reserve_a.checked_sub(amount_out).expect("reserve underflow"); } env.storage().instance().set(&DataKey::ReserveA, &reserve_a); @@ -233,22 +233,22 @@ impl AMM { panic!("insufficient shares"); } - let amount_a = (shares * reserve_a) / total_shares; - let amount_b = (shares * reserve_b) / total_shares; + let amount_a = shares.checked_mul(reserve_a).expect("withdraw overflow") / total_shares; + let amount_b = shares.checked_mul(reserve_b).expect("withdraw overflow") / total_shares; // Update state env.storage() .instance() - .set(&DataKey::ReserveA, &(reserve_a - amount_a)); + .set(&DataKey::ReserveA, &reserve_a.checked_sub(amount_a).expect("reserve underflow")); env.storage() .instance() - .set(&DataKey::ReserveB, &(reserve_b - amount_b)); + .set(&DataKey::ReserveB, &reserve_b.checked_sub(amount_b).expect("reserve underflow")); env.storage() .instance() - .set(&DataKey::TotalShares, &(total_shares - shares)); + .set(&DataKey::TotalShares, &total_shares.checked_sub(shares).expect("shares underflow")); env.storage() .persistent() - .set(&DataKey::Shares(from.clone()), &(user_shares - shares)); + .set(&DataKey::Shares(from.clone()), &user_shares.checked_sub(shares).expect("shares underflow")); // Transfer tokens back to user transfer( diff --git a/src/batch/lib.rs b/src/batch/lib.rs index 5bacacc..a102a2a 100644 --- a/src/batch/lib.rs +++ b/src/batch/lib.rs @@ -61,7 +61,7 @@ impl BatchExecutor { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::Nonce(caller.clone()), &(current_nonce + 1)); + .set(&DataKey::Nonce(caller.clone()), ¤t_nonce.checked_add(1).expect("nonce overflow")); // Execute all calls atomically let mut results = Vec::new(&env); diff --git a/src/circuit_breaker/lib.rs b/src/circuit_breaker/lib.rs index ec8ed03..d2903d1 100644 --- a/src/circuit_breaker/lib.rs +++ b/src/circuit_breaker/lib.rs @@ -272,7 +272,7 @@ impl CircuitBreaker { .get(&DataKey::TimelockSeconds) .unwrap_or(DEFAULT_TIMELOCK_SECONDS); - let unlocks_at = env.ledger().timestamp() + timelock; + let unlocks_at = env.ledger().timestamp().checked_add(timelock).expect("timelock overflow"); env.storage() .instance() @@ -465,7 +465,7 @@ impl CircuitBreaker { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TripCount, &(count + 1)); + .set(&DataKey::TripCount, &count.checked_add(1).expect("trip count overflow")); env.events() .publish((symbol_short!("tripped"), tier), (caller, source)); diff --git a/src/governance/lib.rs b/src/governance/lib.rs index fc6bfe4..233a08b 100644 --- a/src/governance/lib.rs +++ b/src/governance/lib.rs @@ -168,13 +168,13 @@ impl GovernanceContract { .get(&DataKey::TotalCreditsIssued) .unwrap_or(0); - let new_credits = current_credits + credits; + let new_credits = current_credits.checked_add(credits).expect("credits overflow"); env.storage() .instance() .set(&DataKey::VotingCredits(user.clone()), &new_credits); env.storage() .instance() - .set(&DataKey::TotalCreditsIssued, &(total_issued + credits)); + .set(&DataKey::TotalCreditsIssued, &total_issued.checked_add(credits).expect("credits overflow")); env.events().publish( (symbol_short!("credits"), user), @@ -258,7 +258,7 @@ impl GovernanceContract { .instance() .get(&DataKey::ProposalCounter) .unwrap_or(0); - let new_id = counter + 1; + let new_id = counter.checked_add(1).expect("proposal counter overflow"); // Calculate quorum based on total credits issued let total_credits: i128 = env @@ -274,7 +274,7 @@ impl GovernanceContract { // Quorum is percentage of total credits that must participate // Using integer math: quorum = (total_credits * quorum_percentage) / 100 - let quorum = (total_credits * quorum_percentage) / 100; + let quorum = total_credits.checked_mul(quorum_percentage).expect("quorum overflow") / 100; let proposal = Proposal { id: new_id, @@ -286,7 +286,7 @@ impl GovernanceContract { total_quadratic_cost: 0, voter_count: 0, created_at: env.ledger().timestamp(), - deadline: env.ledger().timestamp() + voting_period, + deadline: env.ledger().timestamp().checked_add(voting_period).expect("deadline overflow"), status: ProposalStatus::OPEN, quorum, created_at_ledger: env.ledger().sequence(), @@ -392,14 +392,14 @@ impl GovernanceContract { // Update proposal vote totals if support { - proposal.votes_for += votes; + proposal.votes_for = proposal.votes_for.checked_add(votes).expect("votes overflow"); } else { - proposal.votes_against += votes; + proposal.votes_against = proposal.votes_against.checked_add(votes).expect("votes overflow"); } // Update quadratic cost tracking - proposal.total_quadratic_cost += quadratic_cost; - proposal.voter_count += 1; + proposal.total_quadratic_cost = proposal.total_quadratic_cost.checked_add(quadratic_cost).expect("cost overflow"); + proposal.voter_count = proposal.voter_count.checked_add(1).expect("voter count overflow"); // Store vote record let vote_record = VoteRecord { diff --git a/src/indexing/lib.rs b/src/indexing/lib.rs index c5d5c19..d3b17d6 100644 --- a/src/indexing/lib.rs +++ b/src/indexing/lib.rs @@ -64,7 +64,7 @@ impl IndexingContract { env.storage() .persistent() .set(&DataKey::AddrToId(user), &id); - env.storage().instance().set(&DataKey::Counter, &(id + 1)); + env.storage().instance().set(&DataKey::Counter, &(id.checked_add(1).expect("counter overflow"))); id } diff --git a/src/liquidation/lib.rs b/src/liquidation/lib.rs index b70bf1e..f310229 100644 --- a/src/liquidation/lib.rs +++ b/src/liquidation/lib.rs @@ -39,7 +39,7 @@ impl LiquidationEngine { }; env.storage().persistent().set(&DataKey::Vaults(id), &vault); - id += 1; + id = id.checked_add(1).expect("vault id overflow"); env.storage().instance().set(&DataKey::NextVaultId, &id); id - 1 } @@ -54,14 +54,14 @@ impl LiquidationEngine { // Assume oracle has `fn get_price(env: Env) -> u128` (price in e.g. 7 decimals) let collateral_price: u128 = env.invoke_contract(&oracle_id, &symbol_short!("get_price"), soroban_sdk::vec![&env]); - let collateral_value = vault.collateral_amount * collateral_price; + let collateral_value = vault.collateral_amount.checked_mul(collateral_price).expect("value overflow"); // Assume debt is represented in same base units. Health factor * 100 - let health_factor = (collateral_value * 100) / vault.debt_amount; + let health_factor = collateral_value.checked_mul(100).expect("health factor overflow") / vault.debt_amount; assert!(health_factor < 120, "vault is healthy"); // 120% min health factor // Liquidator incentive: 5% spread + 10 units fixed fee - let incentive = (vault.collateral_amount * 5) / 100 + 10; + let incentive = vault.collateral_amount.checked_mul(5).expect("incentive overflow") / 100 + 10; let liquidated_collateral = vault.collateral_amount; vault.collateral_amount = 0; diff --git a/src/oracle_consumer/lib.rs b/src/oracle_consumer/lib.rs index c31e423..0b0df37 100644 --- a/src/oracle_consumer/lib.rs +++ b/src/oracle_consumer/lib.rs @@ -74,7 +74,7 @@ impl OracleConsumer { .expect("price record not found locally. call update_price first."); let current_time = env.ledger().timestamp(); - if current_time > price_info.timestamp + max_age_seconds { + if current_time > price_info.timestamp.checked_add(max_age_seconds).expect("timestamp overflow") { panic!("price record is too stale and cannot be used."); } diff --git a/src/oracle_medianizer/lib.rs b/src/oracle_medianizer/lib.rs index af554fa..80ea41a 100644 --- a/src/oracle_medianizer/lib.rs +++ b/src/oracle_medianizer/lib.rs @@ -113,7 +113,7 @@ impl OracleMedianizer { .get(&DataKey::OracleCount) .unwrap_or(0); - env.storage().instance().set(&DataKey::OracleCount, &(count + 1)); + env.storage().instance().set(&DataKey::OracleCount, &count.checked_add(1).expect("oracle count overflow")); env.events().publish( (symbol_short!("oracle"), oracle), @@ -297,13 +297,13 @@ impl OracleMedianizer { prices = Self::sort_prices(&env, prices); // Calculate mean for outlier detection - let sum: i128 = prices.iter().fold(0i128, |acc, p| acc + p); + let sum: i128 = prices.iter().fold(0i128, |acc, p| acc.checked_add(p).expect("sum overflow")); let mean = sum / prices.len() as i128; // Calculate standard deviation let variance_sum: i128 = prices.iter().fold(0i128, |acc, p| { let diff = p - mean; - acc + (diff * diff) + acc.checked_add(diff.checked_mul(diff).expect("variance overflow")).expect("variance overflow") }); let variance = variance_sum / prices.len() as i128; @@ -312,7 +312,7 @@ impl OracleMedianizer { // Filter outliers (remove values > 2 standard deviations from mean) let mut filtered_prices: Vec = Vec::new(&env); - let threshold = 2 * std_dev; + let threshold = 2_i128.checked_mul(std_dev).expect("threshold overflow"); for price in prices.iter() { let diff = if price > mean { price - mean } else { mean - price }; @@ -336,7 +336,7 @@ impl OracleMedianizer { // Even number: average of two middle values let mid1 = filtered_prices.get_unchecked(len / 2 - 1); let mid2 = filtered_prices.get_unchecked(len / 2); - (mid1 + mid2) / 2 + (mid1.checked_add(mid2).expect("median overflow")) / 2 } else { // Odd number: middle value filtered_prices.get_unchecked(len / 2) @@ -420,7 +420,7 @@ impl OracleMedianizer { .get(&DataKey::LastUpdate(asset.clone())) .unwrap_or(0); - if current_time > last_update + heartbeat { + if current_time > last_update.checked_add(heartbeat).expect("heartbeat overflow") { return true; // Heartbeat exceeded } } @@ -435,7 +435,7 @@ impl OracleMedianizer { old_price - new_price }; - let deviation_bps = (deviation * 10000) / old_price; + let deviation_bps = deviation.checked_mul(10000).expect("deviation overflow") / old_price; if deviation_bps >= deviation_threshold_bps as i128 { return true; // Deviation threshold exceeded diff --git a/src/random/lib.rs b/src/random/lib.rs index 40a36e4..1e089a0 100644 --- a/src/random/lib.rs +++ b/src/random/lib.rs @@ -113,7 +113,7 @@ impl RandomNumberGenerator { .get(&DataKey::RoundCounter) .unwrap_or(0); - let new_round_id = counter + 1; + let new_round_id = counter.checked_add(1).expect("round id overflow"); env.storage() .instance() @@ -189,7 +189,7 @@ impl RandomNumberGenerator { env.storage() .instance() - .set(&DataKey::CommitCount(round_id), &(commit_count + 1)); + .set(&DataKey::CommitCount(round_id), &commit_count.checked_add(1).expect("commit count overflow")); env.events().publish( (symbol_short!("commit"), round_id, user), @@ -318,7 +318,7 @@ impl RandomNumberGenerator { env.storage() .instance() - .set(&DataKey::RevealCount(round_id), &(reveal_count + 1)); + .set(&DataKey::RevealCount(round_id), &reveal_count.checked_add(1).expect("reveal count overflow")); env.events().publish( (symbol_short!("reveal"), round_id, user), diff --git a/src/staking/lib.rs b/src/staking/lib.rs index 9a590ed..618a8cb 100644 --- a/src/staking/lib.rs +++ b/src/staking/lib.rs @@ -80,15 +80,19 @@ impl StakingContract { let current_time = env.ledger().timestamp(); // Update rewards - info.accumulated_rewards += Self::calc_new_rewards(env.clone(), &info, current_time); - info.amount += amount; + info.accumulated_rewards = info.accumulated_rewards + .checked_add(Self::calc_new_rewards(env.clone(), &info, current_time)) + .expect("rewards overflow"); + info.amount = info.amount.checked_add(amount).expect("stake overflow"); info.last_updated = current_time; info.lock_end = current_time - + env - .storage() - .instance() - .get::<_, u64>(&DataKey::LockPeriod) - .unwrap(); + .checked_add( + env + .storage() + .instance() + .get::<_, u64>(&DataKey::LockPeriod) + .unwrap() + ).expect("lock end overflow"); env.storage() .persistent() @@ -112,19 +116,19 @@ impl StakingContract { let current_time = env.ledger().timestamp(); let rewards = - info.accumulated_rewards + Self::calc_new_rewards(env.clone(), &info, current_time); + info.accumulated_rewards.checked_add(Self::calc_new_rewards(env.clone(), &info, current_time)).expect("rewards overflow"); let mut amount_to_return = info.amount; // Apply penalty if before lock_end if current_time < info.lock_end { let penalty_bps: i128 = env.storage().instance().get(&DataKey::PenaltyBps).unwrap(); - let penalty = (amount_to_return * penalty_bps) / 10000; - amount_to_return -= penalty; + let penalty = amount_to_return.checked_mul(penalty_bps).expect("penalty overflow") / 10000; + amount_to_return = amount_to_return.checked_sub(penalty).expect("penalty underflow"); // Penalties stay in contract as "unclaimed rewards" or similar // Or just lost. } - let total_to_send = amount_to_return + rewards; + let total_to_send = amount_to_return.checked_add(rewards).expect("total overflow"); // Reset stake info env.storage() @@ -154,7 +158,7 @@ impl StakingContract { let mut info = Self::get_stake_info(env.clone(), user.clone()); let current_time = env.ledger().timestamp(); let rewards = - info.accumulated_rewards + Self::calc_new_rewards(env.clone(), &info, current_time); + info.accumulated_rewards.checked_add(Self::calc_new_rewards(env.clone(), &info, current_time)).expect("rewards overflow"); assert!(rewards > 0, "no rewards to claim"); info.accumulated_rewards = 0; @@ -189,6 +193,6 @@ impl StakingContract { } let rate: i128 = env.storage().instance().get(&DataKey::RewardRate).unwrap(); let seconds = (current_time - info.last_updated) as i128; - (info.amount * rate * seconds) / REWARD_PRECISION + info.amount.checked_mul(rate).expect("reward overflow").checked_mul(seconds).expect("reward overflow") / REWARD_PRECISION } } diff --git a/src/token/lib.rs b/src/token/lib.rs index e0fbb27..cd0c9a7 100644 --- a/src/token/lib.rs +++ b/src/token/lib.rs @@ -46,7 +46,7 @@ impl TokenContract { let bal = Self::balance_of(env.clone(), to.clone(), token_id); env.storage() .persistent() - .set(&DataKey::Balance(token_id, to.clone()), &(bal + amount)); + .set(&DataKey::Balance(token_id, to.clone()), &bal.checked_add(amount).expect("balance overflow")); let supply: i128 = env .storage() @@ -55,7 +55,7 @@ impl TokenContract { .unwrap_or(0); env.storage() .instance() - .set(&DataKey::TotalSupply(token_id), &(supply + amount)); + .set(&DataKey::TotalSupply(token_id), &supply.checked_add(amount).expect("supply overflow")); env.events() .publish((symbol_short!("mint"), to, token_id), amount); @@ -252,7 +252,7 @@ impl TokenContract { env.storage().persistent().set( &DataKey::Balance(token_id, from.clone()), - &(from_bal - amount), + &from_bal.checked_sub(amount).expect("balance underflow"), ); let to_bal = env .storage() @@ -264,7 +264,7 @@ impl TokenContract { env.storage() .persistent() - .set(&DataKey::Balance(token_id, to.clone()), &(to_bal + amount)); + .set(&DataKey::Balance(token_id, to.clone()), &to_bal.checked_add(amount).expect("balance overflow")); env.events() .publish((symbol_short!("transfer"), from, to, token_id), amount); diff --git a/src/upgradeable/lib.rs b/src/upgradeable/lib.rs index f3e519b..b148d70 100644 --- a/src/upgradeable/lib.rs +++ b/src/upgradeable/lib.rs @@ -71,7 +71,7 @@ impl UpgradeableContract { let current_version: u32 = env.storage().instance().get(&DataKey::Version).unwrap_or(1); env.storage() .instance() - .set(&DataKey::Version, &(current_version + 1)); + .set(&DataKey::Version, ¤t_version.checked_add(1).expect("version overflow")); // Perform the upgrade — this replaces the running WASM. env.deployer().update_current_contract_wasm(new_wasm_hash); diff --git a/src/utils/fees.rs b/src/utils/fees.rs index ea56530..d106366 100644 --- a/src/utils/fees.rs +++ b/src/utils/fees.rs @@ -24,14 +24,14 @@ pub fn calculate_dynamic_fee(amount: i128, volume: i128, tiers: Vec) -> } } - (amount * selected_bps) / 10000 + amount.checked_mul(selected_bps).expect("fee overflow") / 10000 } /** * Basic fee calculation using basis points. */ pub fn calculate_simple_fee(amount: i128, fee_bps: i128) -> i128 { - (amount * fee_bps) / 10000 + amount.checked_mul(fee_bps).expect("fee overflow") / 10000 } #[cfg(test)] diff --git a/src/vesting/lib.rs b/src/vesting/lib.rs index 00f3887..623e1a2 100644 --- a/src/vesting/lib.rs +++ b/src/vesting/lib.rs @@ -81,7 +81,7 @@ impl VestingContract { env.storage().persistent().set(&DataKey::Grant(id), &grant); env.storage() .instance() - .set(&DataKey::GrantCounter, &(id + 1)); + .set(&DataKey::GrantCounter, &id.checked_add(1).expect("grant counter overflow")); env.events() .publish((symbol_short!("grant_new"), id), amount); @@ -104,7 +104,7 @@ impl VestingContract { assert!(claimable > 0, "nothing to claim"); - grant.claimed_amount += claimable; + grant.claimed_amount = grant.claimed_amount.checked_add(claimable).expect("claimed overflow"); env.storage() .persistent() .set(&DataKey::Grant(grant_id), &grant); @@ -146,12 +146,12 @@ impl VestingContract { fn calculate_vested_amount(grant: &Grant, current_time: u64) -> i128 { // Before cliff - if current_time < grant.start_time + grant.cliff_duration { + if current_time < grant.start_time.checked_add(grant.cliff_duration).expect("time overflow") { return 0; } // After full duration - if current_time >= grant.start_time + grant.vesting_duration { + if current_time >= grant.start_time.checked_add(grant.vesting_duration).expect("time overflow") { return grant.total_amount; } @@ -159,7 +159,7 @@ impl VestingContract { let elapsed = (current_time - grant.start_time) as i128; let duration = grant.vesting_duration as i128; - (grant.total_amount * elapsed) / duration + grant.total_amount.checked_mul(elapsed).expect("vesting overflow") / duration } } diff --git a/src/yield_farming/src/lib.rs b/src/yield_farming/src/lib.rs index ef8cb00..f66149a 100644 --- a/src/yield_farming/src/lib.rs +++ b/src/yield_farming/src/lib.rs @@ -69,13 +69,20 @@ impl YieldFarmingDistributor { if current_ledger > last_update && total_shares > 0 { let ledgers_elapsed = (current_ledger - last_update) as i128; - reward_per_share += (ledgers_elapsed * reward_rate * 1_000_000_000_000_000_000) / total_shares; + reward_per_share = reward_per_share.checked_add( + ledgers_elapsed + .checked_mul(reward_rate).expect("reward overflow") + .checked_mul(1_000_000_000_000_000_000).expect("reward overflow") + / total_shares + ).expect("reward overflow"); } let user_paid: i128 = env.storage().persistent().get(&DataKey::UserRewardPerSharePaid(user.clone())).unwrap_or(0); let rewards_accrued: i128 = env.storage().persistent().get(&DataKey::Rewards(user.clone())).unwrap_or(0); - rewards_accrued + (user_shares * (reward_per_share - user_paid)) / 1_000_000_000_000_000_000 + rewards_accrued.checked_add( + user_shares.checked_mul(reward_per_share - user_paid).expect("reward overflow") / 1_000_000_000_000_000_000 + ).expect("reward overflow") } fn _update_global_reward(env: &Env) { @@ -89,7 +96,12 @@ impl YieldFarmingDistributor { let reward_rate: i128 = env.storage().instance().get(&DataKey::RewardRate).unwrap(); let ledgers_elapsed = (current_ledger - last_update) as i128; let mut stored: i128 = env.storage().instance().get(&DataKey::RewardPerShareStored).unwrap_or(0); - stored += (ledgers_elapsed * reward_rate * 1_000_000_000_000_000_000) / total_shares; + stored = stored.checked_add( + ledgers_elapsed + .checked_mul(reward_rate).expect("reward overflow") + .checked_mul(1_000_000_000_000_000_000).expect("reward overflow") + / total_shares + ).expect("reward overflow"); env.storage().instance().set(&DataKey::RewardPerShareStored, &stored); } env.storage().instance().set(&DataKey::LastUpdateLedger, ¤t_ledger); @@ -105,10 +117,10 @@ impl YieldFarmingDistributor { let amm_pool: Address = env.storage().instance().get(&DataKey::AmmPool).unwrap(); let user_shares: i128 = env.invoke_contract(&amm_pool, &soroban_sdk::Symbol::new(&env, "get_shares"), soroban_sdk::vec![env, user.to_val()]); - let earned = (user_shares * (stored - user_paid)) / 1_000_000_000_000_000_000; + let earned = user_shares.checked_mul(stored - user_paid).expect("reward overflow") / 1_000_000_000_000_000_000; if earned > 0 { let prev: i128 = env.storage().persistent().get(&DataKey::Rewards(user.clone())).unwrap_or(0); - env.storage().persistent().set(&DataKey::Rewards(user.clone()), &(prev + earned)); + env.storage().persistent().set(&DataKey::Rewards(user.clone()), &prev.checked_add(earned).expect("reward overflow")); } env.storage().persistent().set(&DataKey::UserRewardPerSharePaid(user.clone()), &stored); }