Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: track and query protocol rev across all modules #6804

Merged
merged 21 commits into from
Nov 10, 2023
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Features

* [#6804](https://github.com/osmosis-labs/osmosis/pull/6804) feat: track and query protocol rev across all modules

### State Breaks

* [#6758](https://github.com/osmosis-labs/osmosis/pull/6758) Add codec for MsgUndelegateFromRebalancedValidatorSet
Expand Down
85 changes: 85 additions & 0 deletions app/apptesting/txfees.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package apptesting
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The methods in the file are extracted from previously existing txfees helpers because they need to be used in other module tests, and I wanted to prevent code duplication.


import (
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
"github.com/cosmos/cosmos-sdk/simapp"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
sdk "github.com/cosmos/cosmos-sdk/types"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"

"github.com/cosmos/cosmos-sdk/client"

clienttx "github.com/cosmos/cosmos-sdk/client/tx"

"github.com/osmosis-labs/osmosis/osmomath"
"github.com/osmosis-labs/osmosis/v20/x/txfees/keeper"
"github.com/osmosis-labs/osmosis/v20/x/txfees/types"
)

var baseGas = uint64(10000)

func (s *KeeperTestHelper) ExecuteUpgradeFeeTokenProposal(feeToken string, poolId uint64) error {
upgradeProp := types.NewUpdateFeeTokenProposal(
"Test Proposal",
"test",
[]types.FeeToken{
{
Denom: feeToken,
PoolID: poolId,
},
},
)
return s.App.TxFeesKeeper.HandleUpdateFeeTokenProposal(s.Ctx, &upgradeProp)
}

func (s *KeeperTestHelper) SetupTxFeeAnteHandlerAndChargeFee(clientCtx client.Context, minGasPrices sdk.DecCoins, gasRequested uint64, isCheckTx, isSimulate bool, txFee sdk.Coins) error {
mempoolFeeOpts := types.NewDefaultMempoolFeeOptions()
mempoolFeeOpts.MinGasPriceForHighGasTx = osmomath.MustNewDecFromStr("0.0025")

uionPoolId := s.PrepareBalancerPoolWithCoins(
sdk.NewInt64Coin(sdk.DefaultBondDenom, 500),
sdk.NewInt64Coin("uion", 500),
)
err := s.ExecuteUpgradeFeeTokenProposal("uion", uionPoolId)
s.Require().NoError(err)

if gasRequested == 0 {
gasRequested = baseGas
}
s.Ctx = s.Ctx.WithIsCheckTx(isCheckTx).WithMinGasPrices(minGasPrices)

// TODO: Cleanup this code.
// TxBuilder components reset for every test case
txBuilder := clientCtx.TxConfig.NewTxBuilder()
priv0, _, addr0 := testdata.KeyTestPubAddr()
acc1 := s.App.AccountKeeper.NewAccountWithAddress(s.Ctx, addr0)
s.App.AccountKeeper.SetAccount(s.Ctx, acc1)
msgs := []sdk.Msg{testdata.NewTestMsg(addr0)}
privs, accNums, accSeqs := []cryptotypes.PrivKey{priv0}, []uint64{0}, []uint64{0}
signerData := authsigning.SignerData{
ChainID: s.Ctx.ChainID(),
AccountNumber: accNums[0],
Sequence: accSeqs[0],
}

gasLimit := gasRequested
sigV2, _ := clienttx.SignWithPrivKey(
1,
signerData,
txBuilder,
privs[0],
clientCtx.TxConfig,
accSeqs[0],
)

err = simapp.FundAccount(s.App.BankKeeper, s.Ctx, addr0, txFee)
s.Require().NoError(err)

tx := s.BuildTx(txBuilder, msgs, sigV2, "", txFee, gasLimit)

mfd := keeper.NewMempoolFeeDecorator(*s.App.TxFeesKeeper, mempoolFeeOpts)
dfd := keeper.NewDeductFeeDecorator(*s.App.TxFeesKeeper, *s.App.AccountKeeper, *s.App.BankKeeper, nil)
antehandlerMFD := sdk.ChainAnteDecorators(mfd, dfd)
_, err = antehandlerMFD(s.Ctx, tx, isSimulate)
return err
}
2 changes: 2 additions & 0 deletions app/keepers/keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.EpochsKeeper,
appKeepers.PoolManagerKeeper,
appKeepers.ConcentratedLiquidityKeeper,
appKeepers.TxFeesKeeper,
)
appKeepers.ProtoRevKeeper = &protorevKeeper
appKeepers.PoolManagerKeeper.SetProtorevKeeper(appKeepers.ProtoRevKeeper)
Expand All @@ -383,6 +384,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers(
appKeepers.DistrKeeper,
)
appKeepers.TxFeesKeeper = &txFeesKeeper
appKeepers.ProtoRevKeeper.SetTxFeesKeeper(appKeepers.TxFeesKeeper)

appKeepers.IncentivesKeeper = incentiveskeeper.NewKeeper(
appKeepers.keys[incentivestypes.StoreKey],
Expand Down
12 changes: 12 additions & 0 deletions app/upgrades/v21/upgrades.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"github.com/cosmos/cosmos-sdk/types/module"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

"github.com/osmosis-labs/osmosis/osmoutils"
"github.com/osmosis-labs/osmosis/v20/app/keepers"
"github.com/osmosis-labs/osmosis/v20/app/upgrades"
)
Expand All @@ -23,6 +24,17 @@ func CreateUpgradeHandler(
return nil, err
}

// Since we are now tracking all protocol rev, we set the accounting height to the current block height for each module
// that generates protocol rev.
keepers.PoolManagerKeeper.SetTakerFeeTrackerStartHeight(ctx, ctx.BlockHeight())
keepers.TxFeesKeeper.SetTxFeesTrackerStartHeight(ctx, ctx.BlockHeight())
// We start the cyclic arb tracker from the value it currently is at since it has been tracking since inception (without a start height).
// This will allow us to display the amount of cyclic arb profits that have been generated from a certain block height.
allCyclicArbProfits := keepers.ProtoRevKeeper.GetAllProfits(ctx)
allCyclicArbProfitsCoins := osmoutils.ConvertCoinArrayToCoins(allCyclicArbProfits)
keepers.ProtoRevKeeper.SetCyclicArbProfitTrackerValue(ctx, allCyclicArbProfitsCoins)
keepers.ProtoRevKeeper.SetCyclicArbProfitTrackerStartHeight(ctx, ctx.BlockHeight())

return migrations, nil
}
}
69 changes: 69 additions & 0 deletions app/upgrades/v21/upgrades_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package v21_test

import (
"testing"

"github.com/stretchr/testify/suite"

upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"

abci "github.com/tendermint/tendermint/abci/types"

"github.com/osmosis-labs/osmosis/osmomath"
"github.com/osmosis-labs/osmosis/v20/app/apptesting"
"github.com/osmosis-labs/osmosis/v20/x/protorev/types"

sdk "github.com/cosmos/cosmos-sdk/types"

poolmanagertypes "github.com/osmosis-labs/osmosis/v20/x/poolmanager/types"
)

const (
v21UpgradeHeight = int64(10)
)

type UpgradeTestSuite struct {
apptesting.KeeperTestHelper
}

func TestUpgradeTestSuite(t *testing.T) {
suite.Run(t, new(UpgradeTestSuite))
}

func (s *UpgradeTestSuite) TestUpgrade() {
s.Setup()
dummyUpgrade(s)
s.Require().NotPanics(func() {
s.App.BeginBlocker(s.Ctx, abci.RequestBeginBlock{})
})

// Psuedo collect cyclic arb profits
cyclicArbProfits := sdk.NewCoins(sdk.NewCoin(types.OsmosisDenomination, osmomath.NewInt(9000)), sdk.NewCoin("Atom", osmomath.NewInt(3000)))
err := s.App.AppKeepers.ProtoRevKeeper.UpdateStatistics(s.Ctx, poolmanagertypes.SwapAmountInRoutes{}, cyclicArbProfits[0].Denom, cyclicArbProfits[0].Amount)
s.Require().NoError(err)
err = s.App.AppKeepers.ProtoRevKeeper.UpdateStatistics(s.Ctx, poolmanagertypes.SwapAmountInRoutes{}, cyclicArbProfits[1].Denom, cyclicArbProfits[1].Amount)
s.Require().NoError(err)

allProtocolRevenue := s.App.ProtoRevKeeper.GetAllProtocolRevenue(s.Ctx)
// Check all accounting start heights should be the same height as the upgrade
s.Require().Equal(v21UpgradeHeight, allProtocolRevenue.CyclicArbTracker.HeightAccountingStartsFrom)
s.Require().Equal(v21UpgradeHeight, allProtocolRevenue.TakerFeesTracker.HeightAccountingStartsFrom)
s.Require().Equal(v21UpgradeHeight, allProtocolRevenue.TxFeesTracker.HeightAccountingStartsFrom)
// All values should be nill except for the cyclic arb profits, which should start at the value it was at time of upgrade
s.Require().Equal(sdk.Coins(nil), allProtocolRevenue.TakerFeesTracker.TakerFeesToCommunityPool)
s.Require().Equal(sdk.Coins(nil), allProtocolRevenue.TakerFeesTracker.TakerFeesToStakers)
s.Require().Equal(sdk.Coins(nil), allProtocolRevenue.TxFeesTracker.TxFees)
s.Require().Equal(cyclicArbProfits, allProtocolRevenue.CyclicArbTracker.CyclicArb)

}

func dummyUpgrade(s *UpgradeTestSuite) {
s.Ctx = s.Ctx.WithBlockHeight(v21UpgradeHeight - 1)
plan := upgradetypes.Plan{Name: "v21", Height: v21UpgradeHeight}
err := s.App.UpgradeKeeper.ScheduleUpgrade(s.Ctx, plan)
s.Require().NoError(err)
_, exists := s.App.UpgradeKeeper.GetUpgradePlan(s.Ctx)
s.Require().True(exists)

s.Ctx = s.Ctx.WithBlockHeight(v21UpgradeHeight)
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ require (
github.com/ory/dockertest/v3 v3.10.0
github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3
github.com/osmosis-labs/osmosis/osmomath v0.0.7-0.20231014001935-1946419d44eb
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231017074304-84e27b5e2aad
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231101190541-d45713e67d41
github.com/osmosis-labs/osmosis/x/epochs v0.0.3-0.20231011004221-fd24b80f8366
github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.9-0.20231014001935-1946419d44eb
github.com/pkg/errors v0.9.1
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -966,8 +966,8 @@ github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3 h1:Ylmch
github.com/osmosis-labs/go-mutesting v0.0.0-20221208041716-b43bcd97b3b3/go.mod h1:lV6KnqXYD/ayTe7310MHtM3I2q8Z6bBfMAi+bhwPYtI=
github.com/osmosis-labs/osmosis/osmomath v0.0.7-0.20231014001935-1946419d44eb h1:pXsC6vqGD+pbMGt+fVBHi9XBk/KDQuRZde2fh4s/1+k=
github.com/osmosis-labs/osmosis/osmomath v0.0.7-0.20231014001935-1946419d44eb/go.mod h1:jNZ952fypVNMzOsh31LAUS27JbF9naNJGtELxId6ZCg=
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231017074304-84e27b5e2aad h1:UcQ/XLz0SqWMrA+BhgDXy9ukD4C+FlN4ULdazZmFOsE=
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231017074304-84e27b5e2aad/go.mod h1:16AXMzbTLkYE5If5VLTA07fV9JNcLFwgf/VoW5sHrtU=
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231101190541-d45713e67d41 h1:XrXYbkR3i8P8dlWRsmd+16qzfky2yCGdIAeg13JcpyE=
github.com/osmosis-labs/osmosis/osmoutils v0.0.7-0.20231101190541-d45713e67d41/go.mod h1:16AXMzbTLkYE5If5VLTA07fV9JNcLFwgf/VoW5sHrtU=
github.com/osmosis-labs/osmosis/x/epochs v0.0.3-0.20231011004221-fd24b80f8366 h1:E6H0V3MKbSNwo1iXE9Kzatd2M02MgZpS5AiJ6CKK5us=
github.com/osmosis-labs/osmosis/x/epochs v0.0.3-0.20231011004221-fd24b80f8366/go.mod h1:vU0IHK5W38dqMeux3MkSaT3MZU6whAkx7vNuxv1IzeU=
github.com/osmosis-labs/osmosis/x/ibc-hooks v0.0.9-0.20231014001935-1946419d44eb h1:6lYLEiJERdD+QK925XYyHkvNyvQTghVFufMH5VAQLpg=
Expand Down
8 changes: 8 additions & 0 deletions osmoutils/coin_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,11 @@ func MergeCoinMaps[T comparable](currentEpochExpectedDistributionsOne map[T]sdk.
}
return newMap
}

func ConvertCoinArrayToCoins(coinArray []sdk.Coin) sdk.Coins {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While coin arrays can be used in place of a coins object and still be valid, coin array do not posses the same sub methods (ex. Sub, Add, etc), so this method is useful imo.

coins := sdk.Coins{}
for _, coin := range coinArray {
coins = append(coins, coin)
}
return coins
}
32 changes: 32 additions & 0 deletions osmoutils/coin_helper_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,3 +291,35 @@ func TestMergeCoinMaps(t *testing.T) {
}
})
}

func TestConvertCoinArrayToCoins(t *testing.T) {
tests := []struct {
name string
coinArray []sdk.Coin
expectedCoins sdk.Coins
}{
{
name: "Empty input",
coinArray: []sdk.Coin{},
expectedCoins: sdk.NewCoins(),
},
{
name: "Single coin",
coinArray: []sdk.Coin{sdk.NewCoin("atom", osmomath.NewInt(100000000))},
expectedCoins: sdk.NewCoins(sdk.NewCoin("atom", osmomath.NewInt(100000000))),
},
{
name: "Multiple coins",
coinArray: []sdk.Coin{sdk.NewCoin("atom", osmomath.NewInt(100000000)), sdk.NewCoin("usdc", osmomath.NewInt(500000000))},
expectedCoins: sdk.NewCoins(sdk.NewCoin("atom", osmomath.NewInt(100000000)), sdk.NewCoin("usdc", osmomath.NewInt(500000000))),
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
result := osmoutils.ConvertCoinArrayToCoins(test.coinArray)
require.Equal(t, result, test.expectedCoins)

})
}
}
16 changes: 16 additions & 0 deletions proto/osmosis/poolmanager/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ message GenesisState {
Params params = 2 [ (gogoproto.nullable) = false ];
// pool_routes is the container of the mappings from pool id to pool type.
repeated ModuleRoute pool_routes = 3 [ (gogoproto.nullable) = false ];

// KVStore state
TakerFeesTracker taker_fees_tracker = 4;
}

// TakerFeeParams consolidates the taker fee parameters for the poolmanager.
Expand Down Expand Up @@ -117,3 +120,16 @@ message TakerFeeDistributionPercentage {
(gogoproto.nullable) = false
];
}

message TakerFeesTracker {
repeated cosmos.base.v1beta1.Coin taker_fees_to_stakers = 1 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
repeated cosmos.base.v1beta1.Coin taker_fees_to_community_pool = 2 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
int64 height_accounting_starts_from = 3
[ (gogoproto.moretags) = "yaml:\"height_accounting_starts_from\"" ];
}
4 changes: 3 additions & 1 deletion proto/osmosis/protorev/v1beta1/genesis.proto
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,6 @@ message GenesisState {
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"info_by_pool_type\""
];
}
CyclicArbTracker cyclic_arb_tracker = 14
[ (gogoproto.moretags) = "yaml:\"cyclic_arb_tracker\"" ];
}
28 changes: 27 additions & 1 deletion proto/osmosis/protorev/v1beta1/protorev.proto
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import "cosmos_proto/cosmos.proto";
import "gogoproto/gogo.proto";

import "cosmos/base/v1beta1/coin.proto";
import "osmosis/poolmanager/v1beta1/genesis.proto";
import "osmosis/txfees/v1beta1/genesis.proto";

option go_package = "github.com/osmosis-labs/osmosis/v20/x/protorev/types";

Expand Down Expand Up @@ -179,4 +181,28 @@ message BaseDenom {
(gogoproto.nullable) = false,
(gogoproto.moretags) = "yaml:\"step_size\""
];
}
}

message AllProtocolRevenue {
osmosis.poolmanager.v1beta1.TakerFeesTracker taker_fees_tracker = 1 [
(gogoproto.moretags) = "yaml:\"taker_fees_tracker\"",
(gogoproto.nullable) = false
];
osmosis.txfees.v1beta1.TxFeesTracker tx_fees_tracker = 2 [
(gogoproto.moretags) = "yaml:\"tx_fees_tracker\"",
(gogoproto.nullable) = false
];
CyclicArbTracker cyclic_arb_tracker = 3 [
(gogoproto.moretags) = "yaml:\"cyclic_arb_tracker\"",
(gogoproto.nullable) = false
];
}

message CyclicArbTracker {
repeated cosmos.base.v1beta1.Coin cyclic_arb = 1 [
(gogoproto.nullable) = false,
(gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"
];
int64 height_accounting_starts_from = 2
[ (gogoproto.moretags) = "yaml:\"height_accounting_starts_from\"" ];
}
16 changes: 16 additions & 0 deletions proto/osmosis/protorev/v1beta1/query.proto
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,13 @@ service Query {
returns (QueryGetProtoRevPoolResponse) {
option (google.api.http).get = "/osmosis/protorev/pool";
}

// GetAllProtocolRevenue queries all of the protocol revenue that has been
// accumulated by any module
rpc GetAllProtocolRevenue(QueryGetAllProtocolRevenueRequest)
returns (QueryGetAllProtocolRevenueResponse) {
option (google.api.http).get = "/osmosis/protorev/all_protocol_revenue";
}
}

// QueryParamsRequest is request type for the Query/Params RPC method.
Expand Down Expand Up @@ -324,4 +331,13 @@ message QueryGetProtoRevPoolRequest {
message QueryGetProtoRevPoolResponse {
// pool_id is the pool_id stored for the denom pair
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
}

message QueryGetAllProtocolRevenueRequest {}

message QueryGetAllProtocolRevenueResponse {
AllProtocolRevenue all_protocol_revenue = 1 [
(gogoproto.moretags) = "yaml:\"all_protocol_revenue\"",
(gogoproto.nullable) = false
];
}
Loading