diff --git a/app/ante.go b/app/ante.go index 62d03fd7b..57d33d8d7 100644 --- a/app/ante.go +++ b/app/ante.go @@ -78,6 +78,11 @@ func (min MinCommissionDecorator) CalculateValidatorProjectedVotingPower(ctx sdk projectedTotalDelegatedTokens := totalDelegatedTokens.Add(delegateAmount) projectedValidatorTokens := delegateAmount + // Ensure projectedTotalDelegatedTokens is not zero to avoid division by zero + if projectedTotalDelegatedTokens.IsZero() { + return sdk.ZeroDec() + } + return projectedValidatorTokens.Quo(projectedTotalDelegatedTokens).Mul(sdk.NewDec(100)) } @@ -89,6 +94,11 @@ func (min MinCommissionDecorator) CalculateDelegateProjectedVotingPower(ctx sdk. projectedTotalDelegatedTokens := totalDelegatedTokens.Add(delegateAmount) projectedValidatorTokens := validatorTokens.Add(delegateAmount) + // Ensure projectedTotalDelegatedTokens is not zero to avoid division by zero + if projectedTotalDelegatedTokens.IsZero() { + return sdk.ZeroDec() + } + return projectedValidatorTokens.Quo(projectedTotalDelegatedTokens).Mul(sdk.NewDec(100)) } @@ -99,6 +109,11 @@ func (min MinCommissionDecorator) CalculateRedelegateProjectedVotingPower(ctx sd projectedValidatorTokens := validatorTokens.Add(delegateAmount) + // Ensure projectedTotalDelegatedTokens is not zero to avoid division by zero + if projectedTotalDelegatedTokens.IsZero() { + return sdk.ZeroDec() + } + return projectedValidatorTokens.Quo(projectedTotalDelegatedTokens).Mul(sdk.NewDec(100)) } diff --git a/x/amm/keeper/calc_in_route_spot_price.go b/x/amm/keeper/calc_in_route_spot_price.go index b10f95153..1ab6a3e03 100644 --- a/x/amm/keeper/calc_in_route_spot_price.go +++ b/x/amm/keeper/calc_in_route_spot_price.go @@ -67,6 +67,11 @@ func (k Keeper) CalcInRouteSpotPrice(ctx sdk.Context, tokenIn sdk.Coin, routes [ availableLiquidity = poolAsset.Token } + // Ensure tokenIn.Amount is not zero to avoid division by zero + if tokenIn.IsZero() { + return sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, types.ErrAmountTooLow + } + // Calculate the spot price given the initial token in and the final token out spotPrice := sdk.NewDecFromInt(tokensIn[0].Amount).Quo(sdk.NewDecFromInt(tokenIn.Amount)) diff --git a/x/amm/keeper/calc_out_route_spot_price.go b/x/amm/keeper/calc_out_route_spot_price.go index fa9766561..757688f84 100644 --- a/x/amm/keeper/calc_out_route_spot_price.go +++ b/x/amm/keeper/calc_out_route_spot_price.go @@ -67,6 +67,11 @@ func (k Keeper) CalcOutRouteSpotPrice(ctx sdk.Context, tokenOut sdk.Coin, routes availableLiquidity = poolAsset.Token } + // Ensure tokenIn.Amount is not zero to avoid division by zero + if tokenOut.IsZero() { + return sdk.ZeroDec(), sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), sdk.Coin{}, types.ErrAmountTooLow + } + // Calculate the spot price given the initial token in and the final token in spotPrice := sdk.NewDecFromInt(tokensOut[0].Amount).Quo(sdk.NewDecFromInt(tokenOut.Amount)) diff --git a/x/amm/keeper/msg_server_feed_multiple_external_liquidity.go b/x/amm/keeper/msg_server_feed_multiple_external_liquidity.go index 26c3d9a7f..84730adaf 100644 --- a/x/amm/keeper/msg_server_feed_multiple_external_liquidity.go +++ b/x/amm/keeper/msg_server_feed_multiple_external_liquidity.go @@ -68,8 +68,22 @@ func (k msgServer) FeedMultipleExternalLiquidity(goCtx context.Context, msg *typ return nil, err } + // Ensure tvl is not zero to avoid division by zero + if tvl.IsZero() { + return nil, types.ErrAmountTooLow + } + elRatio := elValue.Quo(tvl) - elRatio = elRatio.Quo(LiquidityRatioFromPriceDepth(elDepth)) + + // calculate liquidity ratio + liquidityRatio := LiquidityRatioFromPriceDepth(elDepth) + + // Ensure tvl is not zero to avoid division by zero + if liquidityRatio.IsZero() { + return nil, types.ErrAmountTooLow + } + + elRatio = elRatio.Quo(liquidityRatio) if elRatio.LT(sdk.OneDec()) { elRatio = sdk.OneDec() } diff --git a/x/amm/types/calc_exit_pool.go b/x/amm/types/calc_exit_pool.go index c980904b6..c1bfeda15 100644 --- a/x/amm/types/calc_exit_pool.go +++ b/x/amm/types/calc_exit_pool.go @@ -17,6 +17,12 @@ func CalcExitValueWithoutSlippage(ctx sdk.Context, oracleKeeper OracleKeeper, ac totalShares := pool.GetTotalShares() var refundedShares sdk.Dec refundedShares = sdk.NewDecFromInt(exitingShares) + + // Ensure totalShares is not zero to avoid division by zero + if totalShares.IsZero() { + return sdk.ZeroDec(), ErrAmountTooLow + } + exitValue := tvl.Mul(refundedShares).Quo(sdk.NewDecFromInt(totalShares.Amount)) if exitingShares.GTE(totalShares.Amount) { @@ -97,6 +103,11 @@ func CalcExitPool(ctx sdk.Context, oracleKeeper OracleKeeper, pool Pool, account return sdk.Coins{}, err } + // Ensure tokenPrice is not zero to avoid division by zero + if tokenPrice.IsZero() { + return sdk.Coins{}, ErrAmountTooLow + } + oracleOutAmount := exitValueWithoutSlippage.Quo(tokenPrice) newAssetPools, err := pool.NewPoolAssetsAfterSwap( diff --git a/x/amm/types/calc_in_amt_given_out.go b/x/amm/types/calc_in_amt_given_out.go index 3e902d12e..d891c5178 100644 --- a/x/amm/types/calc_in_amt_given_out.go +++ b/x/amm/types/calc_in_amt_given_out.go @@ -48,12 +48,21 @@ func (p Pool) CalcInAmtGivenOut( poolPostSwapOutBalance := poolTokenOutBalance.Sub(sdk.NewDecFromInt(tokenOut.Amount)) // (x_0)(y_0) = (x_0 + in)(y_0 - out) - tokenAmountIn := solveConstantFunctionInvariant( + tokenAmountIn, err := solveConstantFunctionInvariant( poolTokenOutBalance, poolPostSwapOutBalance, outWeight, poolTokenInBalance, inWeight, - ).Neg() + ) + if err != nil { + return sdk.Coin{}, err + } + tokenAmountIn = tokenAmountIn.Neg() + + // Ensure (1 - swapfee) is not zero to avoid division by zero + if sdk.OneDec().Sub(swapFee).IsZero() { + return sdk.Coin{}, ErrAmountTooLow + } // We deduct a swap fee on the input asset. The swap happens by following the invariant curve on the input * (1 - swap fee) // and then the swap fee is added to the pool. diff --git a/x/amm/types/calc_out_amt_given_in.go b/x/amm/types/calc_out_amt_given_in.go index e7ae4a9ba..0423b7d29 100644 --- a/x/amm/types/calc_out_amt_given_in.go +++ b/x/amm/types/calc_out_amt_given_in.go @@ -52,13 +52,16 @@ func (p Pool) CalcOutAmtGivenIn( // deduct swapfee on the tokensIn // delta balanceOut is positive(tokens inside the pool decreases) - tokenAmountOut := solveConstantFunctionInvariant( + tokenAmountOut, err := solveConstantFunctionInvariant( poolTokenInBalance, poolPostSwapInBalance, inWeight, poolTokenOutBalance, outWeight, ) + if err != nil { + return sdk.Coin{}, err + } // We ignore the decimal component, as we round down the token amount out. tokenAmountOutInt := tokenAmountOut.TruncateInt() diff --git a/x/amm/types/pool_calc_join_pool_shares.go b/x/amm/types/pool_calc_join_pool_shares.go index 4534a07e5..ae3136c98 100644 --- a/x/amm/types/pool_calc_join_pool_shares.go +++ b/x/amm/types/pool_calc_join_pool_shares.go @@ -13,13 +13,17 @@ func (p *Pool) calcSingleAssetJoin(tokenIn sdk.Coin, spreadFactor sdk.Dec, token return sdk.ZeroInt(), errors.New("pool misconfigured, total weight = 0") } normalizedWeight := sdk.NewDecFromInt(tokenInPoolAsset.Weight).Quo(sdk.NewDecFromInt(totalWeight)) - return calcPoolSharesOutGivenSingleAssetIn( + poolShares, err := calcPoolSharesOutGivenSingleAssetIn( sdk.NewDecFromInt(tokenInPoolAsset.Token.Amount), normalizedWeight, sdk.NewDecFromInt(totalShares), sdk.NewDecFromInt(tokenIn.Amount), spreadFactor, - ).TruncateInt(), nil + ) + if err != nil { + return sdk.ZeroInt(), err + } + return poolShares.TruncateInt(), nil } // CalcJoinPoolShares calculates the number of shares created to join pool with the provided amount of `tokenIn`. diff --git a/x/amm/types/pool_join_pool_no_swap.go b/x/amm/types/pool_join_pool_no_swap.go index 73e44ebcd..331349aab 100644 --- a/x/amm/types/pool_join_pool_no_swap.go +++ b/x/amm/types/pool_join_pool_no_swap.go @@ -148,6 +148,10 @@ func (p *Pool) JoinPool(ctx sdk.Context, oracleKeeper OracleKeeper, accountedPoo if err != nil { return sdk.ZeroInt(), err } + // Ensure tvl is not zero to avoid division by zero + if tvl.IsZero() { + return sdk.ZeroInt(), ErrAmountTooLow + } newAssetPools, err := p.NewPoolAssetsAfterSwap( tokensIn, diff --git a/x/amm/types/solve_constant_function_invariant.go b/x/amm/types/solve_constant_function_invariant.go index 67e46070d..6509d35a4 100644 --- a/x/amm/types/solve_constant_function_invariant.go +++ b/x/amm/types/solve_constant_function_invariant.go @@ -18,10 +18,20 @@ func solveConstantFunctionInvariant( tokenWeightFixed, tokenBalanceUnknownBefore, tokenWeightUnknown sdk.Dec, -) sdk.Dec { +) (sdk.Dec, error) { + // Ensure tokenWeightUnknown is not zero to avoid division by zero + if tokenWeightUnknown.IsZero() { + return sdk.ZeroDec(), ErrAmountTooLow + } + // weightRatio = (weightX/weightY) weightRatio := tokenWeightFixed.Quo(tokenWeightUnknown) + // Ensure tokenBalanceFixedAfter is not zero to avoid division by zero + if tokenBalanceFixedAfter.IsZero() { + return sdk.ZeroDec(), ErrAmountTooLow + } + // y = balanceXBefore/balanceXAfter y := tokenBalanceFixedBefore.Quo(tokenBalanceFixedAfter) @@ -29,7 +39,7 @@ func solveConstantFunctionInvariant( yToWeightRatio := Pow(y, weightRatio) paranthetical := sdk.OneDec().Sub(yToWeightRatio) amountY := tokenBalanceUnknownBefore.Mul(paranthetical) - return amountY + return amountY, nil } // feeRatio returns the fee ratio that is defined as follows: @@ -46,7 +56,7 @@ func calcPoolSharesOutGivenSingleAssetIn( poolShares, tokenAmountIn, spreadFactor sdk.Dec, -) sdk.Dec { +) (sdk.Dec, error) { // deduct spread factor on the in asset. // We don't charge spread factor on the token amount that we imagine as unswapped (the normalized weight). // So effective_swapfee = spread factor * (1 - normalized_token_weight) @@ -61,11 +71,15 @@ func calcPoolSharesOutGivenSingleAssetIn( // The number of new shares we need to make is then `old_shares * ((k'/k) - 1)` // Whats very cool, is that this turns out to be the exact same `solveConstantFunctionInvariant` code // with the answer's sign reversed. - poolAmountOut := solveConstantFunctionInvariant( + poolAmountOut, err := solveConstantFunctionInvariant( tokenBalanceIn.Add(tokenAmountInAfterFee), tokenBalanceIn, normalizedTokenWeightIn, poolShares, - sdk.OneDec()).Neg() - return poolAmountOut + sdk.OneDec()) + if err != nil { + return sdk.Dec{}, err + } + poolAmountOut = poolAmountOut.Neg() + return poolAmountOut, nil } diff --git a/x/amm/types/swap_in_amt_given_out.go b/x/amm/types/swap_in_amt_given_out.go index 154789535..82b14213b 100644 --- a/x/amm/types/swap_in_amt_given_out.go +++ b/x/amm/types/swap_in_amt_given_out.go @@ -87,6 +87,11 @@ func (p *Pool) SwapInAmtGivenOut( // actualSlippageAmount = balancer slippage(resizedAmount) oracleInAmount := sdk.NewDecFromInt(tokenOut.Amount).Mul(outTokenPrice).Quo(inTokenPrice) + // Ensure p.PoolParams.ExternalLiquidityRatio is not zero to avoid division by zero + if p.PoolParams.ExternalLiquidityRatio.IsZero() { + return sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), ErrAmountTooLow + } + resizedAmount := sdk.NewDecFromInt(tokenOut.Amount).Quo(p.PoolParams.ExternalLiquidityRatio).RoundInt() slippageAmount, err = p.CalcGivenOutSlippage( ctx, diff --git a/x/amm/types/swap_out_amt_given_in.go b/x/amm/types/swap_out_amt_given_in.go index 74468359c..f2b8fa0a2 100644 --- a/x/amm/types/swap_out_amt_given_in.go +++ b/x/amm/types/swap_out_amt_given_in.go @@ -74,6 +74,10 @@ func (p Pool) StackedRatioFromSnapshot(ctx sdk.Context, oracleKeeper OracleKeepe stackedRatio := sdk.ZeroDec() for index, asset := range snapshot.PoolAssets { assetDiff := sdk.NewDecFromInt(p.PoolAssets[index].Token.Amount.Sub(asset.Token.Amount).Abs()) + // Ensure asset.Token is not zero to avoid division by zero + if asset.Token.IsZero() { + asset.Token.Amount = sdk.OneInt() + } assetStacked := assetDiff.Quo(sdk.NewDecFromInt(asset.Token.Amount)) stackedRatio = stackedRatio.Add(assetStacked) } @@ -93,6 +97,10 @@ func (p Pool) WeightDistanceFromTarget(ctx sdk.Context, oracleKeeper OracleKeepe distance := targetWeights[i].Weight.Sub(oracleWeights[i].Weight).Abs() distanceSum = distanceSum.Add(distance) } + // Ensure len(p.PoolAssets) is not zero to avoid division by zero + if len(p.PoolAssets) == 0 { + return sdk.ZeroDec() + } return distanceSum.Quo(sdk.NewDec(int64(len(p.PoolAssets)))) } @@ -179,6 +187,12 @@ func (p *Pool) SwapOutAmtGivenIn( // resizedAmount = tokenIn / externalLiquidityRatio // actualSlippageAmount = balancer slippage(resizedAmount) oracleOutAmount := sdk.NewDecFromInt(tokenIn.Amount).Mul(inTokenPrice).Quo(outTokenPrice) + + // Ensure p.PoolParams.ExternalLiquidityRatio is not zero to avoid division by zero + if p.PoolParams.ExternalLiquidityRatio.IsZero() { + return sdk.Coin{}, sdk.ZeroDec(), sdk.ZeroDec(), ErrAmountTooLow + } + resizedAmount := sdk.NewDecFromInt(tokenIn.Amount).Quo(p.PoolParams.ExternalLiquidityRatio).RoundInt() slippageAmount, err = p.CalcGivenInSlippage( ctx, diff --git a/x/commitment/keeper/msg_server_vest_now.go b/x/commitment/keeper/msg_server_vest_now.go index ae1cc0803..5f6c4d15b 100644 --- a/x/commitment/keeper/msg_server_vest_now.go +++ b/x/commitment/keeper/msg_server_vest_now.go @@ -31,6 +31,11 @@ func (k msgServer) VestNow(goCtx context.Context, msg *types.MsgVestNow) (*types return nil, err } + // Ensure vestingInfo.VestNowFactor is not zero to avoid division by zero + if vestingInfo.VestNowFactor.IsZero() { + return nil, types.ErrInvalidAmount + } + vestAmount := msg.Amount.Quo(vestingInfo.VestNowFactor) withdrawCoins := sdk.NewCoins(sdk.NewCoin(vestingInfo.VestingDenom, vestAmount)) diff --git a/x/commitment/keeper/vest.go b/x/commitment/keeper/vest.go index 88e9b5a71..f31f34708 100644 --- a/x/commitment/keeper/vest.go +++ b/x/commitment/keeper/vest.go @@ -17,7 +17,7 @@ func (k Keeper) VestTokens(ctx sdk.Context, epochIdentifier string) error { for index := len(commitments.VestingTokens) - 1; index >= 0; index-- { vesting := commitments.VestingTokens[index] vesting.CurrentEpoch = vesting.CurrentEpoch + 1 - if vesting.CurrentEpoch > vesting.NumEpochs || vesting.UnvestedAmount.IsZero() { + if vesting.NumEpochs == 0 || vesting.CurrentEpoch > vesting.NumEpochs || vesting.UnvestedAmount.IsZero() { continue } diff --git a/x/incentive/keeper/abci.go b/x/incentive/keeper/abci.go index 4c00810c0..4641da57d 100644 --- a/x/incentive/keeper/abci.go +++ b/x/incentive/keeper/abci.go @@ -107,15 +107,26 @@ func (k Keeper) ProcessUpdateIncentiveParams(ctx sdk.Context) bool { params := k.GetParams(ctx) + // Ensure distribution epoch is not zero to avoid division by zero + if params.DistributionEpochForLpsInBlocks == 0 || params.DistributionEpochForStakersInBlocks == 0 { + return false + } + for _, inflation := range listTimeBasedInflations { if inflation.StartBlockHeight > uint64(ctx.BlockHeight()) || inflation.EndBlockHeight < uint64(ctx.BlockHeight()) { continue } totalBlocksPerYear := sdk.NewInt(int64(inflation.EndBlockHeight - inflation.StartBlockHeight + 1)) + + // ptypes.DaysPerYear is guaranteed to be positive as it is defined as a constant allocationEpochInblocks := totalBlocksPerYear.Quo(sdk.NewInt(ptypes.DaysPerYear)) if len(params.LpIncentives) == 0 { totalDistributionEpochPerYear := totalBlocksPerYear.Quo(sdk.NewInt(params.DistributionEpochForLpsInBlocks)) + // If totalDistributionEpochPerYear is zero, we skip this inflation to avoid division by zero + if totalBlocksPerYear == sdk.ZeroInt() { + continue + } currentEpochInBlocks := sdk.NewInt(ctx.BlockHeight() - int64(inflation.StartBlockHeight)).Mul(totalDistributionEpochPerYear).Quo(totalBlocksPerYear) maxEdenPerAllocation := sdk.NewInt(int64(inflation.Inflation.LmRewards)).Mul(allocationEpochInblocks).Quo(totalBlocksPerYear) params.LpIncentives = append(params.LpIncentives, types.IncentiveInfo{ @@ -140,6 +151,10 @@ func (k Keeper) ProcessUpdateIncentiveParams(ctx sdk.Context) bool { if len(params.StakeIncentives) == 0 { totalDistributionEpochPerYear := totalBlocksPerYear.Quo(sdk.NewInt(params.DistributionEpochForStakersInBlocks)) + // If totalDistributionEpochPerYear is zero, we skip this inflation to avoid division by zero + if totalBlocksPerYear == sdk.ZeroInt() { + continue + } currentEpochInBlocks := sdk.NewInt(ctx.BlockHeight() - int64(inflation.StartBlockHeight)).Mul(totalDistributionEpochPerYear).Quo(totalBlocksPerYear) maxEdenPerAllocation := sdk.NewInt(int64(inflation.Inflation.IcsStakingRewards)).Mul(allocationEpochInblocks).Quo(totalBlocksPerYear) params.StakeIncentives = append(params.StakeIncentives, types.IncentiveInfo{ diff --git a/x/incentive/keeper/apr.go b/x/incentive/keeper/apr.go index 59a47415b..8f794e953 100644 --- a/x/incentive/keeper/apr.go +++ b/x/incentive/keeper/apr.go @@ -74,6 +74,11 @@ func (k Keeper) CalculateApr(ctx sdk.Context, query *types.QueryAprRequest) (sdk k.UpdateTotalCommitmentInfo(ctx, baseCurrency) totalStakedSnapshot := k.tci.TotalElysBonded.Add(k.tci.TotalEdenEdenBoostCommitted) + // Ensure totalStakedSnapshot is not zero to avoid division by zero + if totalStakedSnapshot.IsZero() { + return sdk.ZeroInt(), nil + } + // Calculate edenAmountPerEpochStakersPerDay := stkIncentive.EdenAmountPerYear.Mul(stkIncentive.AllocationEpochInBlocks).Quo(stkIncentive.TotalBlocksPerYear) @@ -119,6 +124,11 @@ func (k Keeper) CalculateApr(ctx sdk.Context, query *types.QueryAprRequest) (sdk k.UpdateTotalCommitmentInfo(ctx, baseCurrency) totalStakedSnapshot := k.tci.TotalElysBonded.Add(k.tci.TotalEdenEdenBoostCommitted) + // Ensure totalStakedSnapshot is not zero to avoid division by zero + if totalStakedSnapshot.IsZero() { + return sdk.ZeroInt(), nil + } + // Usdc apr for elys staking = (24 hour dex rewards in USDC generated for stakers) * 365*100/ {price ( elys/usdc)*( sum of (elys staked, Eden committed, Eden boost committed))} // we multiply 10 as we have use 10elys as input in the price estimation apr := amtDexRewardPerDay.MulInt(sdk.NewInt(ptypes.DaysPerYear)).MulInt(sdk.NewInt(100)).MulInt(sdk.NewInt(10)).QuoInt(edenPrice).QuoInt(totalStakedSnapshot) diff --git a/x/incentive/keeper/keeper.go b/x/incentive/keeper/keeper.go index 767b179b3..55956f07c 100644 --- a/x/incentive/keeper/keeper.go +++ b/x/incentive/keeper/keeper.go @@ -134,6 +134,11 @@ func (k Keeper) UpdateStakersRewardsUnclaimed(ctx sdk.Context, stakeIncentive ty // Calculate eden amount per epoch params := k.GetParams(ctx) + // Ensure stakeIncentive.TotalBlocksPerYear or stakeIncentive.AllocationEpochInBlocks are not zero to avoid division by zero + if stakeIncentive.TotalBlocksPerYear.IsZero() || stakeIncentive.AllocationEpochInBlocks.IsZero() { + return sdkerrors.Wrap(types.ErrNoNonInflationaryParams, "invalid inflationary params") + } + // Calculate edenAmountPerEpochStakersPerDay := stakeIncentive.EdenAmountPerYear.Mul(stakeIncentive.AllocationEpochInBlocks).Quo(stakeIncentive.TotalBlocksPerYear) @@ -353,6 +358,11 @@ func (k Keeper) UpdateLPRewardsUnclaimed(ctx sdk.Context, lpIncentive types.Ince // Proxy TVL = 20*0.3+30*0.5+40*1.0 totalProxyTVL := k.CalculateProxyTVL(ctx, baseCurrency) + // Ensure lpIncentive.TotalBlocksPerYear or lpIncentive.AllocationEpochInBlocks are not zero to avoid division by zero + if lpIncentive.TotalBlocksPerYear.IsZero() || lpIncentive.AllocationEpochInBlocks.IsZero() { + return sdkerrors.Wrap(types.ErrNoNonInflationaryParams, "invalid inflationary params") + } + // Calculate eden amount per epoch edenAmountPerEpochLPsPerDay := lpIncentive.EdenAmountPerYear.Mul(lpIncentive.AllocationEpochInBlocks).Quo(lpIncentive.TotalBlocksPerYear) diff --git a/x/incentive/keeper/keeper_stable_stake_lps.go b/x/incentive/keeper/keeper_stable_stake_lps.go index f33ccdf7e..155eb341e 100644 --- a/x/incentive/keeper/keeper_stable_stake_lps.go +++ b/x/incentive/keeper/keeper_stable_stake_lps.go @@ -27,7 +27,7 @@ func (k Keeper) CalculatePoolShareForStableStakeLPs(ctx sdk.Context, totalProxyT // Calculate Proxy TVL share considering multiplier proxyTVL := sdk.NewDecFromInt(tvl).Mul(poolInfo.Multiplier) - if totalProxyTVL.Equal(sdk.ZeroDec()) { + if totalProxyTVL.IsZero() { return sdk.ZeroDec() } poolShare := proxyTVL.Quo(totalProxyTVL) diff --git a/x/incentive/keeper/keeper_stakers.go b/x/incentive/keeper/keeper_stakers.go index 47fb9c23e..e1057e3df 100644 --- a/x/incentive/keeper/keeper_stakers.go +++ b/x/incentive/keeper/keeper_stakers.go @@ -69,6 +69,11 @@ func (k Keeper) CalculateEdenBoostRewards(ctx sdk.Context, delegatedAmt sdk.Int, // Compute eden reward based on above and param factors for each totalEden := delegatedAmt.Add(edenCommitted) + // Ensure incentiveInfo.DistributionEpochInBlocks is not zero to avoid division by zero + if incentiveInfo.DistributionEpochInBlocks.IsZero() { + return sdk.ZeroInt(), sdk.ZeroInt(), sdk.ZeroInt() + } + // Calculate edenBoostAPR % APR for eden boost epochNumsPerYear := incentiveInfo.TotalBlocksPerYear.Quo(incentiveInfo.DistributionEpochInBlocks) if epochNumsPerYear.IsZero() { diff --git a/x/leveragelp/keeper/position_close.go b/x/leveragelp/keeper/position_close.go index dfe8c0370..3edccaa16 100644 --- a/x/leveragelp/keeper/position_close.go +++ b/x/leveragelp/keeper/position_close.go @@ -19,6 +19,12 @@ func (k Keeper) ForceCloseLong(ctx sdk.Context, position types.Position, pool ty // Repay with interest debt := k.stableKeeper.UpdateInterestStackedByAddress(ctx, position.GetPositionAddress()) + + // Ensure position.LeveragedLpAmount is not zero to avoid division by zero + if position.LeveragedLpAmount.IsZero() { + return sdk.ZeroInt(), types.ErrAmountTooLow + } + repayAmount := debt.Borrowed.Add(debt.InterestStacked).Sub(debt.InterestPaid).Mul(lpAmount).Quo(position.LeveragedLpAmount) if err != nil { return sdk.ZeroInt(), err diff --git a/x/leveragelp/keeper/utils.go b/x/leveragelp/keeper/utils.go index 8687a0632..9eb57c0a4 100644 --- a/x/leveragelp/keeper/utils.go +++ b/x/leveragelp/keeper/utils.go @@ -64,6 +64,10 @@ func (k Keeper) GetLpTokenPrice(ctx sdk.Context, ammPool *ammtypes.Pool) (sdk.De if err != nil { return sdk.ZeroDec(), err } + // Ensure ammPool.TotalShares is not zero to avoid division by zero + if ammPool.TotalShares.IsZero() { + return sdk.ZeroDec(), types.ErrAmountTooLow + } lpTokenPrice := ammPoolTvl.Quo(sdkmath.LegacyNewDecFromInt(ammPool.TotalShares.Amount)) return lpTokenPrice, nil } diff --git a/x/margin/keeper/calc_mtp_interest_liabilities.go b/x/margin/keeper/calc_mtp_interest_liabilities.go index 771d31ea5..86a87e36d 100644 --- a/x/margin/keeper/calc_mtp_interest_liabilities.go +++ b/x/margin/keeper/calc_mtp_interest_liabilities.go @@ -8,7 +8,12 @@ import ( "github.com/elys-network/elys/x/margin/types" ) -func (k Keeper) CalcMTPBorrowInterestLiabilities(ctx sdk.Context, mtp *types.MTP, borrowInterestRate sdk.Dec, epochPosition, epochLength int64, ammPool ammtypes.Pool, collateralAsset string, baseCurrency string) sdk.Int { +func (k Keeper) CalcMTPBorrowInterestLiabilities(ctx sdk.Context, mtp *types.MTP, borrowInterestRate sdk.Dec, epochPosition, epochLength int64, ammPool ammtypes.Pool, collateralAsset string, baseCurrency string) (sdk.Int, error) { + // Ensure borrow interest rate or liabilities are not zero to avoid division by zero + if borrowInterestRate.IsZero() || mtp.Liabilities.IsZero() { + return sdk.ZeroInt(), types.ErrAmountTooLow + } + var borrowInterestRational, liabilitiesRational, rate, epochPositionRational, epochLengthRational big.Rat rate.SetFloat64(borrowInterestRate.MustFloat64()) @@ -23,7 +28,7 @@ func (k Keeper) CalcMTPBorrowInterestLiabilities(ctx sdk.Context, mtp *types.MTP unpaidCollateralIn := sdk.NewCoin(mtp.Collaterals[collateralIndex].Denom, mtp.BorrowInterestUnpaidCollaterals[collateralIndex].Amount) C, err := k.EstimateSwapGivenOut(ctx, unpaidCollateralIn, baseCurrency, ammPool) if err != nil { - return sdk.ZeroInt() + return sdk.ZeroInt(), err } unpaidCollaterals = unpaidCollaterals.Add(C) @@ -50,5 +55,5 @@ func (k Keeper) CalcMTPBorrowInterestLiabilities(ctx sdk.Context, mtp *types.MTP // apply take profit borrow rate to borrow interest borrowInterestNewInt = sdk.NewDecFromInt(borrowInterestNewInt).Mul(mtp.TakeProfitBorrowRate).TruncateInt() - return borrowInterestNewInt + return borrowInterestNewInt, nil } diff --git a/x/margin/keeper/calc_mtp_take_profit_borrow_rate.go b/x/margin/keeper/calc_mtp_take_profit_borrow_rate.go index 37ca8cdec..2d2495605 100644 --- a/x/margin/keeper/calc_mtp_take_profit_borrow_rate.go +++ b/x/margin/keeper/calc_mtp_take_profit_borrow_rate.go @@ -13,6 +13,11 @@ func (k Keeper) CalcMTPTakeProfitBorrowRate(ctx sdk.Context, mtp *types.MTP) (sd var totalTakeProfitBorrowRate sdk.Dec = sdk.ZeroDec() for takeProfitCustodyIndex, takeProfitCustody := range mtp.TakeProfitCustodies { + // Ensure mtp.Custodies[takeProfitCustodyIndex].Amount is not zero to avoid division by zero + if mtp.Custodies[takeProfitCustodyIndex].Amount.IsZero() { + return sdk.ZeroDec(), types.ErrAmountTooLow + } + // Calculate the borrow rate for this takeProfitCustody takeProfitBorrowRateInt := takeProfitCustody.Amount.Quo(mtp.Custodies[takeProfitCustodyIndex].Amount) diff --git a/x/margin/keeper/handle_borrow_interest.go b/x/margin/keeper/handle_borrow_interest.go index de9e720bc..c7394ed39 100644 --- a/x/margin/keeper/handle_borrow_interest.go +++ b/x/margin/keeper/handle_borrow_interest.go @@ -22,7 +22,10 @@ func (k Keeper) HandleBorrowInterest(ctx sdk.Context, mtp *types.MTP, pool *type } baseCurrency := entry.Denom - borrowInterestPayment := k.CalcMTPBorrowInterestLiabilities(ctx, mtp, pool.BorrowInterestRate, epochPosition, epochLength, ammPool, collateralAsset, baseCurrency) + borrowInterestPayment, err := k.CalcMTPBorrowInterestLiabilities(ctx, mtp, pool.BorrowInterestRate, epochPosition, epochLength, ammPool, collateralAsset, baseCurrency) + if err != nil { + return err + } finalBorrowInterestPayment := k.HandleBorrowInterestPayment(ctx, collateralAsset, custodyAsset, borrowInterestPayment, mtp, pool, ammPool, baseCurrency) // finalInterestPayment is in custodyAsset @@ -30,6 +33,6 @@ func (k Keeper) HandleBorrowInterest(ctx sdk.Context, mtp *types.MTP, pool *type return err } - _, err := k.UpdateMTPHealth(ctx, *mtp, ammPool, baseCurrency) + _, err = k.UpdateMTPHealth(ctx, *mtp, ammPool, baseCurrency) return err } diff --git a/x/margin/keeper/handle_funding_fee_distribution.go b/x/margin/keeper/handle_funding_fee_distribution.go index 279da9ba4..1ee389b86 100644 --- a/x/margin/keeper/handle_funding_fee_distribution.go +++ b/x/margin/keeper/handle_funding_fee_distribution.go @@ -54,9 +54,17 @@ func (k Keeper) HandleFundingFeeDistribution(ctx sdk.Context, mtps []*types.MTP, // calc funding fee share fundingFeeShare := sdk.ZeroDec() if fundingRate.IsNegative() && mtp.Position == types.Position_LONG { + // Ensure liabilitiesLong is not zero to avoid division by zero + if liabilitiesLong.IsZero() { + return types.ErrAmountTooLow + } fundingFeeShare = sdk.NewDecFromInt(mtp.Liabilities).Quo(sdk.NewDecFromInt(liabilitiesLong)) } if fundingRate.IsPositive() && mtp.Position == types.Position_SHORT { + // Ensure liabilitiesShort is not zero to avoid division by zero + if liabilitiesShort.IsZero() { + return types.ErrAmountTooLow + } fundingFeeShare = sdk.NewDecFromInt(mtp.Liabilities).Quo(sdk.NewDecFromInt(liabilitiesShort)) } diff --git a/x/margin/keeper/keeper.go b/x/margin/keeper/keeper.go index 731996947..a0a891955 100644 --- a/x/margin/keeper/keeper.go +++ b/x/margin/keeper/keeper.go @@ -401,8 +401,12 @@ func (k Keeper) BorrowInterestRateComputationByPosition(ctx sdk.Context, pool ty balance := sdk.NewDecFromInt(asset.AssetBalance.Add(ammBalance)) liabilities := sdk.NewDecFromInt(asset.Liabilities) + // Ensure balance is not zero to avoid division by zero + if balance.IsZero() { + return sdk.ZeroDec(), nil + } if balance.Add(liabilities).IsZero() { - return sdk.ZeroDec(), err + return sdk.ZeroDec(), nil } mul := balance.Add(liabilities).Quo(balance) @@ -464,6 +468,11 @@ func (k Keeper) CheckMinLiabilities(ctx sdk.Context, collateralAmount sdk.Coin, var borrowInterestRational, liabilitiesRational, rate big.Rat minBorrowInterestRate := k.GetBorrowInterestRateMin(ctx) + // Ensure minBorrowInterestRate is not zero to avoid division by zero + if minBorrowInterestRate.IsZero() { + return types.ErrAmountTooLow + } + collateralAmountDec := sdk.NewDecFromInt(collateralAmount.Amount) liabilitiesDec := collateralAmountDec.Mul(eta) liabilities := sdk.NewUint(liabilitiesDec.TruncateInt().Uint64()) diff --git a/x/margin/types/calc_funding_rate.go b/x/margin/types/calc_funding_rate.go index 3697c661c..00c6af707 100644 --- a/x/margin/types/calc_funding_rate.go +++ b/x/margin/types/calc_funding_rate.go @@ -8,10 +8,19 @@ import ( func CalcFundingRate(longAmount, shortAmount sdk.Int, baseRate, maxRate, minRate sdk.Dec) sdk.Dec { var ratio sdk.Dec + // Check for division by zero when longAmount > shortAmount if longAmount.GT(shortAmount) { + if shortAmount.IsZero() { + // Handle the case where shortAmount is zero + return maxRate + } ratio = sdk.NewDecFromInt(longAmount).Quo(sdk.NewDecFromInt(shortAmount)) return sdk.MinDec(sdk.MaxDec(baseRate.Mul(ratio), minRate), maxRate) } else if shortAmount.GT(longAmount) { + if longAmount.IsZero() { + // Handle the case where longAmount is zero + return maxRate + } ratio = sdk.NewDecFromInt(shortAmount).Quo(sdk.NewDecFromInt(longAmount)) return sdk.MinDec(sdk.MaxDec(baseRate.Mul(ratio).Neg(), minRate), maxRate) } else { diff --git a/x/margin/types/calc_funding_rate_test.go b/x/margin/types/calc_funding_rate_test.go index b28804926..7c2a6541a 100644 --- a/x/margin/types/calc_funding_rate_test.go +++ b/x/margin/types/calc_funding_rate_test.go @@ -44,6 +44,18 @@ func TestCalculateFundingRate(t *testing.T) { shortAmount: sdk.NewInt(500000), expectedRate: "0.001000000000000000", // Capped at maxRate }, + { + name: "Zero Short Amount", + longAmount: sdk.NewInt(1000000), + shortAmount: sdk.NewInt(0), + expectedRate: "0.001000000000000000", // maxRate when short amount is zero + }, + { + name: "Zero Long Amount", + longAmount: sdk.NewInt(0), + shortAmount: sdk.NewInt(1000000), + expectedRate: "0.001000000000000000", // maxRate when long amount is zero + }, } for _, tt := range tests { diff --git a/x/margin/types/calc_mtp_take_profit_custodies.go b/x/margin/types/calc_mtp_take_profit_custodies.go index f9ca0b6d1..22ba193a0 100644 --- a/x/margin/types/calc_mtp_take_profit_custodies.go +++ b/x/margin/types/calc_mtp_take_profit_custodies.go @@ -7,7 +7,7 @@ import ( func CalcMTPTakeProfitCustodies(mtp *MTP) sdk.Coins { takeProfitCustodies := mtp.TakeProfitCustodies for custodyIndex, custody := range mtp.Custodies { - if IsTakeProfitPriceInifite(mtp) { + if IsTakeProfitPriceInifite(mtp) || mtp.TakeProfitPrice.IsZero() { takeProfitCustodies[custodyIndex].Amount = custody.Amount continue } diff --git a/x/stablestake/keeper/msg_server_bond.go b/x/stablestake/keeper/msg_server_bond.go index 5d2e63c37..dfb7e4d78 100644 --- a/x/stablestake/keeper/msg_server_bond.go +++ b/x/stablestake/keeper/msg_server_bond.go @@ -24,6 +24,9 @@ func (k msgServer) Bond(goCtx context.Context, msg *types.MsgBond) (*types.MsgBo } shareDenom := types.GetShareDenom() + if params.RedemptionRate.IsZero() { + return nil, types.ErrRedemptionRateIsZero + } shareAmount := sdk.NewDecFromInt(depositCoin.Amount).Quo(params.RedemptionRate).RoundInt() shareCoins := sdk.NewCoins(sdk.NewCoin(shareDenom, shareAmount)) diff --git a/x/stablestake/types/errors.go b/x/stablestake/types/errors.go index 9d08c56e9..3a542ac2d 100644 --- a/x/stablestake/types/errors.go +++ b/x/stablestake/types/errors.go @@ -8,6 +8,7 @@ import ( // x/stablestake module sentinel errors var ( - ErrInvalidDepositDenom = sdkerrors.Register(ModuleName, 1, "invalid deposit denom") - ErrInvalidBorrowDenom = sdkerrors.Register(ModuleName, 2, "invalid borrow denom") + ErrInvalidDepositDenom = sdkerrors.Register(ModuleName, 1, "invalid deposit denom") + ErrInvalidBorrowDenom = sdkerrors.Register(ModuleName, 2, "invalid borrow denom") + ErrRedemptionRateIsZero = sdkerrors.Register(ModuleName, 3, "redemption rate is zero") )