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 }