code.vegaprotocol.io/vega@v0.79.0/core/execution/common/fees.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package common 17 18 import ( 19 "errors" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/libs/num" 24 ) 25 26 type FeeSplitter struct { 27 timeWindowStart time.Time 28 currentTime time.Time 29 tradeValue *num.Uint 30 changed bool 31 avg num.Decimal 32 window uint64 33 } 34 35 func NewFeeSplitter() *FeeSplitter { 36 return &FeeSplitter{ 37 tradeValue: num.UintZero(), 38 changed: true, 39 window: 1, // initialise as 1 otherwise the average value calculation ends up being borked 40 avg: num.DecimalZero(), 41 } 42 } 43 44 func (fs *FeeSplitter) SetCurrentTime(t time.Time) error { 45 if t.Before(fs.timeWindowStart) { 46 return errors.New("current time can't be before current window time") 47 } 48 // we're past the opening auction, or we have a trade value (ie we're leaving opening auction) 49 fs.currentTime = t 50 return nil 51 } 52 53 func (fs *FeeSplitter) TradeValue() *num.Uint { 54 return fs.tradeValue.Clone() 55 } 56 57 func (fs *FeeSplitter) AddTradeValue(tv *num.Uint) { 58 fs.tradeValue.AddSum(tv) 59 fs.changed = true 60 } 61 62 func (fs *FeeSplitter) SetTradeValue(tv *num.Uint) { 63 fs.tradeValue = tv.Clone() 64 } 65 66 // TimeWindowStart starts or restarts (if active) a current time window. 67 // This sets the internal timers to `t` and resets the accumulated trade values. 68 func (fs *FeeSplitter) TimeWindowStart(t time.Time) { 69 // if we have an average value, that means we left the opening auction 70 // and we can increase the window to the next value 71 if !fs.avg.IsZero() { 72 fs.window++ 73 } else if !fs.tradeValue.IsZero() { 74 // if tradeValue is set, but the average hasn't been updated, it means we're currently leaving opening auction 75 // we should set the average accordingly: avg == trade_value, but keep the window as-is. 76 // next time we calculate the avg for window == 1, the value should be avg + trade_val (opening auction + trade value) 77 fs.avg = num.DecimalFromUint(fs.tradeValue) 78 } 79 // reset the trade value for this window 80 fs.tradeValue = num.UintZero() 81 82 // reset both timers 83 fs.timeWindowStart = t 84 fs.SetCurrentTime(t) 85 fs.changed = true 86 } 87 88 // Elapsed returns the distance (in duration) from TimeWindowStart and 89 // CurrentTime. 90 func (fs *FeeSplitter) Elapsed() time.Duration { 91 return fs.currentTime.Sub(fs.timeWindowStart) 92 } 93 94 func (fs *FeeSplitter) SetElapsed(e time.Duration) { 95 fs.timeWindowStart = fs.currentTime.Add(-e) 96 } 97 98 func (fs *FeeSplitter) activeWindowLength(mvw time.Duration) time.Duration { 99 t := fs.Elapsed() 100 return t - num.MaxV(t-mvw, 0) 101 } 102 103 // MarketValueProxy returns the market value proxy according to the spec: 104 // https://github.com/vegaprotocol/product/blob/master/specs/0042-setting-fees-and-rewarding-lps.md 105 func (fs *FeeSplitter) MarketValueProxy(mvwl time.Duration, totalStakeU *num.Uint) num.Decimal { 106 totalStake := num.DecimalFromUint(totalStakeU) 107 // t is the distance between 108 awl := fs.activeWindowLength(mvwl) 109 if awl > 0 { 110 factor := num.DecimalFromInt64(mvwl.Nanoseconds()).Div( 111 num.DecimalFromInt64(awl.Nanoseconds())) 112 tv := num.DecimalFromUint(fs.tradeValue) 113 return num.MaxD(totalStake, factor.Mul(tv)) 114 } 115 return totalStake 116 } 117 118 func (fs *FeeSplitter) AvgTradeValue() num.Decimal { 119 tv := num.DecimalFromUint(fs.tradeValue) 120 // end of 1st window after opening auction 121 if fs.window == 1 { 122 fs.avg = fs.avg.Add(tv) 123 if !tv.IsZero() { 124 fs.changed = true 125 } 126 return fs.avg 127 } 128 fs.changed = true 129 // n == 2 or more 130 n := num.DecimalFromInt64(int64(fs.window)) 131 // nmin == 1 or more 132 nmin := num.DecimalFromInt64(int64(fs.window - 1)) 133 // avg = avg * ((n-1)/n) + tv/n 134 fs.avg = fs.avg.Mul(nmin.Div(n)).Add(tv.Div(n)) 135 return fs.avg 136 // return tv 137 } 138 139 func NewFeeSplitterFromSnapshot(fs *types.FeeSplitter, now time.Time) *FeeSplitter { 140 return &FeeSplitter{ 141 timeWindowStart: fs.TimeWindowStart, 142 currentTime: now, 143 tradeValue: fs.TradeValue, 144 changed: true, 145 avg: fs.Avg, 146 window: fs.Window, 147 } 148 } 149 150 func (fs *FeeSplitter) GetState() *types.FeeSplitter { 151 fs.changed = false 152 return &types.FeeSplitter{ 153 TimeWindowStart: fs.timeWindowStart, 154 TradeValue: fs.tradeValue, 155 Avg: fs.avg, 156 Window: fs.window, 157 } 158 } 159 160 func (fs *FeeSplitter) Changed() bool { 161 return fs.changed 162 }