code.vegaprotocol.io/vega@v0.79.0/core/integration/steps/funding_payment_events.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 steps 17 18 import ( 19 "fmt" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/events" 23 "code.vegaprotocol.io/vega/core/integration/stubs" 24 "code.vegaprotocol.io/vega/core/types" 25 "code.vegaprotocol.io/vega/libs/num" 26 "code.vegaprotocol.io/vega/logging" 27 eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1" 28 29 "github.com/cucumber/godog" 30 ) 31 32 func TheFollowingFundingPeriodEventsShouldBeEmitted(broker *stubs.BrokerStub, table *godog.Table) error { 33 fundingPeriodEvents := broker.GetFundingPeriodEvents() 34 for _, row := range parseFundingPeriodEventTable(table) { 35 fpe := FundingPeriodEventWrapper{ 36 row: row, 37 } 38 39 matched := false 40 for _, evt := range fundingPeriodEvents { 41 if checkFundingPeriodEvent(evt, fpe) { 42 matched = true 43 break 44 } 45 } 46 if !matched { 47 start, _ := fpe.Start() 48 end, _ := fpe.End() 49 return fmt.Errorf("Funding period event with start=%v, end=%v, internal TWAP=%s, external TWAP=%s not found", start, end, fpe.InternalTWAP(), fpe.ExternalTWAP()) 50 } 51 } 52 return nil 53 } 54 55 func TheFollowingFundingPaymentEventsShouldBeEmitted(broker *stubs.BrokerStub, table *godog.Table) error { 56 paymentEvts := broker.GetFundginPaymentEvents() 57 checkLoss := false 58 rows := parseFundingPaymentsTable(table) 59 matchers := make([]FundingPaymentsWrapper, 0, len(rows)) 60 for _, row := range rows { 61 w := FundingPaymentsWrapper{ 62 r: row, 63 } 64 matchers = append(matchers, w) 65 checkLoss = (checkLoss || w.CheckLoss()) 66 } 67 // map the events by party and market 68 lsEvt := map[string]map[string][]*events.LossSoc{} 69 if checkLoss { 70 for _, ls := range broker.GetLossSoc() { 71 mID, pID := ls.MarketID(), ls.PartyID() 72 mmap, ok := lsEvt[mID] 73 if !ok { 74 mmap = map[string][]*events.LossSoc{} 75 } 76 ps, ok := mmap[pID] 77 if !ok { 78 ps = []*events.LossSoc{} 79 } 80 ps = append(ps, ls) 81 mmap[pID] = ps 82 lsEvt[mID] = mmap 83 } 84 } 85 // get by party and market 86 pEvts := map[string]map[string][]*eventspb.FundingPayment{} 87 for _, pe := range paymentEvts { 88 mID := pe.MarketID() 89 mmap, ok := pEvts[mID] 90 if !ok { 91 mmap = map[string][]*eventspb.FundingPayment{} 92 } 93 for _, fp := range pe.FundingPayments().Payments { 94 fps, ok := mmap[fp.PartyId] 95 if !ok { 96 fps = []*eventspb.FundingPayment{} 97 } 98 fps = append(fps, fp) 99 mmap[fp.PartyId] = fps 100 } 101 pEvts[mID] = mmap 102 } 103 // now start matching 104 for _, row := range matchers { 105 mID, pID := row.Market(), row.Party() 106 mmap, ok := pEvts[mID] 107 if !ok { 108 return fmt.Errorf("could not find funding payment events for market %s", mID) 109 } 110 ppayments, ok := mmap[pID] 111 if !ok { 112 return fmt.Errorf("could not find funding payment events for party %s in market %s", pID, mID) 113 } 114 matched := false 115 amt := row.Amount().String() 116 for _, fp := range ppayments { 117 if fp.Amount == amt { 118 matched = true 119 break 120 } 121 } 122 if !matched { 123 return fmt.Errorf("could not find funding payment of amount %s for party %s in market %s", amt, pID, mID) 124 } 125 if !checkLoss || !row.CheckLoss() { 126 continue 127 } 128 mloss, ok := lsEvt[mID] 129 if !ok { 130 return fmt.Errorf("could not find loss socialisation events for market %s", mID) 131 } 132 pLoss, ok := mloss[pID] 133 if !ok { 134 return fmt.Errorf("could not find loss socialisation event for party %s in market %s", pID, mID) 135 } 136 matched = false 137 for _, le := range pLoss { 138 if !row.matchLossType(le.LossType()) { 139 continue 140 } 141 if !row.matchLossAmount(le.Amount()) { 142 continue 143 } 144 matched = true 145 break 146 } 147 if !matched { 148 return fmt.Errorf("could not find loss amount/type %s/%s for party %s in market %s", row.LossAmount().String(), row.LossType().String(), pID, mID) 149 } 150 } 151 return nil 152 } 153 154 func DebugFundingPaymentsEvents(broker *stubs.BrokerStub, log *logging.Logger) { 155 paymentEvts := broker.GetFundginPaymentEvents() 156 lossSoc := broker.GetLossSoc() 157 pEvts := map[string]map[string][]*eventspb.FundingPayment{} 158 lsEvt := map[string]map[string][]*events.LossSoc{} 159 for _, pe := range paymentEvts { 160 mID := pe.MarketID() 161 mmap, ok := pEvts[mID] 162 if !ok { 163 mmap = map[string][]*eventspb.FundingPayment{} 164 } 165 for _, fp := range pe.FundingPayments().Payments { 166 fps, ok := mmap[fp.PartyId] 167 if !ok { 168 fps = []*eventspb.FundingPayment{} 169 } 170 fps = append(fps, fp) 171 mmap[fp.PartyId] = fps 172 } 173 pEvts[mID] = mmap 174 } 175 for _, le := range lossSoc { 176 mID, pID := le.MarketID(), le.PartyID() 177 // ignore loss socialisation unless they are related to funding payments: 178 if mmap, ok := pEvts[mID]; !ok { 179 continue 180 } else if _, ok := mmap[pID]; !ok { 181 // also skip the parties that don't have funding payment events. 182 continue 183 } 184 mmap, ok := lsEvt[mID] 185 if !ok { 186 mmap = map[string][]*events.LossSoc{} 187 } 188 // ignore irrelevant parties? 189 ps, ok := mmap[pID] 190 if !ok { 191 ps = []*events.LossSoc{} 192 } 193 ps = append(ps, le) 194 mmap[pID] = ps 195 lsEvt[mID] = mmap 196 } 197 log.Info("DUMPING FUNDING PAYMENTS EVENTS") 198 for mID, fpMap := range pEvts { 199 log.Infof("Market ID: %s\n", mID) 200 for pID, fpe := range fpMap { 201 log.Infof("PartyID: %s\n", pID) 202 var lSoc []*events.LossSoc 203 lossM, ok := lsEvt[mID] 204 if ok { 205 lSoc = lossM[pID] 206 } 207 for i, fe := range fpe { 208 log.Infof("%d: Amount %s\n", i+1, fe.Amount) 209 } 210 if len(lSoc) > 0 { 211 log.Info("\nLOSS SOCIALISATION:\n") 212 } 213 for i, le := range lSoc { 214 log.Infof("%d: Amount: %s - Type: %s\n", i+1, le.Amount().String(), le.LossType().String()) 215 } 216 } 217 } 218 } 219 220 func DebugFundingPeriodEventss(broker *stubs.BrokerStub, log *logging.Logger) { 221 log.Info("DUMPING FUNDING PERIOD EVENTS") 222 data := broker.GetFundingPeriodEvents() 223 for _, evt := range data { 224 p := evt.Proto() 225 log.Infof("%s\n", p.String()) 226 } 227 } 228 229 func VerifyTime(now time.Time, expected int64) error { 230 actual := now.Unix() 231 if actual == expected || now.UnixNano() == expected { 232 return nil 233 } 234 return fmt.Errorf("Expected unix time=%v, actual=%v", expected, actual) 235 } 236 237 func checkFundingPeriodEvent(evt events.FundingPeriod, row FundingPeriodEventWrapper) bool { 238 fundingPeriod := evt.FundingPeriod() 239 240 expectedStart, b := row.Start() 241 actualStart := fundingPeriod.GetStart() 242 if b && !actualEqualsExpectedInSecondsOrNanos(actualStart, expectedStart) { 243 return false 244 } 245 expectedEnd, b := row.End() 246 actualEnd := fundingPeriod.GetEnd() 247 if b && !actualEqualsExpectedInSecondsOrNanos(actualEnd, expectedEnd) { 248 return false 249 } 250 expectedInternalTwap := row.InternalTWAP() 251 actualInternalTwap := fundingPeriod.GetInternalTwap() 252 if actualInternalTwap == "" && len(expectedInternalTwap) > 0 || expectedInternalTwap != actualInternalTwap { 253 return false 254 } 255 expectedExternalTwap := row.ExternalTWAP() 256 actualExternalTwap := fundingPeriod.GetExternalTwap() 257 258 if actualExternalTwap == "" && len(expectedExternalTwap) > 0 || expectedExternalTwap != actualExternalTwap { 259 return false 260 } 261 expectedFundingPayment, b := row.FundingPayment() 262 actualFundingPayment := fundingPeriod.GetFundingPayment() 263 if b && (actualFundingPayment == "" && len(expectedFundingPayment) > 0 || expectedFundingPayment != actualFundingPayment) { 264 return false 265 } 266 expectedFundingRate, b := row.FundingRate() 267 actualFundingRate := fundingPeriod.GetFundingRate() 268 if b && (actualFundingRate == "" && len(expectedFundingRate) > 0 || expectedFundingRate != actualFundingRate) { 269 return false 270 } 271 272 return true 273 } 274 275 func actualEqualsExpectedInSecondsOrNanos(actual, expected int64) bool { 276 return expected == actual || expected*int64(time.Second) == actual 277 } 278 279 type FundingPeriodEventWrapper struct { 280 row RowWrapper 281 } 282 283 type FundingPaymentsWrapper struct { 284 r RowWrapper 285 } 286 287 func parseFundingPeriodEventTable(table *godog.Table) []RowWrapper { 288 return StrictParseTable(table, []string{ 289 "internal twap", 290 "external twap", 291 }, []string{ 292 "funding payment", 293 "funding rate", 294 "start", 295 "end", 296 }) 297 } 298 299 func parseFundingPaymentsTable(table *godog.Table) []RowWrapper { 300 return StrictParseTable(table, []string{ 301 "party", 302 "market", 303 "amount", 304 }, []string{ 305 "loss type", 306 "loss amount", 307 }) 308 } 309 310 func (f FundingPaymentsWrapper) Party() string { 311 return f.r.MustStr("party") 312 } 313 314 func (f FundingPaymentsWrapper) Market() string { 315 return f.r.MustStr("market") 316 } 317 318 func (f FundingPaymentsWrapper) Amount() *num.Int { 319 return f.r.MustInt("amount") 320 } 321 322 func (f FundingPaymentsWrapper) LossAmount() *num.Int { 323 if !f.r.HasColumn("loss amount") { 324 return num.IntZero() 325 } 326 return f.r.MustInt("loss amount") 327 } 328 329 func (f FundingPaymentsWrapper) LossType() types.LossType { 330 if !f.r.HasColumn("loss type") { 331 return types.LossTypeUnspecified 332 } 333 return f.r.MustLossType("loss type") 334 } 335 336 func (f FundingPaymentsWrapper) CheckLoss() bool { 337 return f.r.HasColumn("loss type") || f.r.HasColumn("loss amount") 338 } 339 340 func (f FundingPaymentsWrapper) matchLossType(t types.LossType) bool { 341 if !f.r.HasColumn("loss type") { 342 return true 343 } 344 return f.LossType() == t 345 } 346 347 func (f FundingPaymentsWrapper) matchLossAmount(amt *num.Int) bool { 348 if !f.r.HasColumn("loss amount") { 349 return true 350 } 351 return f.LossAmount().EQ(amt) 352 } 353 354 func (f FundingPeriodEventWrapper) InternalTWAP() string { 355 return f.row.MustStr("internal twap") 356 } 357 358 func (f FundingPeriodEventWrapper) ExternalTWAP() string { 359 return f.row.MustStr("external twap") 360 } 361 362 func (f FundingPeriodEventWrapper) FundingPayment() (string, bool) { 363 return f.row.StrB("funding payment") 364 } 365 366 func (f FundingPeriodEventWrapper) FundingRate() (string, bool) { 367 return f.row.StrB("funding rate") 368 } 369 370 func (f FundingPeriodEventWrapper) Start() (int64, bool) { 371 return f.row.I64B("start") 372 } 373 374 func (f FundingPeriodEventWrapper) End() (int64, bool) { 375 return f.row.I64B("end") 376 }