Skip to content

Commit

Permalink
Merge pull request #10205 from vegaprotocol/feat-10195
Browse files Browse the repository at this point in the history
Fix transfer fee discount
  • Loading branch information
karlem authored Dec 4, 2023
2 parents b4f2ccf + 7d1aeef commit b112c0f
Show file tree
Hide file tree
Showing 9 changed files with 708 additions and 832 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
- [10158](https://github.com/vegaprotocol/vega/issues/10158) - Add the network as the zero-share default party in settlement engine.
- [10183](https://github.com/vegaprotocol/vega/issues/10183) - Fix transfer fees registration and decay.
- [9840](https://github.com/vegaprotocol/vega/issues/9840) - Team API inconsistency in joined at timestamps.
- [10205](https://github.com/vegaprotocol/vega/issues/10205) - Fix for transfer discount fees.

## 0.73.0

Expand Down
45 changes: 23 additions & 22 deletions core/banking/fee_discount.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,19 +77,24 @@ func (e *Engine) decayAllFeeDiscounts(ctx context.Context, perAssetAndPartyUpdat

assetQuantum := asset.Type().Details.Quantum

var decayAmountD *num.Uint
// apply decay discount amount
decayAmount := e.decayFeeDiscountAmount(e.feeDiscountPerPartyAndAsset[key], assetQuantum)
if decayAmount.IsZero() {
decayAmount := e.decayFeeDiscountAmount(e.feeDiscountPerPartyAndAsset[key])

// or 0 if discount is less than e.feeDiscountMinimumTrackedAmount x quantum (where quantum is the asset quantum).
if decayAmount.LessThan(e.feeDiscountMinimumTrackedAmount.Mul(assetQuantum)) {
decayAmountD = num.UintZero()
delete(e.feeDiscountPerPartyAndAsset, key)
} else {
e.feeDiscountPerPartyAndAsset[key] = decayAmount
decayAmountD, _ = num.UintFromDecimal(decayAmount)
e.feeDiscountPerPartyAndAsset[key] = decayAmountD
}

updateDiscountEvents = append(updateDiscountEvents, events.NewTransferFeesDiscountUpdated(
ctx,
key.party,
key.asset,
decayAmount.Clone(),
decayAmountD.Clone(),
e.currentEpoch,
))
}
Expand Down Expand Up @@ -126,11 +131,15 @@ func (e *Engine) updateFeeDiscountsForAsset(
e.feeDiscountPerPartyAndAsset[key] = num.UintZero()
}

// apply decay discount amount
e.feeDiscountPerPartyAndAsset[key] = e.decayFeeDiscountAmount(e.feeDiscountPerPartyAndAsset[key], assetQuantum)
// apply decay discount amount and add new fees to it
newAmount := e.decayFeeDiscountAmount(e.feeDiscountPerPartyAndAsset[key]).Add(fee.ToDecimal())

// add fees
e.feeDiscountPerPartyAndAsset[key].AddSum(fee)
if newAmount.LessThan(e.feeDiscountMinimumTrackedAmount.Mul(assetQuantum)) {
e.feeDiscountPerPartyAndAsset[key] = num.UintZero()
} else {
newAmountD, _ := num.UintFromDecimal(newAmount)
e.feeDiscountPerPartyAndAsset[key] = newAmountD
}

updateDiscountEvents = append(updateDiscountEvents, events.NewTransferFeesDiscountUpdated(
ctx,
Expand Down Expand Up @@ -212,21 +221,13 @@ func (e *Engine) AvailableFeeDiscount(asset string, party string) *num.Uint {
return num.UintZero()
}

// decayFeeDiscountAmount update current discount with: discount x e.feeDiscountDecayFraction
// or 0 if discount is less than e.feeDiscountMinimumTrackedAmount x quantum (where quantum is the asset quantum).
func (e *Engine) decayFeeDiscountAmount(currentDiscount *num.Uint, assetQuantum num.Decimal) *num.Uint {
if currentDiscount.IsZero() {
return currentDiscount
}

decayedAmount := currentDiscount.ToDecimal().Mul(e.feeDiscountDecayFraction)

if decayedAmount.LessThan(e.feeDiscountMinimumTrackedAmount.Mul(assetQuantum)) {
return num.UintZero()
// decayFeeDiscountAmount update current discount with: discount x e.feeDiscountDecayFraction.
func (e *Engine) decayFeeDiscountAmount(currentDiscount *num.Uint) num.Decimal {
discount := currentDiscount.ToDecimal()
if discount.IsZero() {
return discount
}

decayedAmountUint, _ := num.UintFromDecimal(decayedAmount)
return decayedAmountUint
return discount.Mul(e.feeDiscountDecayFraction)
}

func calculateDiscount(accumulatedDiscount, theoreticalFee *num.Uint) (discountedFee, discount *num.Uint) {
Expand Down
4 changes: 3 additions & 1 deletion core/fee/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,6 @@ func (e *Engine) CalculateForContinuousMode(
e.feesStats.RegisterMakerFee(maker, taker, fee.MakerFee)

totalTradingFees := num.UintZero().AddSum(fee.MakerFee, fee.InfrastructureFee, fee.LiquidityFee)
e.feesStats.RegisterTradingFees(maker, taker, totalTradingFees)

switch trade.Aggressor {
case types.SideBuy:
Expand All @@ -197,6 +196,9 @@ func (e *Engine) CalculateForContinuousMode(
maker = trade.Buyer
}

e.feesStats.RegisterTradingFees(taker, totalTradingFees)
e.feesStats.RegisterTradingFees(maker, fee.MakerFee)

totalFeeAmount.AddSum(totalTradingFees)
totalInfrastructureFeeAmount.AddSum(fee.InfrastructureFee)
totalLiquidityFeeAmount.AddSum(fee.LiquidityFee)
Expand Down
36 changes: 18 additions & 18 deletions core/fee/engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,16 +229,16 @@ func testCalcContinuousTradingAndCheckAmounts(t *testing.T) {
},
},
},
TradingFeesGenerated: []*eventspb.TradingFeesGenerated{
TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
{
Taker: "party1",
TradingFeesPaid: []*eventspb.PartyAmount{
{
Party: "party2",
Amount: "875",
QuantumAmount: "875",
},
},
Party: "party1",
Amount: "875",
QuantumAmount: "875",
},
{
Party: "party2",
Amount: "125",
QuantumAmount: "125",
},
},
}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
Expand Down Expand Up @@ -352,16 +352,16 @@ func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) {
},
},
},
TradingFeesGenerated: []*eventspb.TradingFeesGenerated{
TotalFeesPaidAndReceived: []*eventspb.PartyAmount{
{
Taker: "party1",
TradingFeesPaid: []*eventspb.PartyAmount{
{
Party: "party2",
Amount: "443",
QuantumAmount: "443",
},
},
Party: "party1",
Amount: "443",
QuantumAmount: "443",
},
{
Party: "party2",
Amount: "64",
QuantumAmount: "64",
},
},
}, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1)))
Expand Down
97 changes: 26 additions & 71 deletions core/fee/rebate_stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ type FeesStats struct {
// TotalMakerFeesReceived is the total of maker fees received by the maker side.
// maker -> amount
TotalMakerFeesReceived map[string]*num.Uint
// TradingFeesGenerated tracks all trading fees paid by taker (aggressor) to the maker.
// taker -> maker -> amount
TradingFeesGenerated map[string]map[string]*num.Uint
// TradingFeesPaidAndReceived tracks all trading fees paid and received by party.
TradingFeesPaidAndReceived map[string]*num.Uint
// MakerFeesGenerated tracks maker fees paid by taker (aggressor) to the maker.
// taker -> maker -> amount
MakerFeesGenerated map[string]map[string]*num.Uint
Expand All @@ -44,13 +43,13 @@ type FeesStats struct {

func NewFeesStats() *FeesStats {
return &FeesStats{
TotalMakerFeesReceived: map[string]*num.Uint{},
MakerFeesGenerated: map[string]map[string]*num.Uint{},
TradingFeesGenerated: map[string]map[string]*num.Uint{},
TotalRewardsReceived: map[string]*num.Uint{},
ReferrerRewardsGenerated: map[string]map[string]*num.Uint{},
RefereeDiscountApplied: map[string]*num.Uint{},
VolumeDiscountApplied: map[string]*num.Uint{},
TotalMakerFeesReceived: map[string]*num.Uint{},
MakerFeesGenerated: map[string]map[string]*num.Uint{},
TradingFeesPaidAndReceived: map[string]*num.Uint{},
TotalRewardsReceived: map[string]*num.Uint{},
ReferrerRewardsGenerated: map[string]map[string]*num.Uint{},
RefereeDiscountApplied: map[string]*num.Uint{},
VolumeDiscountApplied: map[string]*num.Uint{},
}
}

Expand Down Expand Up @@ -91,13 +90,8 @@ func NewFeesStatsFromProto(fsp *eventspb.FeesStats) *FeesStats {
fs.MakerFeesGenerated[f.Taker] = rg
}

for _, f := range fsp.TradingFeesGenerated {
rg := map[string]*num.Uint{}
for _, pa := range f.TradingFeesPaid {
rg[pa.Party] = num.MustUintFromString(pa.Amount, 10)
}

fs.TradingFeesGenerated[f.Taker] = rg
for _, f := range fsp.TotalFeesPaidAndReceived {
fs.TradingFeesPaidAndReceived[f.Party] = num.MustUintFromString(f.Amount, 10)
}

return fs
Expand Down Expand Up @@ -127,20 +121,13 @@ func (f *FeesStats) RegisterMakerFee(makerID, takerID string, amount *num.Uint)
makerTally.Add(makerTally, amount)
}

func (f *FeesStats) RegisterTradingFees(makerID, takerID string, amount *num.Uint) {
tradingFeesGenerated, ok := f.TradingFeesGenerated[takerID]
if !ok {
tradingFeesGenerated = map[string]*num.Uint{}
f.TradingFeesGenerated[takerID] = tradingFeesGenerated
}

makerTally, ok := tradingFeesGenerated[makerID]
if !ok {
makerTally = num.NewUint(0)
tradingFeesGenerated[makerID] = makerTally
// RegisterTradingFees registers fees paid or received by the party.
func (f *FeesStats) RegisterTradingFees(partyID string, amount *num.Uint) {
if _, ok := f.TradingFeesPaidAndReceived[partyID]; !ok {
f.TradingFeesPaidAndReceived[partyID] = num.UintZero()
}

makerTally.Add(makerTally, amount)
f.TradingFeesPaidAndReceived[partyID].AddSum(amount)
}

func (f *FeesStats) RegisterReferrerReward(
Expand Down Expand Up @@ -192,24 +179,7 @@ func (f *FeesStats) RegisterVolumeDiscount(party string, amount *num.Uint) {

// TotalTradingFeesPerParty returns per party sum of all paid and received trading fees.
func (f *FeesStats) TotalTradingFeesPerParty() map[string]*num.Uint {
generatedFeesPerParty := make(map[string]*num.Uint, len(f.TradingFeesGenerated))

for payer, makers := range f.TradingFeesGenerated {
if _, ok := generatedFeesPerParty[payer]; !ok {
generatedFeesPerParty[payer] = num.UintZero()
}

for receiver, amount := range makers {
if _, ok := generatedFeesPerParty[receiver]; !ok {
generatedFeesPerParty[receiver] = num.UintZero()
}

generatedFeesPerParty[receiver].AddSum(amount)
generatedFeesPerParty[payer].AddSum(amount)
}
}

return generatedFeesPerParty
return f.TradingFeesPaidAndReceived
}

func (f *FeesStats) ToProto(asset string, assetQuantum num.Decimal) *eventspb.FeesStats {
Expand All @@ -221,7 +191,7 @@ func (f *FeesStats) ToProto(asset string, assetQuantum num.Decimal) *eventspb.Fe
VolumeDiscountApplied: make([]*eventspb.PartyAmount, 0, len(f.VolumeDiscountApplied)),
TotalMakerFeesReceived: make([]*eventspb.PartyAmount, 0, len(f.TotalMakerFeesReceived)),
MakerFeesGenerated: make([]*eventspb.MakerFeesGenerated, 0, len(f.MakerFeesGenerated)),
TradingFeesGenerated: make([]*eventspb.TradingFeesGenerated, 0, len(f.TradingFeesGenerated)),
TotalFeesPaidAndReceived: make([]*eventspb.PartyAmount, 0, len(f.TradingFeesPaidAndReceived)),
}

totalRewardsReceivedParties := maps.Keys(f.TotalRewardsReceived)
Expand Down Expand Up @@ -322,31 +292,16 @@ func (f *FeesStats) ToProto(asset string, assetQuantum num.Decimal) *eventspb.Fe
fs.MakerFeesGenerated = append(fs.MakerFeesGenerated, rewardsGenerated)
}

tradingFeesGeneratedParties := maps.Keys(f.TradingFeesGenerated)
tradingFeesGeneratedParties := maps.Keys(f.TradingFeesPaidAndReceived)
sort.Strings(tradingFeesGeneratedParties)
for _, taker := range tradingFeesGeneratedParties {
makersAmounts := f.TradingFeesGenerated[taker]
for _, party := range tradingFeesGeneratedParties {
amount := f.TradingFeesPaidAndReceived[party]

rewardsGenerated := &eventspb.TradingFeesGenerated{
Taker: taker,
TradingFeesPaid: make([]*eventspb.PartyAmount, 0, len(makersAmounts)),
}

makersAmountsParties := maps.Keys(makersAmounts)
sort.Strings(makersAmountsParties)
for _, maker := range makersAmountsParties {
amount := makersAmounts[maker]
rewardsGenerated.TradingFeesPaid = append(
rewardsGenerated.TradingFeesPaid,
&eventspb.PartyAmount{
Party: maker,
Amount: amount.String(),
QuantumAmount: amount.ToDecimal().Div(assetQuantum).Truncate(6).String(),
},
)
}

fs.TradingFeesGenerated = append(fs.TradingFeesGenerated, rewardsGenerated)
fs.TotalFeesPaidAndReceived = append(fs.TotalFeesPaidAndReceived, &eventspb.PartyAmount{
Party: party,
Amount: amount.String(),
QuantumAmount: amount.ToDecimal().Div(assetQuantum).Truncate(6).String(),
})
}

return fs
Expand Down
10 changes: 6 additions & 4 deletions core/fee/rebate_stats_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,12 @@ func TestFeesStats(t *testing.T) {
func testFeesStatsTotalTradingFeesPerParty(t *testing.T) {
stats := fee.NewFeesStats()

stats.RegisterTradingFees("maker-1", "taker-1", num.NewUint(10))
stats.RegisterTradingFees("maker-1", "taker-2", num.NewUint(20))
stats.RegisterTradingFees("taker-1", "maker-1", num.NewUint(5))
stats.RegisterTradingFees("party1", num.NewUint(10))
stats.RegisterTradingFees("party2", num.NewUint(20))
stats.RegisterTradingFees("party1", num.NewUint(5))
stats.RegisterTradingFees("party1", num.NewUint(20))
stats.RegisterTradingFees("party2", num.NewUint(20))

expected := map[string]*num.Uint{"maker-1": num.NewUint(35), "taker-1": num.NewUint(15), "taker-2": num.NewUint(20)}
expected := map[string]*num.Uint{"party1": num.NewUint(35), "party2": num.NewUint(40)}
assert.Equal(t, expected, stats.TotalTradingFeesPerParty())
}
2 changes: 1 addition & 1 deletion core/integration/features/transfers/fee_discounts.feature
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Scenario: transfer where fee-discount has decayed to 0 results in no fee discoun

And the parties have the following transfer fee discounts:
| party | asset | available discount |
| f0b40ebdc5b92cf2cf82ff5d0c3f94085d23d5ec2d37d0b929e177c6d4d37e4c | ETH | 324 |
| f0b40ebdc5b92cf2cf82ff5d0c3f94085d23d5ec2d37d0b929e177c6d4d37e4c | ETH | 16 |

# let the discount decay to zero
Given the following network parameters are set:
Expand Down
14 changes: 4 additions & 10 deletions protos/sources/vega/events/v1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ message PartyVestingStats {

// Fees rewards and discounts paid / received per parties during an epoch
message FeesStats {
reserved 10;

// Market the fees were paid in
string market = 1;
// Settlement asset of the market.
Expand All @@ -105,8 +107,8 @@ message FeesStats {
repeated PartyAmount total_maker_fees_received = 8;
// Maker fees paid by all trade aggressors, and which makers the fees were paid to.
repeated MakerFeesGenerated maker_fees_generated = 9;
// All trading fees paid by all trade aggressors, and which makers the fees were paid to.
repeated TradingFeesGenerated trading_fees_generated = 10;
// Total trading fees received and paid by the party.
repeated PartyAmount total_fees_paid_and_received = 11;
}

// Rewards generated for referrers by each of their referees
Expand All @@ -125,14 +127,6 @@ message MakerFeesGenerated {
repeated PartyAmount maker_fees_paid = 2;
}

// Trading fees generated by the trade aggressor
message TradingFeesGenerated {
// Party that paid the fees.
string taker = 1;
// Amount of trading fees paid by the taker to the maker.
repeated PartyAmount trading_fees_paid = 2;
}

// A pair of a party and amount
message PartyAmount {
// Receiving party ID.
Expand Down
Loading

0 comments on commit b112c0f

Please sign in to comment.