Skip to content

Commit

Permalink
fix: run proto disto on epoch (#8106)
Browse files Browse the repository at this point in the history
* fix: run protorev distro on epoch

* chore: add CHANGELOG.md entry

* fix: only run protorev distro on the day epoch

* chore: rename CalculateDistributeProfits fn to CalculateAndDistributeProfits
  • Loading branch information
PaddyMc authored Apr 22, 2024
1 parent 5b5ab3f commit 7a420c8
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 178 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### State Breaking

* [#8053](https://github.com/osmosis-labs/osmosis/pull/8053) Reset validator signing info missed blocks counter
* [#8106](https://github.com/osmosis-labs/osmosis/pull/8106) Enable ProtoRev distro on epoch.
* [#8053](https://github.com/osmosis-labs/osmosis/pull/8053) Reset validator signing info missed blocks counter.
* [#8030](https://github.com/osmosis-labs/osmosis/pull/8030) Delete legacy behavior where lockups could not unbond at very small block heights on a testnet.
* [#7005](https://github.com/osmosis-labs/osmosis/pull/7005) Adding deactivated smart account module.

Expand Down
47 changes: 47 additions & 0 deletions x/protorev/keeper/epoch_hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ func (h EpochHooks) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epoch
if h.k.GetProtoRevEnabled(ctx) {
switch epochIdentifier {
case "day":
// Calculate and distribute protorev profits
err := h.CalculateAndDistributeProfits(ctx)
if err != nil {
return err
}

// Increment number of days since module genesis to properly calculate developer fees after cyclic arbitrage trades
if daysSinceGenesis, err := h.k.GetDaysSinceModuleGenesis(ctx); err != nil {
h.k.SetDaysSinceModuleGenesis(ctx, 1)
Expand All @@ -54,6 +60,47 @@ func (h EpochHooks) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epoch
return nil
}

// CalculateAndDistributeProfits is executed after epoch. It gets the current base denom profits and distributes them.
func (h EpochHooks) CalculateAndDistributeProfits(ctx sdk.Context) error {
// Get the current arb profits (only in base denoms to prevent spam vector)
profit, err := h.k.CurrentBaseDenomProfits(ctx)
if err != nil {
return err
}

// Distribute profits to developer account, community pool, and burn osmo
err = h.k.DistributeProfit(ctx, profit)
if err != nil {
return err
}
return nil
}

// CurrentBaseDenomProfits retrieves the current balance of the protorev module account and filters for base denoms.
func (k Keeper) CurrentBaseDenomProfits(ctx sdk.Context) (sdk.Coins, error) {
moduleAcc := k.accountKeeper.GetModuleAddress(types.ModuleName)

baseDenoms, err := k.GetAllBaseDenoms(ctx)
if err != nil {
return nil, err
}

// Get the current protorev balance of all denoms
protorevBalanceAllDenoms := k.bankKeeper.GetAllBalances(ctx, moduleAcc)

// Filter for base denoms
var protorevBalanceBaseDenoms sdk.Coins

for _, baseDenom := range baseDenoms {
amountOfBaseDenom := protorevBalanceAllDenoms.AmountOf(baseDenom.Denom)
if !amountOfBaseDenom.IsZero() {
protorevBalanceBaseDenoms = append(protorevBalanceBaseDenoms, sdk.NewCoin(baseDenom.Denom, amountOfBaseDenom))
}
}

return protorevBalanceBaseDenoms.Sort(), nil
}

// UpdatePools first deletes all of the pools paired with any base denom in the store and then adds the highest liquidity pools that match to the store
func (k Keeper) UpdatePools(ctx sdk.Context) error {
// baseDenomPools maps each base denom to a map of the highest liquidity pools paired with that base denom
Expand Down
111 changes: 111 additions & 0 deletions x/protorev/keeper/epoch_hook_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import (
"strings"
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"

"github.com/osmosis-labs/osmosis/osmomath"
"github.com/osmosis-labs/osmosis/v24/app/apptesting"
"github.com/osmosis-labs/osmosis/v24/x/protorev/keeper"
"github.com/osmosis-labs/osmosis/v24/x/protorev/types"
)
Expand Down Expand Up @@ -190,3 +194,110 @@ func contains(baseDenoms []types.BaseDenom, denomToMatch string) bool {
}
return false
}

func (s *KeeperTestSuite) TestAfterEpochEnd() {
tests := []struct {
name string
arbProfits sdk.Coins
}{
{
name: "osmo denom only",
arbProfits: sdk.NewCoins(sdk.NewCoin("uosmo", osmomath.NewInt(100000000))),
},
{
name: "osmo denom and another base denom",
arbProfits: sdk.NewCoins(sdk.NewCoin("uosmo", osmomath.NewInt(100000000)),
sdk.NewCoin("juno", osmomath.NewInt(100000000))),
},
{
name: "osmo denom, another base denom, and a non base denom",
arbProfits: sdk.NewCoins(sdk.NewCoin("uosmo", osmomath.NewInt(100000000)),
sdk.NewCoin("juno", osmomath.NewInt(100000000)),
sdk.NewCoin("eth", osmomath.NewInt(100000000))),
},
{
name: "no profits",
arbProfits: sdk.Coins{},
},
}

for _, tc := range tests {
s.Run(tc.name, func() {
s.SetupPoolsTest()

// Set base denoms
baseDenoms := []types.BaseDenom{
{
Denom: types.OsmosisDenomination,
StepSize: osmomath.NewInt(1_000_000),
},
{
Denom: "juno",
StepSize: osmomath.NewInt(1_000_000),
},
}
s.App.ProtoRevKeeper.SetBaseDenoms(s.Ctx, baseDenoms)

// Set protorev developer account
devAccount := apptesting.CreateRandomAccounts(1)[0]
s.App.ProtoRevKeeper.SetDeveloperAccount(s.Ctx, devAccount)

err := s.App.BankKeeper.MintCoins(s.Ctx, types.ModuleName, tc.arbProfits)
s.Require().NoError(err)

communityPoolBalancePre := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(distrtypes.ModuleName))

// System under test
err = s.App.ProtoRevKeeper.EpochHooks().AfterEpochEnd(s.Ctx, "day", 1)

expectedDevProfit := sdk.Coins{}
expectedOsmoBurn := sdk.Coins{}
arbProfitsBaseDenoms := sdk.Coins{}
arbProfitsNonBaseDenoms := sdk.Coins{}

// Split the profits into base and non base denoms
for _, coin := range tc.arbProfits {
isBaseDenom := false
for _, baseDenom := range baseDenoms {
if coin.Denom == baseDenom.Denom {
isBaseDenom = true
break
}
}
if isBaseDenom {
arbProfitsBaseDenoms = append(arbProfitsBaseDenoms, coin)
} else {
arbProfitsNonBaseDenoms = append(arbProfitsNonBaseDenoms, coin)
}
}
profitSplit := types.ProfitSplitPhase1
for _, arbProfit := range arbProfitsBaseDenoms {
devProfitAmount := arbProfit.Amount.MulRaw(profitSplit).QuoRaw(100)
expectedDevProfit = append(expectedDevProfit, sdk.NewCoin(arbProfit.Denom, devProfitAmount))
}

// Get the developer account balance
devAccountBalance := s.App.BankKeeper.GetAllBalances(s.Ctx, devAccount)
s.Require().Equal(expectedDevProfit, devAccountBalance)

// Get the burn address balance
burnAddressBalance := s.App.BankKeeper.GetAllBalances(s.Ctx, types.DefaultNullAddress)
if arbProfitsBaseDenoms.AmountOf(types.OsmosisDenomination).IsPositive() {
expectedOsmoBurn = sdk.NewCoins(sdk.NewCoin(types.OsmosisDenomination, arbProfitsBaseDenoms.AmountOf(types.OsmosisDenomination).Sub(expectedDevProfit.AmountOf(types.OsmosisDenomination))))
s.Require().Equal(expectedOsmoBurn, burnAddressBalance)
} else {
s.Require().Equal(sdk.Coins{}, burnAddressBalance)
}

// Get the community pool balance
communityPoolBalancePost := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(distrtypes.ModuleName))
actualCommunityPool := communityPoolBalancePost.Sub(communityPoolBalancePre...)
expectedCommunityPool := arbProfitsBaseDenoms.Sub(expectedDevProfit...).Sub(expectedOsmoBurn...)
s.Require().Equal(expectedCommunityPool, actualCommunityPool)

// The protorev module account should only contain the non base denoms if there are any
protorevModuleAccount := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(types.ModuleName))
s.Require().Equal(arbProfitsNonBaseDenoms, protorevModuleAccount)
})
}
}
67 changes: 1 addition & 66 deletions x/protorev/keeper/hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,14 @@ import (
"github.com/osmosis-labs/osmosis/osmomath"
gammtypes "github.com/osmosis-labs/osmosis/v24/x/gamm/types"
"github.com/osmosis-labs/osmosis/v24/x/protorev/types"
epochstypes "github.com/osmosis-labs/osmosis/x/epochs/types"
)

type Hooks struct {
k Keeper
}

var (
_ gammtypes.GammHooks = Hooks{}
_ epochstypes.EpochHooks = Hooks{}
_ gammtypes.GammHooks = Hooks{}
)

// Create new ProtoRev hooks.
Expand Down Expand Up @@ -92,24 +90,6 @@ func (h Hooks) AfterConcentratedPoolSwap(ctx sdk.Context, sender sdk.AccAddress,
h.k.StoreSwap(ctx, poolId, input[0].Denom, output[0].Denom)
}

// ----------------------------------------------------------------------------
// EPOCH HOOKS
// ----------------------------------------------------------------------------

// Hooks wrapper struct for incentives keeper

func (h Hooks) BeforeEpochStart(ctx sdk.Context, epochIdentifier string, epochNumber int64) error {
return h.k.BeforeEpochStart(ctx, epochIdentifier, epochNumber)
}

func (h Hooks) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error {
return h.k.AfterEpochEnd(ctx, epochIdentifier, epochNumber)
}

func (h Hooks) GetModuleName() string {
return types.ModuleName
}

// ----------------------------------------------------------------------------
// HELPER METHODS
// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -241,48 +221,3 @@ func (k Keeper) CompareAndStorePool(ctx sdk.Context, poolId uint64, baseDenom, o
k.SetPoolForDenomPair(ctx, baseDenom, otherDenom, poolId)
}
}

func (k Keeper) BeforeEpochStart(ctx sdk.Context, epochIdentifier string, epochNumber int64) error {
return nil
}

func (k Keeper) AfterEpochEnd(ctx sdk.Context, epochIdentifier string, epochNumber int64) error {
// Get the current arb profits (only in base denoms to prevent spam vector)
profit, err := k.CurrentBaseDenomProfits(ctx)
if err != nil {
return err
}

// Distribute profits to developer account, community pool, and burn osmo
err = k.DistributeProfit(ctx, profit)
if err != nil {
return err
}

return nil
}

// CalculateCurrentProfit retrieves the current balance of the protorev module account and filters for base denoms.
func (k Keeper) CurrentBaseDenomProfits(ctx sdk.Context) (sdk.Coins, error) {
moduleAcc := k.accountKeeper.GetModuleAddress(types.ModuleName)

baseDenoms, err := k.GetAllBaseDenoms(ctx)
if err != nil {
return nil, err
}

// Get the current protorev balance of all denoms
protorevBalanceAllDenoms := k.bankKeeper.GetAllBalances(ctx, moduleAcc)

// Filter for base denoms
var protorevBalanceBaseDenoms sdk.Coins

for _, baseDenom := range baseDenoms {
amountOfBaseDenom := protorevBalanceAllDenoms.AmountOf(baseDenom.Denom)
if !amountOfBaseDenom.IsZero() {
protorevBalanceBaseDenoms = append(protorevBalanceBaseDenoms, sdk.NewCoin(baseDenom.Denom, amountOfBaseDenom))
}
}

return protorevBalanceBaseDenoms.Sort(), nil
}
Loading

0 comments on commit 7a420c8

Please sign in to comment.