code.vegaprotocol.io/vega@v0.79.0/core/collateral/simple_distributor.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 collateral 17 18 import ( 19 "context" 20 21 "code.vegaprotocol.io/vega/core/events" 22 "code.vegaprotocol.io/vega/core/types" 23 "code.vegaprotocol.io/vega/libs/num" 24 "code.vegaprotocol.io/vega/logging" 25 ) 26 27 type request struct { 28 amount num.Decimal 29 amt *num.Uint 30 request *types.Transfer 31 } 32 33 type simpleDistributor struct { 34 log *logging.Logger 35 marketID string 36 expectCollected *num.Uint 37 collected *num.Uint 38 requests []request 39 ts int64 40 lType types.LossType 41 } 42 43 func (s *simpleDistributor) LossSocializationEnabled() bool { 44 return s.collected.LT(s.expectCollected) 45 } 46 47 func (s *simpleDistributor) Add(req *types.Transfer) { 48 col, exp := num.DecimalFromUint(s.collected), num.DecimalFromUint(s.expectCollected) 49 amount := num.DecimalFromUint(req.Amount.Amount).Mul(col.Div(exp)).Floor() 50 amt, _ := num.UintFromBig(amount.BigInt()) 51 s.requests = append(s.requests, request{ 52 amount: amount, 53 amt: amt, 54 request: req, 55 }) 56 } 57 58 func (s *simpleDistributor) Run(ctx context.Context) []events.Event { 59 if s.expectCollected.EQ(s.collected) { 60 return nil 61 } 62 63 var ( 64 total = num.UintZero() 65 evts = make([]events.Event, 0, len(s.requests)) 66 evt *events.LossSoc 67 netReq *request 68 ) 69 for _, v := range s.requests { 70 total.AddSum(v.amt) 71 loss, _ := num.UintZero().Delta(v.amt, v.request.Amount.Amount) 72 v.request.Amount.Amount = v.amt.Clone() 73 if v.request.Owner == types.NetworkParty { 74 v := v 75 netReq = &v 76 } 77 evt = events.NewLossSocializationEvent(ctx, v.request.Owner, s.marketID, loss, true, s.ts, s.lType) 78 s.log.Warn("loss socialization missing funds to be distributed", 79 logging.String("party-id", evt.PartyID()), 80 logging.BigInt("amount", evt.Amount()), 81 logging.String("market-id", evt.MarketID())) 82 evts = append(evts, evt) 83 } 84 85 if total.NEQ(s.collected) { 86 mismatch, _ := total.Delta(s.collected, total) 87 if netReq != nil { 88 netReq.request.Amount.Amount.AddSum(mismatch) 89 return evts 90 } 91 // last one get the remaining bits 92 s.requests[len(s.requests)-1].request.Amount.Amount.AddSum(mismatch) 93 // if the remainder > the loss amount, this rounding error was profitable 94 // so the loss socialisation event should be flagged as profit 95 // profit will be true if the shortfall < mismatch amount 96 loss, profit := mismatch.Delta(evt.Amount().U, mismatch) 97 evts[len(evts)-1] = events.NewLossSocializationEvent( 98 evt.Context(), 99 evt.PartyID(), 100 evt.MarketID(), 101 loss, 102 !profit, // true if party still lost out, false if mismatch > shortfall 103 s.ts, 104 s.lType, 105 ) 106 } 107 return evts 108 }