Skip to content

Commit

Permalink
feat: Add eligible keys filter to reward transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
ze97286 committed Aug 29, 2024
1 parent f1e6e81 commit e271012
Show file tree
Hide file tree
Showing 12 changed files with 1,508 additions and 1,403 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
- [11533](https://github.com/vegaprotocol/vega/issues/11533) - Suppose per party fee discounts in fee estimation.
- [11577](https://github.com/vegaprotocol/vega/issues/11577) - Add API for party discounts and rewards.
- [10716](https://github.com/vegaprotocol/vega/issues/10716) - Set Tendermint defaults during init.
-[11624](https://github.com/vegaprotocol/vega/issues/11624) - prevent creation of rewards with no payout, but with high computational cost.
- [11624](https://github.com/vegaprotocol/vega/issues/11624) - prevent creation of rewards with no payout, but with high computational cost.
- [11627](https://github.com/vegaprotocol/vega/issues/11627) - Add eligible keys filter to reward transfers.

### 🐛 Fixes

Expand Down
2 changes: 1 addition & 1 deletion core/banking/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ type Topology interface {
type MarketActivityTracker interface {
CalculateMetricForIndividuals(ctx context.Context, ds *vega.DispatchStrategy) []*types.PartyContributionScore
CalculateMetricForTeams(ctx context.Context, ds *vega.DispatchStrategy) ([]*types.PartyContributionScore, map[string][]*types.PartyContributionScore)
GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string) []*types.MarketContributionScore
GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string, eligibleKeys []string) []*types.MarketContributionScore
MarkPaidProposer(asset, market, payoutAsset string, marketsInScope []string, funder string)
MarketTrackedForAsset(market, asset string) bool
TeamStatsForMarkets(allMarketsForAssets, onlyTheseMarkets []string) map[string]map[string]*num.Uint
Expand Down
2 changes: 1 addition & 1 deletion core/banking/gov_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ func (e *Engine) processGovernanceTransfer(
ds := gTransfer.Config.RecurringTransferConfig.DispatchStrategy
// if the metric is market value we make the transfer to the market account (as opposed to the metric's hash account)
if ds.Metric == vegapb.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE {
marketProposersScore := e.marketActivityTracker.GetMarketsWithEligibleProposer(ds.AssetForMetric, ds.Markets, gTransfer.Config.Asset, gTransfer.Config.Source)
marketProposersScore := e.marketActivityTracker.GetMarketsWithEligibleProposer(ds.AssetForMetric, ds.Markets, gTransfer.Config.Asset, gTransfer.Config.Source, ds.EligibleKeys)
for _, fms := range marketProposersScore {
amt, _ := num.UintFromDecimal(transferAmount.ToDecimal().Mul(fms.Score))
if amt.IsZero() {
Expand Down
8 changes: 4 additions & 4 deletions core/banking/mocks/mocks.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion core/banking/recurring_transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ func (e *Engine) distributeRecurringTransfers(ctx context.Context, newEpoch uint
// different to how all other metric based rewards behave. The reason is that we need the context of the funder
// and this context is lost when the transfer has already gone through
if v.DispatchStrategy.Metric == vegapb.DispatchMetric_DISPATCH_METRIC_MARKET_VALUE {
marketProposersScore := e.marketActivityTracker.GetMarketsWithEligibleProposer(v.DispatchStrategy.AssetForMetric, v.DispatchStrategy.Markets, v.Asset, v.From)
marketProposersScore := e.marketActivityTracker.GetMarketsWithEligibleProposer(v.DispatchStrategy.AssetForMetric, v.DispatchStrategy.Markets, v.Asset, v.From, v.DispatchStrategy.EligibleKeys)
for _, fms := range marketProposersScore {
amt, _ := num.UintFromDecimal(amount.ToDecimal().Mul(fms.Score))
if amt.IsZero() {
Expand Down
25 changes: 23 additions & 2 deletions core/execution/common/market_activity_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,12 @@ func (mat *MarketActivityTracker) RemoveAMMParty(asset, marketID, ammParty strin

// GetMarketsWithEligibleProposer gets all the markets within the given asset (or just all the markets in scope passed as a parameter) that
// are eligible for proposer bonus.
func (mat *MarketActivityTracker) GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string) []*types.MarketContributionScore {
func (mat *MarketActivityTracker) GetMarketsWithEligibleProposer(asset string, markets []string, payoutAsset string, funder string, eligibleKeys []string) []*types.MarketContributionScore {
eligibleKeySet := make(map[string]struct{}, len(eligibleKeys))
for _, ek := range eligibleKeys {
eligibleKeySet[ek] = struct{}{}
}

var mkts []string
if len(markets) > 0 {
mkts = markets
Expand Down Expand Up @@ -377,7 +382,10 @@ func (mat *MarketActivityTracker) GetMarketsWithEligibleProposer(asset string, m
for _, a := range assets {
for _, v := range mkts {
if t, ok := mat.getMarketTracker(a, v); ok && (len(asset) == 0 || t.asset == asset) && mat.IsMarketEligibleForBonus(a, v, payoutAsset, markets, funder) {
eligibleMarkets = append(eligibleMarkets, v)
proposer := mat.GetProposer(v)
if _, ok := eligibleKeySet[proposer]; len(eligibleKeySet) == 0 || ok {
eligibleMarkets = append(eligibleMarkets, v)
}
}
}
}
Expand Down Expand Up @@ -844,6 +852,19 @@ func (mat *MarketActivityTracker) getPartiesInScope(ds *vega.DispatchStrategy) [
} else if ds.IndividualScope == vega.IndividualScope_INDIVIDUAL_SCOPE_AMM {
parties = sortedK(mat.GetAllAMMParties(ds.AssetForMetric, ds.Markets))
}
if len(ds.EligibleKeys) > 0 {
eligibleParties := make([]string, 0, len(parties))
ep := make(map[string]struct{}, len(ds.EligibleKeys))
for _, ek := range ds.EligibleKeys {
ep[ek] = struct{}{}
}
for _, pp := range parties {
if _, ok := ep[pp]; ok {
eligibleParties = append(eligibleParties, pp)
}
}
parties = eligibleParties
}
return parties
}

Expand Down
36 changes: 21 additions & 15 deletions core/execution/common/market_activity_tracker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1185,12 +1185,14 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarketProposed("asset2", "market6", "me2")

// no trading done so far so expect no one to be eligible for bonus
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset2", []string{}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset2", []string{}, "VEGA", "zohar", []string{})))

// market1 goes above the threshold only it should be eligible
tracker.AddValueTraded("asset1", "market1", num.NewUint(5001))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar")))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me"})))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"not me"})))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
Expand All @@ -1200,7 +1202,11 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
// now market 2 and 3 become eligible
tracker.AddValueTraded("asset1", "market2", num.NewUint(5001))
tracker.AddValueTraded("asset1", "market3", num.NewUint(5001))
require.Equal(t, 2, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar")))
require.Equal(t, 2, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
require.Equal(t, 2, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me", "me2"})))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me", "not me"})))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"me2", "not me"})))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{"not me"})))

// show that only markets 2 and 3 are now eligible with this combo
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
Expand All @@ -1212,14 +1218,14 @@ func TestMarketProposerBonusScenarios(t *testing.T) {

// now market4 goes above the threshold but no one gets paid by this combo
tracker.AddValueTraded("asset1", "market4", num.NewUint(5001))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market2", "market3"}, "VEGA", "zohar", []string{})))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market4", "VEGA", []string{"market1", "market2", "market3"}, "zohar"))

// now "all" is funded by zohar
require.Equal(t, 4, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar")))
require.Equal(t, 4, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "zohar"))
Expand All @@ -1231,14 +1237,14 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarkPaidProposer("asset1", "market4", "VEGA", []string{}, "zohar")

// everyone were paid so next time no one is eligible
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))

// a new market is proposed and gets over the limit
tracker.MarketProposed("asset1", "market7", "mememe")
tracker.AddValueTraded("asset1", "market7", num.NewUint(5001))

// only the new market should be eligible for the "all" combo funded by zohar
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar")))
require.Equal(t, 1, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "zohar"))
Expand All @@ -1247,10 +1253,10 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{}, "zohar")

// check that they are no longer eligible for this combo of all
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "zohar", []string{})))

// check new combo
require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar")))
require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar", []string{})))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{"market1", "market3", "market7"}, "zohar"))
Expand All @@ -1262,10 +1268,10 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{"market1", "market3", "market7"}, "zohar")

// now that they're marked as paid check they're no longer eligible
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "VEGA", "zohar", []string{})))

// check new asset for the same combo
require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar")))
require.Equal(t, 3, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar", []string{})))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
require.False(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "USDC", []string{"market1", "market3", "market7"}, "zohar"))
Expand All @@ -1277,10 +1283,10 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarkPaidProposer("asset1", "market7", "USDC", []string{"market1", "market3", "market7"}, "zohar")

// now that they're marked as paid check they're no longer eligible
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{"market1", "market3", "market7"}, "USDC", "zohar", []string{})))

// check new funder for the all combo
require.Equal(t, 5, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy")))
require.Equal(t, 5, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy", []string{})))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market1", "VEGA", []string{}, "jeremy"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market2", "VEGA", []string{}, "jeremy"))
require.True(t, tracker.IsMarketEligibleForBonus("asset1", "market3", "VEGA", []string{}, "jeremy"))
Expand All @@ -1292,7 +1298,7 @@ func TestMarketProposerBonusScenarios(t *testing.T) {
tracker.MarkPaidProposer("asset1", "market3", "VEGA", []string{}, "jeremy")
tracker.MarkPaidProposer("asset1", "market4", "VEGA", []string{}, "jeremy")
tracker.MarkPaidProposer("asset1", "market7", "VEGA", []string{}, "jeremy")
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy")))
require.Equal(t, 0, len(tracker.GetMarketsWithEligibleProposer("asset1", []string{}, "VEGA", "jeremy", []string{})))
}

func TestNotionalMetric(t *testing.T) {
Expand Down
8 changes: 7 additions & 1 deletion core/integration/steps/transfers.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ func PartiesSubmitRecurringTransfers(
func parseRecurringTransferTable(table *godog.Table) []RowWrapper {
return StrictParseTable(table, []string{
"id", "from", "from_account_type", "to", "to_account_type", "asset", "amount", "start_epoch", "end_epoch", "factor",
}, []string{"metric", "metric_asset", "markets", "lock_period", "window_length", "entity_scope", "individual_scope", "teams", "ntop", "staking_requirement", "notional_requirement", "distribution_strategy", "ranks", "cap_reward_fee_multiple", "transfer_interval", "error"})
}, []string{"metric", "metric_asset", "markets", "lock_period", "window_length", "entity_scope", "individual_scope", "teams", "ntop", "staking_requirement", "notional_requirement", "distribution_strategy", "ranks", "cap_reward_fee_multiple", "transfer_interval", "eligible_keys", "error"})
}

func rowToRecurringTransfer(r RowWrapper) *types.RecurringTransfer {
Expand Down Expand Up @@ -198,6 +198,11 @@ func rowToRecurringTransfer(r RowWrapper) *types.RecurringTransfer {
transferInterval = &interval
}

var eligibleKeys []string
if r.HasColumn("eligible_keys") {
eligibleKeys = r.StrSlice("eligible_keys", ",")
}

distributionStrategy := proto.DistributionStrategy_DISTRIBUTION_STRATEGY_PRO_RATA
var ranks []*proto.Rank
if r.HasColumn("distribution_strategy") {
Expand Down Expand Up @@ -288,6 +293,7 @@ func rowToRecurringTransfer(r RowWrapper) *types.RecurringTransfer {
NotionalTimeWeightedAveragePositionRequirement: notionalRequirement,
RankTable: ranks,
TransferInterval: transferInterval,
EligibleKeys: eligibleKeys,
}
if capRewardFeeMultiple != "" {
dispatchStrategy.CapRewardFeeMultiple = &capRewardFeeMultiple
Expand Down
Loading

0 comments on commit e271012

Please sign in to comment.