code.vegaprotocol.io/vega@v0.79.0/core/products/future_test.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 products_test
    17  
    18  import (
    19  	"context"
    20  	"testing"
    21  
    22  	"code.vegaprotocol.io/vega/core/datasource"
    23  	dstypes "code.vegaprotocol.io/vega/core/datasource/common"
    24  	"code.vegaprotocol.io/vega/core/datasource/external/signedoracle"
    25  	"code.vegaprotocol.io/vega/core/datasource/spec"
    26  	"code.vegaprotocol.io/vega/core/products"
    27  	"code.vegaprotocol.io/vega/core/products/mocks"
    28  	"code.vegaprotocol.io/vega/core/types"
    29  	"code.vegaprotocol.io/vega/libs/num"
    30  	"code.vegaprotocol.io/vega/logging"
    31  	datapb "code.vegaprotocol.io/vega/protos/vega/data/v1"
    32  
    33  	"github.com/golang/mock/gomock"
    34  	"github.com/stretchr/testify/assert"
    35  	"github.com/stretchr/testify/require"
    36  )
    37  
    38  func TestScalingOfSettlementData(t *testing.T) {
    39  	t.Run("No scaling needed for settlement data for asset decimals", testNoScalingNeeded)
    40  	t.Run("Need to scale up the settlement data for asset decimals", testScalingUpNeeded)
    41  	t.Run("Need to scale down the settlement data for asset decimals no loss of precision", testScalingDownNeeded)
    42  	t.Run("Need to scale down the settlement data for asset decimals with loss of precision", testScalingDownNeededWithPrecisionLoss)
    43  	t.Run("a future product can be updated", testUpdateFuture)
    44  }
    45  
    46  func testNoScalingNeeded(t *testing.T) {
    47  	// Create test future with settlement data type integer with decimals (that represents a decimal)
    48  	ft := testFuture(t, datapb.PropertyKey_TYPE_INTEGER)
    49  
    50  	n := &num.Numeric{}
    51  	n.SetUint(num.NewUint(100000))
    52  	// settlement data is in 5 decimal places, asset in 5 decimal places => no scaling
    53  	scaled, err := ft.future.ScaleSettlementDataToDecimalPlaces(
    54  		n, 5,
    55  	)
    56  	require.NoError(t, err)
    57  	require.Equal(t, num.NewUint(100000), scaled)
    58  
    59  	// Create test future with settlement data type decimal with decimals (that represents a decimal)
    60  	ft = testFuture(t, datapb.PropertyKey_TYPE_DECIMAL)
    61  
    62  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
    63  	dec := num.DecimalFromFloat(10000.01101)
    64  	n.SetDecimal(&dec)
    65  	scaled, err = ft.future.ScaleSettlementDataToDecimalPlaces(
    66  		n, 5,
    67  	)
    68  	require.NoError(t, err)
    69  	require.Equal(t, num.NewUint(1000001101), scaled)
    70  }
    71  
    72  func testScalingUpNeeded(t *testing.T) {
    73  	// Create test future with settlement data type integer with decimals (that represents a decimal)
    74  	ft := testFuture(t, datapb.PropertyKey_TYPE_INTEGER)
    75  
    76  	n := &num.Numeric{}
    77  	n.SetUint(num.NewUint(100000))
    78  	// settlement data is in 5 decimal places, asset in 10 decimal places => x10^5
    79  	scaled, err := ft.future.ScaleSettlementDataToDecimalPlaces(
    80  		n, 10,
    81  	)
    82  	require.NoError(t, err)
    83  	require.Equal(t, num.NewUint(10000000000), scaled)
    84  
    85  	// Create test future with settlement data type decimal with decimals (that represents a decimal)
    86  	ft = testFuture(t, datapb.PropertyKey_TYPE_DECIMAL)
    87  
    88  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
    89  	dec := num.DecimalFromFloat(10000.00001)
    90  	n.SetDecimal(&dec)
    91  	scaled, err = ft.future.ScaleSettlementDataToDecimalPlaces(
    92  		n, 10,
    93  	)
    94  	require.NoError(t, err)
    95  	require.Equal(t, num.NewUint(100000000100000), scaled)
    96  }
    97  
    98  func testScalingDownNeeded(t *testing.T) {
    99  	// Create test future with settlement data type integer with decimals (that represents a decimal)
   100  	ft := testFuture(t, datapb.PropertyKey_TYPE_INTEGER)
   101  
   102  	n := &num.Numeric{}
   103  	n.SetUint(num.NewUint(100000))
   104  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
   105  	scaled, err := ft.future.ScaleSettlementDataToDecimalPlaces(
   106  		n, 3,
   107  	)
   108  	require.NoError(t, err)
   109  	require.Equal(t, num.NewUint(1000), scaled)
   110  
   111  	// Create test future with settlement data type decimal with decimals (that represents a decimal)
   112  	ft = testFuture(t, datapb.PropertyKey_TYPE_DECIMAL)
   113  
   114  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
   115  	dec := num.DecimalFromFloat(10000.00001)
   116  	n.SetDecimal(&dec)
   117  	_, err = ft.future.ScaleSettlementDataToDecimalPlaces(
   118  		n, 3,
   119  	)
   120  	require.ErrorIs(t, products.ErrSettlementDataDecimalsNotSupportedByAsset, err)
   121  }
   122  
   123  func testScalingDownNeededWithPrecisionLoss(t *testing.T) {
   124  	// Create test future with settlement data type integer with decimals (that represents a decimal)
   125  	ft := testFuture(t, datapb.PropertyKey_TYPE_INTEGER)
   126  
   127  	n := &num.Numeric{}
   128  	n.SetUint(num.NewUint(123456))
   129  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
   130  	scaled, err := ft.future.ScaleSettlementDataToDecimalPlaces(
   131  		n, 3,
   132  	)
   133  	require.NoError(t, err)
   134  	require.Equal(t, num.NewUint(1234), scaled)
   135  
   136  	// settlement data is in 5 decimal places, asset in 3 decimal places => x10^-2
   137  	dec := num.DecimalFromFloat(12345.678912)
   138  	n.SetDecimal(&dec)
   139  	_, err = ft.future.ScaleSettlementDataToDecimalPlaces(
   140  		n, 3,
   141  	)
   142  	require.ErrorIs(t, products.ErrSettlementDataDecimalsNotSupportedByAsset, err)
   143  
   144  	dec = num.DecimalFromFloat(12345.000)
   145  	n.SetDecimal(&dec)
   146  	scaled, err = ft.future.ScaleSettlementDataToDecimalPlaces(
   147  		n, 4,
   148  	)
   149  
   150  	require.NoError(t, err)
   151  	require.Equal(t, num.NewUint(123450000), scaled)
   152  }
   153  
   154  func testUpdateFuture(t *testing.T) {
   155  	// Create test future with settlement data type integer with decimals (that represents a decimal)
   156  	ft := testFuture(t, datapb.PropertyKey_TYPE_INTEGER)
   157  
   158  	fp := getTestFutureProd(t, datapb.PropertyKey_TYPE_INTEGER, 10)
   159  
   160  	// two new subscription
   161  	ft.oe.EXPECT().
   162  		Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).
   163  		Times(1).
   164  		Return(subscriptionID(3), func(ctx context.Context, sid spec.SubscriptionID) {}, nil)
   165  	ft.oe.EXPECT().
   166  		Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).
   167  		Times(1).
   168  		Return(subscriptionID(4), func(ctx context.Context, sid spec.SubscriptionID) {}, nil)
   169  
   170  	ft.future.Update(context.Background(), &types.InstrumentFuture{Future: fp}, ft.oe)
   171  
   172  	assert.Equal(t, 2, ft.unsub)
   173  }
   174  
   175  type tstFuture struct {
   176  	oe     *mocks.MockOracleEngine
   177  	future *products.Future
   178  	unsub  int
   179  }
   180  
   181  func (tf *tstFuture) unsubscribe(_ context.Context, _ spec.SubscriptionID) {
   182  	tf.unsub++
   183  }
   184  
   185  func getTestFutureProd(t *testing.T, propertyTpe datapb.PropertyKey_Type, dp uint64) *types.Future {
   186  	t.Helper()
   187  	pubKeys := []*dstypes.Signer{
   188  		dstypes.CreateSignerFromString("0xDEADBEEF", dstypes.SignerTypePubKey),
   189  	}
   190  
   191  	f := &types.Future{
   192  		SettlementAsset: "ETH",
   193  		QuoteName:       "ETH",
   194  		DataSourceSpecForTradingTermination: &datasource.Spec{
   195  			Data: datasource.NewDefinition(
   196  				datasource.ContentTypeOracle,
   197  			).SetOracleConfig(
   198  				&signedoracle.SpecConfiguration{
   199  					Signers: pubKeys,
   200  					Filters: []*dstypes.SpecFilter{
   201  						{
   202  							Key: &dstypes.SpecPropertyKey{
   203  								Name: "trading.termination",
   204  								Type: datapb.PropertyKey_TYPE_BOOLEAN,
   205  							},
   206  							Conditions: nil,
   207  						},
   208  					},
   209  				},
   210  			),
   211  		},
   212  		DataSourceSpecBinding: &datasource.SpecBindingForFuture{
   213  			SettlementDataProperty:     "price.ETH.value",
   214  			TradingTerminationProperty: "trading.termination",
   215  		},
   216  	}
   217  
   218  	f.DataSourceSpecForSettlementData = &datasource.Spec{
   219  		Data: datasource.NewDefinition(
   220  			datasource.ContentTypeOracle,
   221  		).SetOracleConfig(
   222  			&signedoracle.SpecConfiguration{
   223  				Signers: pubKeys,
   224  				Filters: []*dstypes.SpecFilter{
   225  					{
   226  						Key: &dstypes.SpecPropertyKey{
   227  							Name:                "price.ETH.value",
   228  							Type:                propertyTpe,
   229  							NumberDecimalPlaces: &dp,
   230  						},
   231  						Conditions: nil,
   232  					},
   233  				},
   234  			},
   235  		),
   236  	}
   237  
   238  	return f
   239  }
   240  
   241  func testFuture(t *testing.T, propertyTpe datapb.PropertyKey_Type) *tstFuture {
   242  	t.Helper()
   243  
   244  	log := logging.NewTestLogger()
   245  	ctrl := gomock.NewController(t)
   246  	oe := mocks.NewMockOracleEngine(ctrl)
   247  
   248  	var dp uint64 = 5
   249  	f := getTestFutureProd(t, propertyTpe, dp)
   250  
   251  	testFuture := &tstFuture{
   252  		oe: oe,
   253  	}
   254  
   255  	ctx := context.Background()
   256  	oe.EXPECT().
   257  		Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).
   258  		Times(1).
   259  		Return(subscriptionID(1), testFuture.unsubscribe, nil)
   260  
   261  	oe.EXPECT().
   262  		Subscribe(gomock.Any(), gomock.Any(), gomock.Any()).
   263  		Times(1).
   264  		Return(subscriptionID(2), testFuture.unsubscribe, nil)
   265  
   266  	future, err := products.NewFuture(ctx, log, f, oe, uint32(dp))
   267  	if err != nil {
   268  		t.Fatalf("couldn't create a Future for testing: %v", err)
   269  	}
   270  	testFuture.future = future
   271  	return testFuture
   272  }
   273  
   274  func subscriptionID(i uint64) spec.SubscriptionID {
   275  	return spec.SubscriptionID(i)
   276  }