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

[CL Incentives] Implement CreateIncentive logic and message #4519

Merged
merged 11 commits into from
Mar 10, 2023
2,037 changes: 2,037 additions & 0 deletions go.work.sum

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions proto/osmosis/concentrated-liquidity/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,57 @@ message MsgCollectIncentivesResponse {
(gogoproto.moretags) = "yaml:\"collected_incentives\"",
(gogoproto.nullable) = false
];
}

// ===================== MsgCreateIncentive
message MsgCreateIncentive {
uint64 pool_id = 1 [ (gogoproto.moretags) = "yaml:\"pool_id\"" ];
string sender = 2 [ (gogoproto.moretags) = "yaml:\"sender\"" ];
string incentive_denom = 3;
string incentive_amount = 4 [
Comment on lines +141 to +142
Copy link
Contributor

@stackman27 stackman27 Mar 6, 2023

Choose a reason for hiding this comment

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

just a thought, why not set this as Coin instead of denom and amount? so that later down the line we can create incentives record for many Coins

Copy link
Member

Choose a reason for hiding this comment

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

Agreed on making this a Coin, unless it was by design for a reason I am not seeing.

(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
(gogoproto.moretags) = "yaml:\"incentive_amount\"",
(gogoproto.nullable) = false
];
string emission_rate = 5 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"emission_rate\"",
(gogoproto.nullable) = false
];
google.protobuf.Timestamp start_time = 6 [
(gogoproto.nullable) = false,
(gogoproto.stdtime) = true,
(gogoproto.moretags) = "yaml:\"start_time\""
];
google.protobuf.Duration min_uptime = 7 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.jsontag) = "duration,omitempty",
(gogoproto.moretags) = "yaml:\"min_uptime\""
];
}

message MsgCreateIncentiveResponse {
string incentive_denom = 1;
string incentive_amount = 2 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"incentive_amount\"",
(gogoproto.nullable) = false
];
Comment on lines +166 to +171
Copy link
Member

Choose a reason for hiding this comment

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

And this

string emission_rate = 3 [
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
(gogoproto.moretags) = "yaml:\"emission_rate\"",
(gogoproto.nullable) = false
];
google.protobuf.Timestamp start_time = 4 [
(gogoproto.nullable) = false,
(gogoproto.stdtime) = true,
(gogoproto.moretags) = "yaml:\"start_time\""
];
google.protobuf.Duration min_uptime = 5 [
(gogoproto.nullable) = false,
(gogoproto.stdduration) = true,
(gogoproto.jsontag) = "duration,omitempty",
(gogoproto.moretags) = "yaml:\"min_uptime\""
];
}
11 changes: 11 additions & 0 deletions x/concentrated-liquidity/client/cli/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ func NewTxCmd() *cobra.Command {
osmocli.AddTxCmd(txCmd, NewCreateConcentratedPoolCmd)
osmocli.AddTxCmd(txCmd, NewCollectFeesCmd)
osmocli.AddTxCmd(txCmd, NewCollectIncentivesCmd)
osmocli.AddTxCmd(txCmd, NewCreateIncentiveCmd)
return txCmd
}

Expand Down Expand Up @@ -71,3 +72,13 @@ func NewCollectIncentivesCmd() (*osmocli.TxCliDesc, *types.MsgCollectIncentives)
Flags: osmocli.FlagDesc{RequiredFlags: []*flag.FlagSet{FlagSetJustPoolId()}},
}, &types.MsgCollectIncentives{}
}

func NewCreateIncentiveCmd() (*osmocli.TxCliDesc, *types.MsgCreateIncentive) {
return &osmocli.TxCliDesc{
Use: "create-incentive [incentive-denom] [incentive-amount] [emission-rate] [start-time] [min-uptime]",
Short: "create an incentive record to emit incentives (per second) to a given pool",
Example: "create-incentive uosmo 69082 0.02 100 2023-03-03 03:20:35.419543805 24h --pool-id 1 --from val --chain-id osmosis-1",
CustomFlagOverrides: poolIdFlagOverride,
Flags: osmocli.FlagDesc{RequiredFlags: []*flag.FlagSet{FlagSetJustPoolId()}},
}, &types.MsgCreateIncentive{}
Comment on lines +82 to +83
Copy link
Member

Choose a reason for hiding this comment

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

Not blocking, but we have been trying to move away from mandatory flags, would be nice if we could just make poolId be part of the create incentive message

}
4 changes: 4 additions & 0 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,7 @@ func (k Keeper) CollectIncentives(ctx sdk.Context, poolId uint64, owner sdk.AccA
func GetUptimeTrackerValues(uptimeTrackers []model.UptimeTracker) []sdk.DecCoins {
return getUptimeTrackerValues(uptimeTrackers)
}

func (k Keeper) CreateIncentive(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, incentiveDenom string, incentiveAmount sdk.Int, emissionRate sdk.Dec, startTime time.Time, minUptime time.Duration) (types.IncentiveRecord, error) {
return k.createIncentive(ctx, poolId, sender, incentiveDenom, incentiveAmount, emissionRate, startTime, minUptime)
}
67 changes: 67 additions & 0 deletions x/concentrated-liquidity/incentives.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,3 +501,70 @@ func (k Keeper) collectIncentives(ctx sdk.Context, poolId uint64, owner sdk.AccA
}
return collectedIncentives, nil
}

// createIncentive creates an incentive record in state for the given pool
func (k Keeper) createIncentive(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, incentiveDenom string, incentiveAmount sdk.Int, emissionRate sdk.Dec, startTime time.Time, minUptime time.Duration) (types.IncentiveRecord, error) {
pool, err := k.getPoolById(ctx, poolId)
if err != nil {
return types.IncentiveRecord{}, err
}

// Ensure incentive amount is nonzero and nonnegative
if !incentiveAmount.IsPositive() {
return types.IncentiveRecord{}, types.NonPositiveIncentiveAmountError{PoolId: poolId, IncentiveAmount: incentiveAmount.ToDec()}
}

// Ensure start time is >= current blocktime
if startTime.Before(ctx.BlockTime()) {
return types.IncentiveRecord{}, types.StartTimeTooEarlyError{PoolId: poolId, CurrentBlockTime: ctx.BlockTime(), StartTime: startTime}
}

// Ensure emission rate is nonzero and nonnegative
if !emissionRate.IsPositive() {
return types.IncentiveRecord{}, types.NonPositiveEmissionRateError{PoolId: poolId, EmissionRate: emissionRate}
}

// Ensure min uptime is one of the supported periods
validUptime := false
for _, supportedUptime := range types.SupportedUptimes {
if minUptime == supportedUptime {
validUptime = true
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
validUptime = true
validUptime = true
break

}
}
if !validUptime {
return types.IncentiveRecord{}, types.InvalidMinUptimeError{PoolId: poolId, MinUptime: minUptime, SupportedUptimes: types.SupportedUptimes}
}

// Ensure sender has balance for incentive denom
incentiveCoin := sdk.NewCoin(incentiveDenom, incentiveAmount)
Copy link
Member

Choose a reason for hiding this comment

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

related to earlier comments - I think having it as Coin from the start makes more sense to me as well

senderHasBalance := k.bankKeeper.HasBalance(ctx, sender, incentiveCoin)
if !senderHasBalance {
return types.IncentiveRecord{}, types.IncentiveInsufficientBalanceError{PoolId: poolId, IncentiveDenom: incentiveDenom, IncentiveAmount: incentiveAmount}
}

// Sync global uptime accumulators to current blocktime to ensure consistency in reward emissions
err = k.updateUptimeAccumulatorsToNow(ctx, poolId)
if err != nil {
return types.IncentiveRecord{}, err
}

// Set up incentive record to put in state
incentiveRecord := types.IncentiveRecord{
PoolId: poolId,
IncentiveDenom: incentiveDenom,
RemainingAmount: incentiveAmount.ToDec(),
EmissionRate: emissionRate,
StartTime: startTime,
MinUptime: minUptime,
}

// Set incentive record in state
k.setIncentiveRecord(ctx, incentiveRecord)

// Transfer tokens from sender to pool balance
if err := k.bankKeeper.SendCoins(ctx, sender, pool.GetAddress(), sdk.NewCoins(incentiveCoin)); err != nil {
return types.IncentiveRecord{}, err
}

return incentiveRecord, nil
}
Loading