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  }