code.vegaprotocol.io/vega@v0.79.0/core/integration/steps/market/oracle_configs.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 market
    17  
    18  import (
    19  	"embed"
    20  	"errors"
    21  	"fmt"
    22  
    23  	"code.vegaprotocol.io/vega/core/datasource"
    24  	"code.vegaprotocol.io/vega/core/integration/steps/helpers"
    25  	"code.vegaprotocol.io/vega/core/integration/steps/market/defaults"
    26  	vegapb "code.vegaprotocol.io/vega/protos/vega"
    27  
    28  	"github.com/jinzhu/copier"
    29  )
    30  
    31  var (
    32  	//go:embed defaults/oracle-config/*future.json
    33  	defaultOracleConfigs         embed.FS
    34  	defaultOracleConfigFileNames = []string{
    35  		"defaults/oracle-config/default-eth-for-future.json",
    36  		"defaults/oracle-config/default-usd-for-future.json",
    37  		"defaults/oracle-config/default-dai-for-future.json",
    38  	}
    39  	//go:embed defaults/oracle-config/*perps.json
    40  	defaultOraclePerpsConfigs         embed.FS
    41  	defaultPerpsOracleConfigFileNames = []string{
    42  		"defaults/oracle-config/default-eth-for-perps.json",
    43  		"defaults/oracle-config/default-usd-for-perps.json",
    44  		"defaults/oracle-config/default-dai-for-perps.json",
    45  	}
    46  
    47  	// swap out the oracle names for future with perp oracles.
    48  	perpsSwapMapping = map[string]string{
    49  		"default-eth-for-future": "default-eth-for-perps",
    50  		"default-usd-for-future": "default-usd-for-perps",
    51  		"default-dai-for-future": "default-dai-for-perps",
    52  	}
    53  )
    54  
    55  type Binding interface {
    56  	Descriptor() ([]byte, []int)
    57  	GetSettlementDataProperty() string
    58  	ProtoMessage()
    59  }
    60  
    61  type BindType interface {
    62  	*vegapb.DataSourceSpecToFutureBinding | *vegapb.DataSourceSpecToPerpetualBinding
    63  }
    64  
    65  type oracleConfigs struct {
    66  	futures               *oConfig[*vegapb.DataSourceSpecToFutureBinding]
    67  	perps                 *oConfig[*vegapb.DataSourceSpecToPerpetualBinding]
    68  	fullPerps             map[string]*vegapb.Perpetual
    69  	fullFutures           map[string]*vegapb.Future
    70  	perpSwap              bool
    71  	compositePriceOracles map[string]CompositePriceOracleConfig
    72  	timeTriggers          map[string]*datasource.Spec
    73  }
    74  
    75  type oConfig[T BindType] struct {
    76  	configForSettlementData    map[string]*OracleConfig[T]
    77  	configFoTradingTermination map[string]*OracleConfig[T]
    78  	configForSchedule          map[string]*OracleConfig[T]
    79  	settlementDataDecimals     map[string]uint32
    80  }
    81  
    82  type OracleConfig[T BindType] struct {
    83  	Spec    *vegapb.OracleSpec
    84  	Binding T
    85  }
    86  
    87  type CompositePriceOracleConfig struct {
    88  	Spec    *vegapb.OracleSpec
    89  	Binding *vegapb.SpecBindingForCompositePrice
    90  }
    91  
    92  func newOracleSpecs(unmarshaler *defaults.Unmarshaler) *oracleConfigs {
    93  	configs := &oracleConfigs{
    94  		futures:               newOConfig[*vegapb.DataSourceSpecToFutureBinding](),
    95  		perps:                 newOConfig[*vegapb.DataSourceSpecToPerpetualBinding](),
    96  		fullPerps:             map[string]*vegapb.Perpetual{},
    97  		fullFutures:           map[string]*vegapb.Future{},
    98  		compositePriceOracles: map[string]CompositePriceOracleConfig{},
    99  		timeTriggers:          map[string]*datasource.Spec{},
   100  	}
   101  	configs.futureOracleSpecs(unmarshaler)
   102  	configs.perpetualOracleSpecs(unmarshaler)
   103  	return configs
   104  }
   105  
   106  func newOConfig[T BindType]() *oConfig[T] {
   107  	return &oConfig[T]{
   108  		configForSettlementData:    map[string]*OracleConfig[T]{},
   109  		configFoTradingTermination: map[string]*OracleConfig[T]{},
   110  		configForSchedule:          map[string]*OracleConfig[T]{},
   111  		settlementDataDecimals:     map[string]uint32{},
   112  	}
   113  }
   114  
   115  func (c *oracleConfigs) SwapToPerps() {
   116  	c.perpSwap = true
   117  }
   118  
   119  func (c *oracleConfigs) CheckName(name string) string {
   120  	if !c.perpSwap {
   121  		return name
   122  	}
   123  	if alt, ok := perpsSwapMapping[name]; ok {
   124  		return alt
   125  	}
   126  	return name
   127  }
   128  
   129  func (c *oracleConfigs) futureOracleSpecs(unmarshaler *defaults.Unmarshaler) {
   130  	contentReaders := helpers.ReadAll(defaultOracleConfigs, defaultOracleConfigFileNames)
   131  	for name, contentReader := range contentReaders {
   132  		future, err := unmarshaler.UnmarshalDataSourceConfig(contentReader)
   133  		if err != nil {
   134  			panic(fmt.Errorf("couldn't unmarshal default data source config %s: %v", name, err))
   135  		}
   136  		if err := c.AddFuture(name, future); err != nil {
   137  			panic(fmt.Errorf("failed to add default data source config %s: %v", name, err))
   138  		}
   139  	}
   140  }
   141  
   142  func (c *oracleConfigs) perpetualOracleSpecs(unmarshaler *defaults.Unmarshaler) {
   143  	contentReaders := helpers.ReadAll(defaultOraclePerpsConfigs, defaultPerpsOracleConfigFileNames)
   144  	for name, contentReader := range contentReaders {
   145  		perp, err := unmarshaler.UnmarshalPerpsDataSourceConfig(contentReader)
   146  		if err != nil {
   147  			panic(fmt.Errorf("couldn't unmarshal default data source config %s: %v", name, err))
   148  		}
   149  		if err := c.AddPerp(name, perp); err != nil {
   150  			panic(fmt.Errorf("failed to add default data source config %s: %v", name, err))
   151  		}
   152  	}
   153  }
   154  
   155  func (c *oracleConfigs) SetSettlementDataDP(name string, decimals uint32) {
   156  	// for now, wing it and set for both
   157  	c.futures.SetSettlementDataDP(name, decimals)
   158  	c.perps.SetSettlementDataDP(name, decimals)
   159  }
   160  
   161  func (f *oConfig[T]) SetSettlementDataDP(name string, decimals uint32) {
   162  	f.settlementDataDecimals[name] = decimals
   163  }
   164  
   165  func (c *oracleConfigs) GetSettlementDataDP(name string) uint32 {
   166  	fd, ok := c.futures.GetSettlementDataDP(name)
   167  	if !ok {
   168  		fd, _ = c.perps.GetSettlementDataDP(name)
   169  	}
   170  	return fd
   171  }
   172  
   173  func (f *oConfig[T]) GetSettlementDataDP(name string) (uint32, bool) {
   174  	dp, ok := f.settlementDataDecimals[name]
   175  	if ok {
   176  		return dp, ok
   177  	}
   178  	return 0, ok
   179  }
   180  
   181  func (c *oracleConfigs) AddFuture(name string, future *vegapb.Future) error {
   182  	if err := c.futures.Add(name, "settlement data", future.DataSourceSpecForSettlementData, future.DataSourceSpecBinding); err != nil {
   183  		return err
   184  	}
   185  	if err := c.futures.Add(name, "trading termination", future.DataSourceSpecForTradingTermination, future.DataSourceSpecBinding); err != nil {
   186  		return err
   187  	}
   188  	c.fullFutures[name] = future
   189  	return nil
   190  }
   191  
   192  func (c *oracleConfigs) AddCompositePriceOracle(name string, spec *vegapb.DataSourceSpec, binding *vegapb.SpecBindingForCompositePrice) {
   193  	c.compositePriceOracles[name] = CompositePriceOracleConfig{
   194  		Spec: &vegapb.OracleSpec{
   195  			ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   196  				Spec: spec,
   197  			},
   198  		},
   199  		Binding: binding,
   200  	}
   201  }
   202  
   203  func (c *oracleConfigs) AddPerp(name string, perp *vegapb.Perpetual) error {
   204  	if err := c.perps.Add(name, "settlement data", perp.DataSourceSpecForSettlementData, perp.DataSourceSpecBinding); err != nil {
   205  		return err
   206  	}
   207  	if err := c.perps.Add(name, "settlement schedule", perp.DataSourceSpecForSettlementSchedule, perp.DataSourceSpecBinding); err != nil {
   208  		return err
   209  	}
   210  	c.fullPerps[name] = perp
   211  	return nil
   212  }
   213  
   214  func (c *oracleConfigs) AddTimeTrigger(name string, spec *datasource.Spec) error {
   215  	c.timeTriggers[name] = spec
   216  	return nil
   217  }
   218  
   219  func (c *oracleConfigs) Add(name, specType string, spec *vegapb.DataSourceSpec, binding Binding) error {
   220  	switch bt := binding.(type) {
   221  	case *vegapb.DataSourceSpecToPerpetualBinding:
   222  		return c.perps.Add(name, specType, spec, bt)
   223  	case *vegapb.DataSourceSpecToFutureBinding:
   224  		return c.futures.Add(name, specType, spec, bt)
   225  	default:
   226  		panic("unsupported binding type")
   227  	}
   228  	return nil
   229  }
   230  
   231  func (f *oConfig[T]) Add(
   232  	name string,
   233  	specType string,
   234  	spec *vegapb.DataSourceSpec,
   235  	binding T,
   236  ) error {
   237  	if specType == "settlement data" {
   238  		f.configForSettlementData[name] = &OracleConfig[T]{
   239  			Spec: &vegapb.OracleSpec{
   240  				ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   241  					Spec: spec,
   242  				},
   243  			},
   244  			Binding: binding,
   245  		}
   246  		for _, filter := range spec.GetData().GetFilters() {
   247  			if filter.Key.NumberDecimalPlaces != nil {
   248  				f.settlementDataDecimals[name] = uint32(*filter.Key.NumberDecimalPlaces)
   249  				break
   250  			}
   251  		}
   252  	} else if specType == "trading termination" {
   253  		f.configFoTradingTermination[name] = &OracleConfig[T]{
   254  			Spec: &vegapb.OracleSpec{
   255  				ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   256  					Spec: spec,
   257  				},
   258  			},
   259  			Binding: binding,
   260  		}
   261  	} else if specType == "settlement schedule" {
   262  		f.configForSchedule[name] = &OracleConfig[T]{
   263  			Spec: &vegapb.OracleSpec{
   264  				ExternalDataSourceSpec: &vegapb.ExternalDataSourceSpec{
   265  					Spec: spec,
   266  				},
   267  			},
   268  			Binding: binding,
   269  		}
   270  	} else {
   271  		return errors.New("unknown oracle spec type definition - expecting settlement data or trading termination")
   272  	}
   273  
   274  	return nil
   275  }
   276  
   277  func (c *oracleConfigs) GetFullInstrument(name string) (*vegapb.Instrument, error) {
   278  	var instrument vegapb.Instrument
   279  	if fut, ok := c.fullFutures[name]; ok {
   280  		instrument.Product = &vegapb.Instrument_Future{
   281  			Future: fut,
   282  		}
   283  		return &instrument, nil
   284  	}
   285  	if perp, ok := c.fullPerps[name]; ok {
   286  		instrument.Product = &vegapb.Instrument_Perpetual{
   287  			Perpetual: perp,
   288  		}
   289  		return &instrument, nil
   290  	}
   291  	return nil, fmt.Errorf("no products with name %s found", name)
   292  }
   293  
   294  func (c *oracleConfigs) GetOracleDefinitionForCompositePrice(name string) (*vegapb.OracleSpec, *vegapb.SpecBindingForCompositePrice, error) {
   295  	if oc, found := c.compositePriceOracles[name]; !found {
   296  		return nil, nil, fmt.Errorf("oracle config for composite price not found")
   297  	} else {
   298  		return oc.Spec, oc.Binding, nil
   299  	}
   300  }
   301  
   302  func (c *oracleConfigs) GetFuture(name, specType string) (*OracleConfig[*vegapb.DataSourceSpecToFutureBinding], error) {
   303  	return c.futures.Get(name, specType)
   304  }
   305  
   306  func (c *oracleConfigs) GetPerps(name, specType string) (*OracleConfig[*vegapb.DataSourceSpecToPerpetualBinding], error) {
   307  	return c.perps.Get(name, specType)
   308  }
   309  
   310  func (c *oracleConfigs) GetFullPerp(name string) (*vegapb.Perpetual, error) {
   311  	// if we're swapping to perps, ensure we have the correct oracle name
   312  	if c.perpSwap {
   313  		name = c.CheckName(name)
   314  	}
   315  	perp, ok := c.fullPerps[name]
   316  	if !ok {
   317  		return nil, fmt.Errorf("perpetual product with name %s not found", name)
   318  	}
   319  	copyConfig := &vegapb.Perpetual{}
   320  	if err := copier.Copy(copyConfig, perp); err != nil {
   321  		panic(fmt.Errorf("failed to deep copy oracle config: %v", err))
   322  	}
   323  	return copyConfig, nil
   324  }
   325  
   326  func (c *oracleConfigs) GetFullFuture(name string) (*vegapb.Future, error) {
   327  	future, ok := c.fullFutures[name]
   328  	if !ok {
   329  		return nil, fmt.Errorf("future product with name %s not found", name)
   330  	}
   331  	copyConfig := &vegapb.Future{}
   332  	if err := copier.Copy(copyConfig, future); err != nil {
   333  		panic(fmt.Errorf("failed to deep copy oracle config: %v", err))
   334  	}
   335  	return copyConfig, nil
   336  }
   337  
   338  func (f *oConfig[T]) Get(name string, specType string) (*OracleConfig[T], error) {
   339  	var cfg map[string]*OracleConfig[T]
   340  
   341  	if specType == "settlement data" {
   342  		cfg = f.configForSettlementData
   343  	} else if specType == "trading termination" {
   344  		cfg = f.configFoTradingTermination
   345  	} else if specType == "settlement schedule" {
   346  		cfg = f.configForSchedule
   347  	} else {
   348  		return nil, errors.New("unknown oracle spec type definition - expecting settlement data or trading termination")
   349  	}
   350  
   351  	config, ok := cfg[name]
   352  	if !ok {
   353  		return config, fmt.Errorf("no oracle spec \"%s\" registered", name)
   354  	}
   355  	// Copy to avoid modification between tests.
   356  	copyConfig := &OracleConfig[T]{}
   357  	if err := copier.Copy(copyConfig, config); err != nil {
   358  		panic(fmt.Errorf("failed to deep copy oracle config: %v", err))
   359  	}
   360  	return copyConfig, nil
   361  }
   362  
   363  func (c *oracleConfigs) GetTimeTrigger(name string) (*datasource.Spec, error) {
   364  	ds, ok := c.timeTriggers[name]
   365  	if !ok {
   366  		return nil, fmt.Errorf("oracle name not found")
   367  	}
   368  	return ds, nil
   369  }