From 2ab3a35d50076cc1f5d09b2004e5a8f8c3b50547 Mon Sep 17 00:00:00 2001 From: Troy Kessler Date: Wed, 16 Jul 2025 14:58:03 +0200 Subject: [PATCH 1/5] chore: use kyve for storage cost first --- x/bundles/keeper/logic_bundles.go | 40 +++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/x/bundles/keeper/logic_bundles.go b/x/bundles/keeper/logic_bundles.go index efe19348..e5b9dc6e 100644 --- a/x/bundles/keeper/logic_bundles.go +++ b/x/bundles/keeper/logic_bundles.go @@ -257,9 +257,9 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk return } - // subtract storage cost from remaining total payout. We split the storage cost between all coins and charge - // the amount per coin, the idea is that every coin should contribute the same USD value to the total storage - // reward. This is done by defining the storage cost as USD / byte and the coin weights as USD / coin denom. + // subtract storage cost from remaining total payout. We first try to cover the storage cost with the + // native $KYVE coin, if that is not enough the remaining USD value is split equally between the remaining + // coins. // // If there is not enough of a coin available to cover the storage reward per coin we simply charge what is left, // so there can be the case that the storageRewards are less than what we actually wanted to pay out. This is @@ -267,15 +267,45 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk // funds left of each coin, and in the case there are not enough the coins are removed and therefore for the // next bundle we split between the other remaining coins. whitelist := k.fundersKeeper.GetCoinWhitelistMap(ctx) + storageCost := k.GetStorageCost(ctx, bundleProposal.StorageProviderId).MulInt64(int64(bundleProposal.DataSize)) + + kyveWeight := whitelist[globalTypes.Denom].CoinWeight + kyveCurrencyUnit := math.LegacyNewDec(10).Power(uint64(whitelist[globalTypes.Denom].CoinDecimals)) + + if !kyveWeight.IsZero() { + kyveAmount := sdk.NewCoins(sdk.NewCoin(globalTypes.Denom, storageCost.Mul(kyveWeight).Quo(kyveCurrencyUnit).TruncateInt())) + bundleReward.UploaderStorageCost = totalPayout.Min(kyveAmount) + totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) + if totalPayout.IsZero() { + return + } + + // calculate how much of the storage reward was initially paid with native + // kyve and give the remainder to the other coins + storageCostPaid := bundleReward.UploaderStorageCost.AmountOf(globalTypes.Denom) + storageCostPaidUsd := math.LegacyNewDec(storageCostPaid.Int64()).Mul(kyveCurrencyUnit).Quo(kyveWeight) + + if storageCost.GTE(storageCostPaidUsd) { + storageCost = storageCost.Sub(storageCostPaidUsd) + } + } + + // get the amount of non-native $KYVE coins + storageCostCoinsLength := int64(totalPayout.Len()) + if found, _ := totalPayout.Find(globalTypes.Denom); found { + storageCostCoinsLength-- + } + // wantedStorageRewards are the amounts based on the current storage cost we want to pay out, this can be more // than we have available in totalPayout wantedStorageRewards := sdk.NewCoins() // storageCostPerCoin is the storage cost in $USD for each coin. This implies that each coin contributes the same // amount of value to the storage rewards - storageCostPerCoin := k.GetStorageCost(ctx, bundleProposal.StorageProviderId).MulInt64(int64(bundleProposal.DataSize)).QuoInt64(int64(totalPayout.Len())) + storageCostPerCoin := storageCost.QuoInt64(storageCostCoinsLength) for _, coin := range totalPayout { weight := whitelist[coin.Denom].CoinWeight - if weight.IsZero() { + // skip the native kyve denom since we already subtracted it above + if coin.Denom == globalTypes.Denom || weight.IsZero() { continue } From 8693f1109f784290daf74ab29eedeb231c12f519 Mon Sep 17 00:00:00 2001 From: Troy Kessler Date: Thu, 17 Jul 2025 10:35:06 +0200 Subject: [PATCH 2/5] chore: completed new storage cost algorithm --- .../keeper_suite_inflation_splitting_test.go | 26 ++++++- .../keeper/keeper_suite_valid_bundles_test.go | 13 +++- x/bundles/keeper/logic_bundles.go | 74 ++++++++++--------- 3 files changed, 72 insertions(+), 41 deletions(-) diff --git a/x/bundles/keeper/keeper_suite_inflation_splitting_test.go b/x/bundles/keeper/keeper_suite_inflation_splitting_test.go index ab800e70..8aa4acae 100644 --- a/x/bundles/keeper/keeper_suite_inflation_splitting_test.go +++ b/x/bundles/keeper/keeper_suite_inflation_splitting_test.go @@ -1512,12 +1512,21 @@ var _ = Describe("inflation splitting", Ordered, func() { // for kyve coin (7410 - (7410 * 0.01) - _((100 * 0.5) / (3 * 1))_) * 0.1 + _((100 * 0.5) / (3 * 1))_ // for acoin (10_000 - (10_000 * 0.01) - _((100 * 0.5) / (3 * 1))_) * 0.1 + _((100 * 0.5) / (3 * 1))_ // for bcoin coins (20_000 - (20_000 * 0.01) - _((100 * 0.5) / (3 * 2))_) * 0.1 + _((100 * 0.5) / (3 * 2))_ - Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(748), i.ACoin(1004), i.BCoin(1987)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the kyve amount is higher for the uploader + // commission rewards and the amount of the other coins are lower because the uploader reward includes the storage + // cost (if kyve is used first the contribution of the remaining coins will be lower) + // VALUES BEFORE: 1004acoin,1987bcoin,748tkyve + Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(778), i.ACoin(990), i.BCoin(1980)).String())) // assert uploader self delegation rewards (here we round up since the result of delegation rewards is the remainder minus the truncated commission rewards) // for kyve coin (7410 - (7410 * 0.01) - _((100 * 0.5) / (3 * 1))_) * (1 - 0.1) // for acoin (10_000 - (10_000 * 0.01) - _((100 * 0.5) / (3 * 1))_) * (1 - 0.1) // for bcoin (20_000 - (20_000 * 0.01) - _((100 * 0.5) / (3 * 2))_) * (1 - 0.1) - Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(6588), i.ACoin(8896), i.BCoin(17813)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the delegators receive less kyve and more + // of the remaining coins + // VALUES BEFORE: 8896acoin,17813bcoin,6588tkyve + Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(6558), i.ACoin(8910), i.BCoin(17820)).String())) fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0) @@ -1622,12 +1631,21 @@ var _ = Describe("inflation splitting", Ordered, func() { // for kyve coin (24720 - (24720 * 0.01) - _((100 * 0.5) / (3 * 1))_) * 0.1 + _((100 * 0.5) / (3 * 1))_ // for acoin (10_000 - (10_000 * 0.01) - _((100 * 0.5) / (3 * 1))_) * 0.1 + _((100 * 0.5) / (3 * 1))_ // for bcoin coins (20_000 - (20_000 * 0.01) - _((100 * 0.5) / (3 * 2))_) * 0.1 + _((100 * 0.5) / (3 * 2))_ - Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(2461), i.ACoin(1004), i.BCoin(1987)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the kyve amount is higher for the uploader + // commission rewards and the amount of the other coins are lower because the uploader reward includes the storage + // cost (if kyve is used first the contribution of the remaining coins will be lower) + // VALUES BEFORE: 1004acoin,1987bcoin,2461tkyve + Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(2492), i.ACoin(990), i.BCoin(1980)).String())) // assert uploader self delegation rewards (here we round up since the result of delegation rewards is the remainder minus the truncated commission rewards) // for kyve coin (24720 - (24720 * 0.01) - _((100 * 0.5) / (3 * 1))_) * (1 - 0.1) // for acoin (10_000 - (10_000 * 0.01) - _((100 * 0.5) / (3 * 1))_) * (1 - 0.1) // for bcoin (20_000 - (20_000 * 0.01) - _((100 * 0.5) / (3 * 2))_) * (1 - 0.1) - Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(22012), i.ACoin(8896), i.BCoin(17813)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the delegators receive less kyve and more + // of the remaining coins + // VALUES BEFORE: 8896acoin,17813bcoin,22012tkyve + Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(21981), i.ACoin(8910), i.BCoin(17820)).String())) fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0) diff --git a/x/bundles/keeper/keeper_suite_valid_bundles_test.go b/x/bundles/keeper/keeper_suite_valid_bundles_test.go index 2240d779..367aa19d 100644 --- a/x/bundles/keeper/keeper_suite_valid_bundles_test.go +++ b/x/bundles/keeper/keeper_suite_valid_bundles_test.go @@ -1957,10 +1957,19 @@ var _ = Describe("valid bundles", Ordered, func() { // (amount_per_bundle - treasury_reward - storage_cost) * uploader_commission + storage_cost // storage_cost = 1MB * storage_price / coin_length * coin_price // (amount_per_bundle - (amount_per_bundle * 0.01) - _((1048576 * 0.000000006288 * 10**coin_decimals) / (4 * coin_weight))_) * 0.1 + _((1048576 * 0.000000006288) / (4 * coin_weight))_ - Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), poolAccountUploader.Staker).String()).To(Equal(sdk.NewCoins(i.KYVECoin(125_973), i.ACoin(99_143), i.BCoin(116_661_015_771_428_571), i.CCoin(100_765)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the kyve amount is higher for the uploader + // commission rewards and the amount of the other coins are lower because the uploader reward includes the storage + // cost (if kyve is used first the contribution of the remaining coins will be lower) + // VALUES BEFORE: 99_143acoin,116_661_015_771_428_571bcoin,100_765ccoin,125_973tkyve + Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), poolAccountUploader.Staker).String()).To(Equal(sdk.NewCoins(i.KYVECoin(87_012), i.ACoin(99_000), i.BCoin(99_000_163_885_714_285), i.CCoin(99_000)).String())) // assert uploader self delegation rewards (here we round up since the result of delegation rewards is the remainder minus the truncated commission rewards) // (amount_per_bundle - (amount_per_bundle * 0.01) - _((29970208 * 0.000000006288 * 1**coin_decimals) / (4 * coin_weight))_) * (1 - 0.1) - Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(864_027), i.ACoin(890_857), i.BCoin(873_338_984_228_571_429), i.CCoin(889_235)).String())) + + // STORAGE COST UPDATE: with $KYVE being used first for storage cost the delegators receive less kyve and more + // of the remaining coins + // VALUES BEFORE: 890_857acoin,873_338_984_228_571_429bcoin,889_235ccoin,864_027tkyve + Expect(s.App().StakersKeeper.GetOutstandingRewards(s.Ctx(), i.STAKER_0, i.STAKER_0).String()).To(Equal(sdk.NewCoins(i.KYVECoin(783_108), i.ACoin(891_000), i.BCoin(890_999_836_114_285_715), i.CCoin(891_000)).String())) fundingState, _ := s.App().FundersKeeper.GetFundingState(s.Ctx(), 0) diff --git a/x/bundles/keeper/logic_bundles.go b/x/bundles/keeper/logic_bundles.go index e5b9dc6e..cacc61f8 100644 --- a/x/bundles/keeper/logic_bundles.go +++ b/x/bundles/keeper/logic_bundles.go @@ -269,11 +269,13 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk whitelist := k.fundersKeeper.GetCoinWhitelistMap(ctx) storageCost := k.GetStorageCost(ctx, bundleProposal.StorageProviderId).MulInt64(int64(bundleProposal.DataSize)) + kyveCoinFound, kyveCoin := totalPayout.Find(globalTypes.Denom) + kyveWeight := whitelist[globalTypes.Denom].CoinWeight kyveCurrencyUnit := math.LegacyNewDec(10).Power(uint64(whitelist[globalTypes.Denom].CoinDecimals)) - if !kyveWeight.IsZero() { - kyveAmount := sdk.NewCoins(sdk.NewCoin(globalTypes.Denom, storageCost.Mul(kyveWeight).Quo(kyveCurrencyUnit).TruncateInt())) + if kyveCoinFound && !kyveWeight.IsZero() { + kyveAmount := sdk.NewCoins(sdk.NewCoin(globalTypes.Denom, storageCost.Mul(kyveCurrencyUnit).Quo(kyveWeight).TruncateInt())) bundleReward.UploaderStorageCost = totalPayout.Min(kyveAmount) totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) if totalPayout.IsZero() { @@ -283,49 +285,51 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk // calculate how much of the storage reward was initially paid with native // kyve and give the remainder to the other coins storageCostPaid := bundleReward.UploaderStorageCost.AmountOf(globalTypes.Denom) - storageCostPaidUsd := math.LegacyNewDec(storageCostPaid.Int64()).Mul(kyveCurrencyUnit).Quo(kyveWeight) + storageCostPaidUsd := math.LegacyNewDec(storageCostPaid.Int64()).Mul(kyveWeight).Quo(kyveCurrencyUnit) if storageCost.GTE(storageCostPaidUsd) { storageCost = storageCost.Sub(storageCostPaidUsd) } } - // get the amount of non-native $KYVE coins - storageCostCoinsLength := int64(totalPayout.Len()) - if found, _ := totalPayout.Find(globalTypes.Denom); found { - storageCostCoinsLength-- - } + kyveCoinFound, kyveCoin = totalPayout.Find(globalTypes.Denom) + remainingCoins := totalPayout + if kyveCoinFound { + remainingCoins = totalPayout.Sub(kyveCoin) + } + + if !storageCost.IsZero() && int64(remainingCoins.Len()) > 0 { + // wantedStorageRewards are the amounts based on the current storage cost we want to pay out, this can be more + // than we have available in totalPayout + wantedStorageRewards := sdk.NewCoins() + // storageCostPerCoin is the storage cost in $USD for each coin. This implies that each coin contributes the same + // amount of value to the storage rewards + storageCostPerCoin := storageCost.QuoInt64(int64(remainingCoins.Len())) + for _, coin := range remainingCoins { + weight := whitelist[coin.Denom].CoinWeight + // skip the native kyve denom since we already subtracted it above + if coin.Denom == globalTypes.Denom || weight.IsZero() { + continue + } - // wantedStorageRewards are the amounts based on the current storage cost we want to pay out, this can be more - // than we have available in totalPayout - wantedStorageRewards := sdk.NewCoins() - // storageCostPerCoin is the storage cost in $USD for each coin. This implies that each coin contributes the same - // amount of value to the storage rewards - storageCostPerCoin := storageCost.QuoInt64(storageCostCoinsLength) - for _, coin := range totalPayout { - weight := whitelist[coin.Denom].CoinWeight - // skip the native kyve denom since we already subtracted it above - if coin.Denom == globalTypes.Denom || weight.IsZero() { - continue + // currencyUnit is the amount of base denoms of the currency + currencyUnit := math.LegacyNewDec(10).Power(uint64(whitelist[coin.Denom].CoinDecimals)) + // amount is the value of storageCostPerCoin in the base denomination of the currency. We calculate this + // by multiplying first with the amount of base denoms of the currency and then divide this by the $USD + // value per currency unit which is the weight. + amount := storageCostPerCoin.Mul(currencyUnit).Quo(weight).TruncateInt() + wantedStorageRewards = wantedStorageRewards.Add(sdk.NewCoin(coin.Denom, amount)) } - // currencyUnit is the amount of base denoms of the currency - currencyUnit := math.LegacyNewDec(10).Power(uint64(whitelist[coin.Denom].CoinDecimals)) - // amount is the value of storageCostPerCoin in the base denomination of the currency. We calculate this - // by multiplying first with the amount of base denoms of the currency and then divide this by the $USD - // value per currency unit which is the weight. - amount := storageCostPerCoin.Mul(currencyUnit).Quo(weight).TruncateInt() - wantedStorageRewards = wantedStorageRewards.Add(sdk.NewCoin(coin.Denom, amount)) - } - - // we take the min here since there can be the case where we want to charge more coins for the storage - // reward than we have left in the total payout - bundleReward.UploaderStorageCost = totalPayout.Min(wantedStorageRewards) + // we take the min here since there can be the case where we want to charge more coins for the storage + // reward than we have left in the total payout + bundleReward.UploaderStorageCost = totalPayout.Min(wantedStorageRewards) - // the remaining total payout is split between the uploader and his delegators. - totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) - if totalPayout.IsZero() { - return + // the remaining total payout is split between the uploader and his delegators. + totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) + if totalPayout.IsZero() { + return + } } commission := k.stakerKeeper.GetValidatorPoolCommission(ctx, bundleProposal.Uploader, poolId) From 9ac707eba387390676584c4a6b7f191d9b3d14f4 Mon Sep 17 00:00:00 2001 From: Troy Kessler Date: Thu, 17 Jul 2025 10:43:16 +0200 Subject: [PATCH 3/5] chore: lint --- proto/kyve/delegation/v1beta1/tx.proto | 7 +++---- x/bundles/keeper/logic_bundles.go | 6 ++---- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/proto/kyve/delegation/v1beta1/tx.proto b/proto/kyve/delegation/v1beta1/tx.proto index bceca101..6c5f509d 100644 --- a/proto/kyve/delegation/v1beta1/tx.proto +++ b/proto/kyve/delegation/v1beta1/tx.proto @@ -8,12 +8,11 @@ import "cosmos_proto/cosmos.proto"; option go_package = "github.com/KYVENetwork/chain/x/stakers/types_delegation_v1beta1"; /* - NOTICE: - This file needs to be kept for backwards compatibility with the governance module. - Otherwise, it is not possible to decode legacy delegation param update proposals. + NOTICE: + This file needs to be kept for backwards compatibility with the governance module. + Otherwise, it is not possible to decode legacy delegation param update proposals. */ - // Msg defines the Msg service. service Msg { option (cosmos.msg.v1.service) = true; diff --git a/x/bundles/keeper/logic_bundles.go b/x/bundles/keeper/logic_bundles.go index cacc61f8..570cef4b 100644 --- a/x/bundles/keeper/logic_bundles.go +++ b/x/bundles/keeper/logic_bundles.go @@ -269,12 +269,10 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk whitelist := k.fundersKeeper.GetCoinWhitelistMap(ctx) storageCost := k.GetStorageCost(ctx, bundleProposal.StorageProviderId).MulInt64(int64(bundleProposal.DataSize)) - kyveCoinFound, kyveCoin := totalPayout.Find(globalTypes.Denom) - kyveWeight := whitelist[globalTypes.Denom].CoinWeight kyveCurrencyUnit := math.LegacyNewDec(10).Power(uint64(whitelist[globalTypes.Denom].CoinDecimals)) - if kyveCoinFound && !kyveWeight.IsZero() { + if found, _ := totalPayout.Find(globalTypes.Denom); found && !kyveWeight.IsZero() { kyveAmount := sdk.NewCoins(sdk.NewCoin(globalTypes.Denom, storageCost.Mul(kyveCurrencyUnit).Quo(kyveWeight).TruncateInt())) bundleReward.UploaderStorageCost = totalPayout.Min(kyveAmount) totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) @@ -292,7 +290,7 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk } } - kyveCoinFound, kyveCoin = totalPayout.Find(globalTypes.Denom) + kyveCoinFound, kyveCoin := totalPayout.Find(globalTypes.Denom) remainingCoins := totalPayout if kyveCoinFound { remainingCoins = totalPayout.Sub(kyveCoin) From ff72cca72f04167b4e1ac2285b2e3d8e1cbbbdb9 Mon Sep 17 00:00:00 2001 From: Troy Kessler Date: Wed, 30 Jul 2025 15:28:43 +0200 Subject: [PATCH 4/5] fix: storage cost addition bug --- x/bundles/keeper/keeper_suite_valid_bundles_test.go | 2 +- x/bundles/keeper/logic_bundles.go | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/x/bundles/keeper/keeper_suite_valid_bundles_test.go b/x/bundles/keeper/keeper_suite_valid_bundles_test.go index 367aa19d..c538bb2d 100644 --- a/x/bundles/keeper/keeper_suite_valid_bundles_test.go +++ b/x/bundles/keeper/keeper_suite_valid_bundles_test.go @@ -1962,7 +1962,7 @@ var _ = Describe("valid bundles", Ordered, func() { // commission rewards and the amount of the other coins are lower because the uploader reward includes the storage // cost (if kyve is used first the contribution of the remaining coins will be lower) // VALUES BEFORE: 99_143acoin,116_661_015_771_428_571bcoin,100_765ccoin,125_973tkyve - Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), poolAccountUploader.Staker).String()).To(Equal(sdk.NewCoins(i.KYVECoin(87_012), i.ACoin(99_000), i.BCoin(99_000_163_885_714_285), i.CCoin(99_000)).String())) + Expect(s.App().StakersKeeper.GetOutstandingCommissionRewards(s.Ctx(), poolAccountUploader.Staker).String()).To(Equal(sdk.NewCoins(i.KYVECoin(206_892), i.ACoin(99_000), i.BCoin(99_000_163_885_714_285), i.CCoin(99_000)).String())) // assert uploader self delegation rewards (here we round up since the result of delegation rewards is the remainder minus the truncated commission rewards) // (amount_per_bundle - (amount_per_bundle * 0.01) - _((29970208 * 0.000000006288 * 1**coin_decimals) / (4 * coin_weight))_) * (1 - 0.1) diff --git a/x/bundles/keeper/logic_bundles.go b/x/bundles/keeper/logic_bundles.go index 570cef4b..6ecac356 100644 --- a/x/bundles/keeper/logic_bundles.go +++ b/x/bundles/keeper/logic_bundles.go @@ -287,6 +287,8 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk if storageCost.GTE(storageCostPaidUsd) { storageCost = storageCost.Sub(storageCostPaidUsd) + } else { + storageCost = math.LegacyZeroDec() } } @@ -321,10 +323,11 @@ func (k Keeper) calculatePayouts(ctx sdk.Context, poolId uint64, totalPayout sdk // we take the min here since there can be the case where we want to charge more coins for the storage // reward than we have left in the total payout - bundleReward.UploaderStorageCost = totalPayout.Min(wantedStorageRewards) + multiCoinStorageCostReward := totalPayout.Min(wantedStorageRewards) + bundleReward.UploaderStorageCost = bundleReward.UploaderStorageCost.Add(multiCoinStorageCostReward...) // the remaining total payout is split between the uploader and his delegators. - totalPayout = totalPayout.Sub(bundleReward.UploaderStorageCost...) + totalPayout = totalPayout.Sub(multiCoinStorageCostReward...) if totalPayout.IsZero() { return } From 3c1f7a10308aa7ba56fb5905b6013e49a5a4bdb6 Mon Sep 17 00:00:00 2001 From: Troy Kessler Date: Wed, 30 Jul 2025 15:37:21 +0200 Subject: [PATCH 5/5] chore: update changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f4916c9..43525f17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,12 @@ An '!' indicates a state machine breaking change. +## Unreleased + +### Improvements + +- ! [#266](https://github.com/KYVENetwork/chain/pull/266) Improve storage cost payout. + ## [v2.1.0](https://github.com/KYVENetwork/chain/releases/tag/v2.1.0) - 2025-04-29 ### Improvements