From c3e93f40fe367a4f8f8b6b9b7bae25ebe019d32a Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Wed, 20 Aug 2025 13:33:55 -0400 Subject: [PATCH 1/2] prevent overflow --- cadence/contracts/TidalProtocol.cdc | 60 ++++++++++++++++++++++++----- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/cadence/contracts/TidalProtocol.cdc b/cadence/contracts/TidalProtocol.cdc index b1c3e5d0..a68f09e2 100644 --- a/cadence/contracts/TidalProtocol.cdc +++ b/cadence/contracts/TidalProtocol.cdc @@ -306,18 +306,60 @@ access(all) contract TidalProtocol { // Enhanced updateInterestIndices with deposit capacity update access(all) fun updateInterestIndices() { - let currentTime = getCurrentBlock().timestamp - let timeDelta: UFix64 = currentTime - self.lastUpdate - self.creditInterestIndex = TidalProtocol.compoundInterestIndex(oldIndex: self.creditInterestIndex, perSecondRate: self.currentCreditRate, elapsedSeconds: timeDelta) - self.debitInterestIndex = TidalProtocol.compoundInterestIndex(oldIndex: self.debitInterestIndex, perSecondRate: self.currentDebitRate, elapsedSeconds: timeDelta) + let currentTime: UFix64 = getCurrentBlock().timestamp + let dt: UFix64 = currentTime - self.lastUpdate + + // No time elapsed or already at cap → nothing to do + if dt <= 0.0 { + return + } + + // Update interest indices (dt > 0 ensures sensible compounding) + self.creditInterestIndex = TidalProtocol.compoundInterestIndex( + oldIndex: self.creditInterestIndex, + perSecondRate: self.currentCreditRate, + elapsedSeconds: dt + ) + self.debitInterestIndex = TidalProtocol.compoundInterestIndex( + oldIndex: self.debitInterestIndex, + perSecondRate: self.currentDebitRate, + elapsedSeconds: dt + ) + + // Record the moment we accounted for self.lastUpdate = currentTime - // Update deposit capacity based on time - let newDepositCapacity = self.depositCapacity + (self.depositRate * timeDelta) - if newDepositCapacity >= self.depositCapacityCap { + // ---- Deposit capacity with overflow-proof, saturating growth ---- + // Early exits keep gas down and avoid unnecessary math + if self.depositCapacity >= self.depositCapacityCap { + self.depositCapacity = self.depositCapacityCap + return + } + if self.depositRate <= 0.0 { + return + } + + // Remaining headroom before hitting the cap + let remaining: UFix64 = self.depositCapacityCap - self.depositCapacity + if remaining <= 0.0 { + self.depositCapacity = self.depositCapacityCap + return + } + + // Bound the multiplier BEFORE multiplying to prevent overflow: + // choose the largest rate that still ensures rate*dt <= remaining + let maxRateForStep: UFix64 = remaining / dt + let effectiveRate: UFix64 = self.depositRate < maxRateForStep + ? self.depositRate + : maxRateForStep + + // Safe: growth <= remaining, so the addition cannot overflow + let growth: UFix64 = effectiveRate * dt + self.depositCapacity = self.depositCapacity + growth + + // Defensive clamp (handles any rounding edge cases) + if self.depositCapacity > self.depositCapacityCap { self.depositCapacity = self.depositCapacityCap - } else { - self.depositCapacity = newDepositCapacity } } From aedaa869d4c1a2a5da6f77a2d189ef747b5117f2 Mon Sep 17 00:00:00 2001 From: Alex Ni <12097569+nialexsan@users.noreply.github.com> Date: Tue, 9 Sep 2025 11:10:30 -0400 Subject: [PATCH 2/2] address comments --- cadence/contracts/TidalProtocol.cdc | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cadence/contracts/TidalProtocol.cdc b/cadence/contracts/TidalProtocol.cdc index a68f09e2..81da3401 100644 --- a/cadence/contracts/TidalProtocol.cdc +++ b/cadence/contracts/TidalProtocol.cdc @@ -341,10 +341,6 @@ access(all) contract TidalProtocol { // Remaining headroom before hitting the cap let remaining: UFix64 = self.depositCapacityCap - self.depositCapacity - if remaining <= 0.0 { - self.depositCapacity = self.depositCapacityCap - return - } // Bound the multiplier BEFORE multiplying to prevent overflow: // choose the largest rate that still ensures rate*dt <= remaining @@ -355,10 +351,11 @@ access(all) contract TidalProtocol { // Safe: growth <= remaining, so the addition cannot overflow let growth: UFix64 = effectiveRate * dt - self.depositCapacity = self.depositCapacity + growth // Defensive clamp (handles any rounding edge cases) - if self.depositCapacity > self.depositCapacityCap { + if self.depositCapacity < (self.depositCapacityCap - growth) { + self.depositCapacity = self.depositCapacity + growth + } else { self.depositCapacity = self.depositCapacityCap } } @@ -970,7 +967,6 @@ access(all) contract TidalProtocol { let uintDepositCollateralFactor = DeFiActionsMathUtils.toUInt128(self.collateralFactor[depositType]!) var requiredEffectiveCollateral = DeFiActionsMathUtils.mul(uintHealthChange, effectiveDebtAfterWithdrawal) requiredEffectiveCollateral = DeFiActionsMathUtils.div(requiredEffectiveCollateral, uintDepositCollateralFactor) - requiredEffectiveCollateral = DeFiActionsMathUtils.div(requiredEffectiveCollateral, uintWithdrawBorrowFactor) // The amount of the token to deposit, in units of the token. let collateralTokenCount = DeFiActionsMathUtils.div(requiredEffectiveCollateral, uintDepositPrice)