code.vegaprotocol.io/vega@v0.79.0/core/fee/engine_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 fee_test 17 18 import ( 19 "errors" 20 "testing" 21 22 "code.vegaprotocol.io/vega/core/fee" 23 "code.vegaprotocol.io/vega/core/fee/mocks" 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/golang/mock/gomock" 30 "github.com/stretchr/testify/assert" 31 "github.com/stretchr/testify/require" 32 ) 33 34 const ( 35 testAsset = "ETH" 36 ) 37 38 var ( 39 testFees = types.Fees{ 40 Factors: &types.FeeFactors{ 41 LiquidityFee: num.DecimalFromFloat(0.1), 42 InfrastructureFee: num.DecimalFromFloat(0.05), 43 MakerFee: num.DecimalFromFloat(0.02), 44 }, 45 } 46 extendedTestFees = types.Fees{ 47 Factors: &types.FeeFactors{ 48 LiquidityFee: num.DecimalFromFloat(0.1), 49 InfrastructureFee: num.DecimalFromFloat(0.05), 50 MakerFee: num.DecimalFromFloat(0.02), 51 BuyBackFee: num.DecimalFromFloat(0.002), 52 TreasuryFee: num.DecimalFromFloat(0.003), 53 }, 54 } 55 ) 56 57 type testFee struct { 58 *fee.Engine 59 } 60 61 func getTestFee(t *testing.T) *testFee { 62 t.Helper() 63 eng, err := fee.New( 64 logging.NewTestLogger(), 65 fee.NewDefaultConfig(), 66 testFees, 67 testAsset, 68 num.DecimalFromInt64(1), 69 ) 70 assert.NoError(t, err) 71 return &testFee{eng} 72 } 73 74 func getExtendedTestFee(t *testing.T) *testFee { 75 t.Helper() 76 eng, err := fee.New( 77 logging.NewTestLogger(), 78 fee.NewDefaultConfig(), 79 extendedTestFees, 80 testAsset, 81 num.DecimalFromInt64(1), 82 ) 83 assert.NoError(t, err) 84 return &testFee{eng} 85 } 86 87 func TestFeeEngine(t *testing.T) { 88 t.Run("update fee factors with invalid input", testUpdateFeeFactorsError) 89 t.Run("update fee factors with valid input", testUpdateFeeFactors) 90 t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade) 91 t.Run("calculate continuous trading fee", testCalcContinuousTrading) 92 t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmounts) 93 t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscount) 94 t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade) 95 t.Run("calculate auction trading fee", testCalcAuctionTrading) 96 t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade) 97 t.Run("calculate batch auction trading fee same batch", testCalcBatchAuctionTradingSameBatch) 98 t.Run("calculate batch auction trading fee different batches", testCalcBatchAuctionTradingDifferentBatches) 99 t.Run("Build liquidity fee transfers with remainder", testBuildLiquidityFeesRemainder) 100 t.Run("calculate closeout fees", testCloseoutFees) 101 } 102 103 func TestFeeEngineWithBuyBackAndTreasury(t *testing.T) { 104 t.Run("update fee factors with invalid input", testUpdateExtendedFeeFactorsError) 105 t.Run("update fee factors with valid input", testUpdateExtendedFeeFactors) 106 t.Run("calculate continuous trading fee empty trade", testCalcContinuousTradingErrorEmptyTrade) 107 t.Run("calculate continuous trading fee", testCalcContinuousTradingExtended) 108 t.Run("calculate continuous trading fee + check amounts", testCalcContinuousTradingAndCheckAmountsExtended) 109 t.Run("calculate continuous trading fee + check amounts with discounts and rewards", testCalcContinuousTradingAndCheckAmountsWithDiscountExtended) 110 t.Run("calculate auction trading fee empty trade", testCalcAuctionTradingErrorEmptyTrade) 111 t.Run("calculate auction trading fee", testCalcAuctionTradingExtended) 112 t.Run("calculate batch auction trading fee empty trade", testCalcBatchAuctionTradingErrorEmptyTrade) 113 } 114 115 func testUpdateFeeFactors(t *testing.T) { 116 eng := getTestFee(t) 117 okFees := types.Fees{ 118 Factors: &types.FeeFactors{ 119 LiquidityFee: num.DecimalFromFloat(0.1), 120 InfrastructureFee: num.DecimalFromFloat(0.5), 121 MakerFee: num.DecimalFromFloat(0.25), 122 }, 123 } 124 err := eng.UpdateFeeFactors(okFees) 125 assert.NoError(t, err) 126 } 127 128 func testUpdateExtendedFeeFactors(t *testing.T) { 129 eng := getExtendedTestFee(t) 130 okFees := types.Fees{ 131 Factors: &types.FeeFactors{ 132 LiquidityFee: num.DecimalFromFloat(0.1), 133 InfrastructureFee: num.DecimalFromFloat(0.5), 134 MakerFee: num.DecimalFromFloat(0.25), 135 BuyBackFee: num.DecimalFromFloat(0.3), 136 TreasuryFee: num.DecimalFromFloat(0.4), 137 }, 138 } 139 err := eng.UpdateFeeFactors(okFees) 140 assert.NoError(t, err) 141 } 142 143 func testUpdateFeeFactorsError(t *testing.T) { 144 eng := getTestFee(t) 145 koFees := types.Fees{ 146 Factors: &types.FeeFactors{ 147 LiquidityFee: num.DecimalFromFloat(-.1), 148 InfrastructureFee: num.DecimalFromFloat(0.5), 149 MakerFee: num.DecimalFromFloat(0.25), 150 }, 151 } 152 err := eng.UpdateFeeFactors(koFees) 153 assert.Error(t, err) 154 155 koFees = types.Fees{ 156 Factors: &types.FeeFactors{ 157 LiquidityFee: num.DecimalFromFloat(0.1), 158 InfrastructureFee: num.DecimalFromFloat(-.1), 159 MakerFee: num.DecimalFromFloat(0.25), 160 }, 161 } 162 err = eng.UpdateFeeFactors(koFees) 163 assert.Error(t, err) 164 koFees = types.Fees{ 165 Factors: &types.FeeFactors{ 166 LiquidityFee: num.DecimalFromFloat(0.1), 167 InfrastructureFee: num.DecimalFromFloat(0.5), 168 MakerFee: num.DecimalFromFloat(-.1), 169 }, 170 } 171 err = eng.UpdateFeeFactors(koFees) 172 assert.Error(t, err) 173 } 174 175 func testUpdateExtendedFeeFactorsError(t *testing.T) { 176 eng := getExtendedTestFee(t) 177 koFees := types.Fees{ 178 Factors: &types.FeeFactors{ 179 LiquidityFee: num.DecimalFromFloat(0.1), 180 InfrastructureFee: num.DecimalFromFloat(0.5), 181 MakerFee: num.DecimalFromFloat(0.25), 182 BuyBackFee: num.DecimalFromFloat(-1), 183 TreasuryFee: num.DecimalFromFloat(0.4), 184 }, 185 } 186 err := eng.UpdateFeeFactors(koFees) 187 assert.Error(t, err) 188 189 koFees = types.Fees{ 190 Factors: &types.FeeFactors{ 191 LiquidityFee: num.DecimalFromFloat(0.1), 192 InfrastructureFee: num.DecimalFromFloat(0.11), 193 MakerFee: num.DecimalFromFloat(0.25), 194 BuyBackFee: num.DecimalFromFloat(0.41), 195 TreasuryFee: num.DecimalFromFloat(-1), 196 }, 197 } 198 err = eng.UpdateFeeFactors(koFees) 199 assert.Error(t, err) 200 } 201 202 func testCalcContinuousTradingErrorEmptyTrade(t *testing.T) { 203 eng := getTestFee(t) 204 ctrl := gomock.NewController(t) 205 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 206 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 207 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 208 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 209 210 _, err := eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 211 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 212 213 eng = getExtendedTestFee(t) 214 _, err = eng.CalculateForContinuousMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 215 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 216 } 217 218 func testCalcContinuousTradingAndCheckAmounts(t *testing.T) { 219 eng := getTestFee(t) 220 ctrl := gomock.NewController(t) 221 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 222 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 223 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 224 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 225 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 226 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 227 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 228 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 229 require.NoError(t, eng.UpdateFeeFactors(types.Fees{ 230 Factors: &types.FeeFactors{ 231 MakerFee: num.DecimalFromFloat(.000250), 232 InfrastructureFee: num.DecimalFromFloat(0.0005), 233 LiquidityFee: num.DecimalFromFloat(0.001), 234 }, 235 })) 236 trades := []*types.Trade{ 237 { 238 Aggressor: types.SideSell, 239 Seller: "party1", 240 Buyer: "party2", 241 Size: 5, 242 Price: num.NewUint(100000), 243 }, 244 } 245 246 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 247 assert.NotNil(t, ft) 248 assert.Nil(t, err) 249 transfers := ft.Transfers() 250 var pay, recv, infra, liquidity int 251 for _, v := range transfers { 252 if v.Type == types.TransferTypeLiquidityFeePay { 253 liquidity++ 254 assert.Equal(t, num.NewUint(500), v.Amount.Amount) 255 } 256 if v.Type == types.TransferTypeInfrastructureFeePay { 257 infra++ 258 assert.Equal(t, num.NewUint(250), v.Amount.Amount) 259 } 260 if v.Type == types.TransferTypeMakerFeeReceive { 261 recv++ 262 assert.Equal(t, num.NewUint(125), v.Amount.Amount) 263 } 264 if v.Type == types.TransferTypeMakerFeePay { 265 pay++ 266 assert.Equal(t, num.NewUint(125), v.Amount.Amount) 267 } 268 } 269 270 assert.Equal(t, liquidity, 1) 271 assert.Equal(t, infra, 1) 272 assert.Equal(t, recv, len(trades)) 273 assert.Equal(t, pay, len(trades)) 274 assert.Equal(t, &eventspb.FeesStats{ 275 Market: "", 276 Asset: testAsset, 277 EpochSeq: 0, 278 TotalRewardsReceived: []*eventspb.PartyAmount{}, 279 ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{}, 280 RefereesDiscountApplied: []*eventspb.PartyAmount{ 281 { 282 Party: "party1", 283 Amount: "0", 284 QuantumAmount: "0", 285 }, 286 }, 287 VolumeDiscountApplied: []*eventspb.PartyAmount{ 288 { 289 Party: "party1", 290 Amount: "0", 291 QuantumAmount: "0", 292 }, 293 }, 294 TotalMakerFeesReceived: []*eventspb.PartyAmount{ 295 { 296 Party: "party2", 297 Amount: "125", 298 QuantumAmount: "125", 299 }, 300 }, 301 MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ 302 { 303 Taker: "party1", 304 MakerFeesPaid: []*eventspb.PartyAmount{ 305 { 306 Party: "party2", 307 Amount: "125", 308 QuantumAmount: "125", 309 }, 310 }, 311 }, 312 }, 313 TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ 314 { 315 Party: "party1", 316 Amount: "875", 317 QuantumAmount: "875", 318 }, 319 { 320 Party: "party2", 321 Amount: "125", 322 QuantumAmount: "125", 323 }, 324 }, 325 }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) 326 } 327 328 func testCalcContinuousTradingAndCheckAmountsExtended(t *testing.T) { 329 eng := getExtendedTestFee(t) 330 ctrl := gomock.NewController(t) 331 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 332 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 333 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 334 volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.0025)).AnyTimes() 335 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 336 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 337 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 338 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 339 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 340 require.NoError(t, eng.UpdateFeeFactors(types.Fees{ 341 Factors: &types.FeeFactors{ 342 MakerFee: num.DecimalFromFloat(.000250), 343 InfrastructureFee: num.DecimalFromFloat(0.0005), 344 LiquidityFee: num.DecimalFromFloat(0.001), 345 BuyBackFee: num.DecimalFromFloat(0.002), 346 TreasuryFee: num.DecimalFromFloat(0.003), 347 }, 348 })) 349 trades := []*types.Trade{ 350 { 351 Aggressor: types.SideSell, 352 Seller: "party1", 353 Buyer: "party2", 354 Size: 5, 355 Price: num.NewUint(100000), 356 }, 357 } 358 359 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 360 assert.NotNil(t, ft) 361 assert.Nil(t, err) 362 transfers := ft.Transfers() 363 var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int 364 for _, v := range transfers { 365 if v.Type == types.TransferTypeLiquidityFeePay { 366 liquidity++ 367 assert.Equal(t, num.NewUint(500), v.Amount.Amount) 368 } 369 if v.Type == types.TransferTypeInfrastructureFeePay { 370 infra++ 371 assert.Equal(t, num.NewUint(250), v.Amount.Amount) 372 } 373 if v.Type == types.TransferTypeMakerFeeReceive { 374 recv++ 375 assert.Equal(t, num.NewUint(125), v.Amount.Amount) 376 } 377 if v.Type == types.TransferTypeMakerFeePay { 378 pay++ 379 assert.Equal(t, num.NewUint(125), v.Amount.Amount) 380 } 381 if v.Type == types.TransferTypeBuyBackFeePay { 382 bb++ 383 assert.Equal(t, num.NewUint(500), v.Amount.Amount) 384 } 385 if v.Type == types.TransferTypeTreasuryPay { 386 treasury++ 387 assert.Equal(t, num.NewUint(750), v.Amount.Amount) 388 } 389 if v.Type == types.TransferTypeHighMakerRebatePay { 390 hmp++ 391 assert.Equal(t, num.NewUint(1250), v.Amount.Amount) 392 } 393 if v.Type == types.TransferTypeHighMakerRebateReceive { 394 hmr++ 395 assert.Equal(t, num.NewUint(1250), v.Amount.Amount) 396 } 397 } 398 399 assert.Equal(t, liquidity, 1) 400 assert.Equal(t, infra, 1) 401 assert.Equal(t, bb, 1) 402 assert.Equal(t, treasury, 1) 403 assert.Equal(t, hmp, 1) 404 assert.Equal(t, hmr, 1) 405 assert.Equal(t, recv, len(trades)) 406 assert.Equal(t, pay, len(trades)) 407 assert.Equal(t, &eventspb.FeesStats{ 408 Market: "", 409 Asset: testAsset, 410 EpochSeq: 0, 411 TotalRewardsReceived: []*eventspb.PartyAmount{}, 412 ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{}, 413 RefereesDiscountApplied: []*eventspb.PartyAmount{ 414 { 415 Party: "party1", 416 Amount: "0", 417 QuantumAmount: "0", 418 }, 419 }, 420 VolumeDiscountApplied: []*eventspb.PartyAmount{ 421 { 422 Party: "party1", 423 Amount: "0", 424 QuantumAmount: "0", 425 }, 426 }, 427 TotalMakerFeesReceived: []*eventspb.PartyAmount{ 428 { 429 Party: "party2", 430 Amount: "125", 431 QuantumAmount: "125", 432 }, 433 }, 434 MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ 435 { 436 Taker: "party1", 437 MakerFeesPaid: []*eventspb.PartyAmount{ 438 { 439 Party: "party2", 440 Amount: "125", 441 QuantumAmount: "125", 442 }, 443 }, 444 }, 445 }, 446 TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ 447 { 448 Party: "party1", 449 Amount: "3375", 450 QuantumAmount: "3375", 451 }, 452 { 453 Party: "party2", 454 Amount: "125", 455 QuantumAmount: "125", 456 }, 457 }, 458 }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) 459 } 460 461 func testCalcContinuousTradingAndCheckAmountsWithDiscount(t *testing.T) { 462 eng := getTestFee(t) 463 ctrl := gomock.NewController(t) 464 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 465 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 466 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 467 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 468 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ 469 Infra: num.NewDecimalFromFloat(0.3), 470 Maker: num.NewDecimalFromFloat(0.3), 471 Liquidity: num.NewDecimalFromFloat(0.3), 472 }).AnyTimes() 473 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ 474 Infra: num.NewDecimalFromFloat(0.2), 475 Maker: num.NewDecimalFromFloat(0.2), 476 Liquidity: num.NewDecimalFromFloat(0.2), 477 }).AnyTimes() 478 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return( 479 types.Factors{ 480 Infra: num.NewDecimalFromFloat(0.1), 481 Maker: num.NewDecimalFromFloat(0.1), 482 Liquidity: num.NewDecimalFromFloat(0.1), 483 }) 484 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes() 485 require.NoError(t, eng.UpdateFeeFactors(types.Fees{ 486 Factors: &types.FeeFactors{ 487 MakerFee: num.DecimalFromFloat(.000250), 488 InfrastructureFee: num.DecimalFromFloat(0.0005), 489 LiquidityFee: num.DecimalFromFloat(0.001), 490 }, 491 })) 492 trades := []*types.Trade{ 493 { 494 Aggressor: types.SideSell, 495 Seller: "party1", 496 Buyer: "party2", 497 Size: 5, 498 Price: num.NewUint(100000), 499 }, 500 } 501 502 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 503 assert.NotNil(t, ft) 504 assert.Nil(t, err) 505 transfers := ft.Transfers() 506 var pay, recv, infra, liquidity int 507 for _, v := range transfers { 508 if v.Type == types.TransferTypeLiquidityFeePay { 509 liquidity++ 510 assert.Equal(t, num.NewUint(252), v.Amount.Amount) 511 } 512 if v.Type == types.TransferTypeInfrastructureFeePay { 513 infra++ 514 assert.Equal(t, num.NewUint(127), v.Amount.Amount) 515 } 516 if v.Type == types.TransferTypeMakerFeeReceive { 517 recv++ 518 assert.Equal(t, num.NewUint(64), v.Amount.Amount) 519 } 520 if v.Type == types.TransferTypeMakerFeePay { 521 pay++ 522 assert.Equal(t, num.NewUint(64), v.Amount.Amount) 523 } 524 } 525 526 assert.Equal(t, liquidity, 1) 527 assert.Equal(t, infra, 1) 528 assert.Equal(t, recv, len(trades)) 529 assert.Equal(t, pay, len(trades)) 530 assert.Equal(t, &eventspb.FeesStats{ 531 Asset: testAsset, 532 TotalRewardsReceived: []*eventspb.PartyAmount{ 533 { 534 Party: "party3", 535 Amount: "110", 536 QuantumAmount: "110", 537 }, 538 }, 539 ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{ 540 { 541 Referrer: "party3", 542 GeneratedReward: []*eventspb.PartyAmount{ 543 { 544 Party: "party1", 545 Amount: "110", 546 QuantumAmount: "110", 547 }, 548 }, 549 }, 550 }, 551 RefereesDiscountApplied: []*eventspb.PartyAmount{ 552 { 553 Party: "party1", 554 Amount: "262", 555 QuantumAmount: "262", 556 }, 557 }, 558 VolumeDiscountApplied: []*eventspb.PartyAmount{ 559 { 560 Party: "party1", 561 Amount: "60", 562 QuantumAmount: "60", 563 }, 564 }, 565 TotalMakerFeesReceived: []*eventspb.PartyAmount{ 566 { 567 Party: "party2", 568 Amount: "64", 569 QuantumAmount: "64", 570 }, 571 }, 572 MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ 573 { 574 Taker: "party1", 575 MakerFeesPaid: []*eventspb.PartyAmount{ 576 { 577 Party: "party2", 578 Amount: "64", 579 QuantumAmount: "64", 580 }, 581 }, 582 }, 583 }, 584 TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ 585 { 586 Party: "party1", 587 Amount: "443", 588 QuantumAmount: "443", 589 }, 590 { 591 Party: "party2", 592 Amount: "64", 593 QuantumAmount: "64", 594 }, 595 }, 596 }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) 597 } 598 599 func testCalcContinuousTradingAndCheckAmountsWithDiscountExtended(t *testing.T) { 600 eng := getExtendedTestFee(t) 601 ctrl := gomock.NewController(t) 602 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 603 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 604 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 605 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes() 606 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ 607 Infra: num.NewDecimalFromFloat(0.3), 608 Maker: num.NewDecimalFromFloat(0.3), 609 Liquidity: num.NewDecimalFromFloat(0.3), 610 }).AnyTimes() 611 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ 612 Infra: num.NewDecimalFromFloat(0.2), 613 Maker: num.NewDecimalFromFloat(0.2), 614 Liquidity: num.NewDecimalFromFloat(0.2), 615 }).AnyTimes() 616 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return( 617 types.Factors{ 618 Infra: num.NewDecimalFromFloat(0.1), 619 Maker: num.NewDecimalFromFloat(0.1), 620 Liquidity: num.NewDecimalFromFloat(0.1), 621 }) 622 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party3"), nil).AnyTimes() 623 require.NoError(t, eng.UpdateFeeFactors(types.Fees{ 624 Factors: &types.FeeFactors{ 625 MakerFee: num.DecimalFromFloat(.000250), 626 InfrastructureFee: num.DecimalFromFloat(0.0005), 627 LiquidityFee: num.DecimalFromFloat(0.001), 628 BuyBackFee: num.DecimalFromFloat(0.002), 629 TreasuryFee: num.DecimalFromFloat(0.003), 630 }, 631 })) 632 trades := []*types.Trade{ 633 { 634 Aggressor: types.SideSell, 635 Seller: "party1", 636 Buyer: "party2", 637 Size: 5, 638 Price: num.NewUint(100000), 639 }, 640 } 641 642 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 643 assert.NotNil(t, ft) 644 assert.Nil(t, err) 645 transfers := ft.Transfers() 646 var pay, recv, infra, liquidity, bb, treasury, hmp, hmr int 647 for _, v := range transfers { 648 if v.Type == types.TransferTypeLiquidityFeePay { 649 liquidity++ 650 assert.Equal(t, num.NewUint(252), v.Amount.Amount) 651 } 652 if v.Type == types.TransferTypeInfrastructureFeePay { 653 infra++ 654 assert.Equal(t, num.NewUint(127), v.Amount.Amount) 655 } 656 if v.Type == types.TransferTypeMakerFeeReceive { 657 recv++ 658 assert.Equal(t, num.NewUint(64), v.Amount.Amount) 659 } 660 if v.Type == types.TransferTypeMakerFeePay { 661 pay++ 662 assert.Equal(t, num.NewUint(64), v.Amount.Amount) 663 } 664 if v.Type == types.TransferTypeBuyBackFeePay { 665 bb++ 666 assert.Equal(t, num.NewUint(500), v.Amount.Amount) 667 } 668 if v.Type == types.TransferTypeTreasuryPay { 669 treasury++ 670 assert.Equal(t, num.NewUint(750), v.Amount.Amount) 671 } 672 if v.Type == types.TransferTypeHighMakerRebatePay { 673 hmp++ 674 assert.Equal(t, num.NewUint(1250), v.Amount.Amount) 675 } 676 if v.Type == types.TransferTypeHighMakerRebateReceive { 677 hmr++ 678 assert.Equal(t, num.NewUint(1250), v.Amount.Amount) 679 } 680 } 681 682 assert.Equal(t, liquidity, 1) 683 assert.Equal(t, infra, 1) 684 assert.Equal(t, bb, 1) 685 assert.Equal(t, treasury, 1) 686 assert.Equal(t, hmp, 1) 687 assert.Equal(t, hmr, 1) 688 assert.Equal(t, recv, len(trades)) 689 assert.Equal(t, pay, len(trades)) 690 assert.Equal(t, &eventspb.FeesStats{ 691 Asset: testAsset, 692 TotalRewardsReceived: []*eventspb.PartyAmount{ 693 { 694 Party: "party3", 695 Amount: "110", 696 QuantumAmount: "110", 697 }, 698 }, 699 ReferrerRewardsGenerated: []*eventspb.ReferrerRewardsGenerated{ 700 { 701 Referrer: "party3", 702 GeneratedReward: []*eventspb.PartyAmount{ 703 { 704 Party: "party1", 705 Amount: "110", 706 QuantumAmount: "110", 707 }, 708 }, 709 }, 710 }, 711 RefereesDiscountApplied: []*eventspb.PartyAmount{ 712 { 713 Party: "party1", 714 Amount: "262", 715 QuantumAmount: "262", 716 }, 717 }, 718 VolumeDiscountApplied: []*eventspb.PartyAmount{ 719 { 720 Party: "party1", 721 Amount: "60", 722 QuantumAmount: "60", 723 }, 724 }, 725 TotalMakerFeesReceived: []*eventspb.PartyAmount{ 726 { 727 Party: "party2", 728 Amount: "64", 729 QuantumAmount: "64", 730 }, 731 }, 732 MakerFeesGenerated: []*eventspb.MakerFeesGenerated{ 733 { 734 Taker: "party1", 735 MakerFeesPaid: []*eventspb.PartyAmount{ 736 { 737 Party: "party2", 738 Amount: "64", 739 QuantumAmount: "64", 740 }, 741 }, 742 }, 743 }, 744 TotalFeesPaidAndReceived: []*eventspb.PartyAmount{ 745 { 746 Party: "party1", 747 Amount: "2943", 748 QuantumAmount: "2943", 749 }, 750 { 751 Party: "party2", 752 Amount: "64", 753 QuantumAmount: "64", 754 }, 755 }, 756 }, eng.GetFeesStatsOnEpochEnd(num.DecimalFromInt64(1))) 757 } 758 759 func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t *testing.T, aggressorSide types.Side) { 760 t.Helper() 761 eng := getTestFee(t) 762 ctrl := gomock.NewController(t) 763 764 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 765 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 766 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 767 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 768 769 eng.UpdateFeeFactors(types.Fees{ 770 Factors: &types.FeeFactors{ 771 MakerFee: num.DecimalFromFloat(.000250), 772 InfrastructureFee: num.DecimalFromFloat(0.0005), 773 LiquidityFee: num.DecimalFromFloat(0.001), 774 }, 775 }) 776 777 trades := []*types.Trade{ 778 { 779 Aggressor: aggressorSide, 780 Seller: "party1", 781 Buyer: "party2", 782 Size: 5, 783 Price: num.NewUint(100000), 784 }, 785 } 786 787 aggressor := "party1" 788 if aggressorSide == types.SideBuy { 789 aggressor = "party2" 790 } 791 792 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ 793 Infra: num.NewDecimalFromFloat(0.5), 794 Maker: num.NewDecimalFromFloat(0.5), 795 Liquidity: num.NewDecimalFromFloat(0.5), 796 }).AnyTimes() 797 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ 798 Infra: num.NewDecimalFromFloat(0.25), 799 Maker: num.NewDecimalFromFloat(0.25), 800 Liquidity: num.NewDecimalFromFloat(0.25), 801 }) 802 discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() 803 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ 804 Infra: num.NewDecimalFromFloat(0.3), 805 Maker: num.NewDecimalFromFloat(0.3), 806 Liquidity: num.NewDecimalFromFloat(0.3), 807 }).AnyTimes() 808 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.Factors{ 809 Infra: num.NewDecimalFromFloat(0.3), 810 Maker: num.NewDecimalFromFloat(0.3), 811 Liquidity: num.NewDecimalFromFloat(0.3), 812 }).AnyTimes() 813 814 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 815 assert.NotNil(t, ft) 816 assert.Nil(t, err) 817 transfers := ft.Transfers() 818 var pay, recv, infra, liquidity, reward int 819 820 for _, v := range transfers { 821 if v.Type == types.TransferTypeLiquidityFeePay { 822 liquidity++ 823 // lf = 500 before discounts and rewards 824 // lf = 500 - 0.5 * 500 = 250 after applying referral discount 825 // lf = 250 - 0.25 * 250 = 250 - 62 = 188 826 // applying rewards 827 // lf = 188 - 188 * 0.3 = 188 - 56 = 132 828 assert.Equal(t, num.NewUint(132), v.Amount.Amount) 829 } 830 if v.Type == types.TransferTypeFeeReferrerRewardPay { 831 reward++ 832 // 14 + 56 + 31 = 96 833 require.Equal(t, num.NewUint(98), v.Amount.Amount) 834 } 835 if v.Type == types.TransferTypeFeeReferrerRewardDistribute { 836 reward++ 837 } 838 if v.Type == types.TransferTypeInfrastructureFeePay { 839 infra++ 840 // inf = 250 before discounts and rewards 841 // inf = 250 - 0.5*250 = 125 after applying referral discount 842 // inf = 125 - 0.25*125 = 125-31 = 94 843 // applying rewards 844 // inf = 94 - 94 *0.3 = 66 845 assert.Equal(t, num.NewUint(66), v.Amount.Amount) 846 } 847 if v.Type == types.TransferTypeMakerFeePay { 848 pay++ 849 // mf = 125 before discounts and rewards 850 // inf = 125 - 0.5*125 = 63 after applying referral discount 851 // inf = 63 - 0.25*63 = 63-15 = 48 852 // applying rewards 853 // inf = 48 - 48 *0.3 = 48 - 14 = 34 854 assert.Equal(t, num.NewUint(34), v.Amount.Amount) 855 } 856 if v.Type == types.TransferTypeMakerFeeReceive { 857 recv++ 858 assert.Equal(t, num.NewUint(34), v.Amount.Amount) 859 } 860 } 861 862 assert.Equal(t, liquidity, 1) 863 assert.Equal(t, infra, 1) 864 assert.Equal(t, recv, len(trades)) 865 assert.Equal(t, pay, len(trades)) 866 assert.Equal(t, reward, 2) 867 } 868 869 func testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t *testing.T, aggressorSide types.Side) { 870 t.Helper() 871 eng := getTestFee(t) 872 ctrl := gomock.NewController(t) 873 874 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 875 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 876 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 877 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 878 879 eng.UpdateFeeFactors(types.Fees{ 880 Factors: &types.FeeFactors{ 881 MakerFee: num.DecimalFromFloat(.000250), 882 InfrastructureFee: num.DecimalFromFloat(0.0005), 883 LiquidityFee: num.DecimalFromFloat(0.001), 884 }, 885 }) 886 887 trades := []*types.Trade{ 888 { 889 Aggressor: aggressorSide, 890 Seller: "party1", 891 Buyer: "party2", 892 Size: 1, 893 Price: num.NewUint(100000), 894 }, 895 { 896 Aggressor: aggressorSide, 897 Seller: "party1", 898 Buyer: "party3", 899 Size: 2, 900 Price: num.NewUint(100000), 901 }, 902 { 903 Aggressor: aggressorSide, 904 Seller: "party1", 905 Buyer: "party2", 906 Size: 2, 907 Price: num.NewUint(100000), 908 }, 909 } 910 911 aggressor := "party1" 912 if aggressorSide == types.SideBuy { 913 aggressor = "party2" 914 } 915 916 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.Factors{ 917 Infra: num.NewDecimalFromFloat(0.5), 918 Maker: num.NewDecimalFromFloat(0.5), 919 Liquidity: num.NewDecimalFromFloat(0.5), 920 }).AnyTimes() 921 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.Factors{ 922 Infra: num.NewDecimalFromFloat(0.25), 923 Maker: num.NewDecimalFromFloat(0.25), 924 Liquidity: num.NewDecimalFromFloat(0.25), 925 }).AnyTimes() 926 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("referrer"), nil).AnyTimes() 927 discountRewardService.EXPECT().GetReferrer(types.PartyID(aggressor)).Return(types.PartyID("referrer"), nil).AnyTimes() 928 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(aggressor).Return(types.Factors{ 929 Infra: num.NewDecimalFromFloat(0.3), 930 Maker: num.NewDecimalFromFloat(0.3), 931 Liquidity: num.NewDecimalFromFloat(0.3), 932 }).AnyTimes() 933 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.Factors{ 934 Infra: num.NewDecimalFromFloat(0.3), 935 Maker: num.NewDecimalFromFloat(0.3), 936 Liquidity: num.NewDecimalFromFloat(0.3), 937 }).AnyTimes() 938 939 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 940 assert.NotNil(t, ft) 941 assert.Nil(t, err) 942 transfers := ft.Transfers() 943 var pay, recv, infra, liquidity, reward int 944 totalPaidMakerFee := num.UintZero() 945 totalReceivedMakerFee := num.UintZero() 946 947 for _, v := range transfers { 948 if v.Type == types.TransferTypeLiquidityFeePay { 949 liquidity++ 950 // lf1 = 100 - 0.5 * 100 = 50 951 // lf1 = 50 - 0.25 * 50 = 50-12 = 38 952 // lf1 = 38 - 38 * 0.3 = 38 - 11 = 27 953 954 // lf2 = 200 - 0.5 * 200 = 100 955 // lf2 = 100 - 0.25 * 100 = 75 956 // lf2 = 75 - 75 * 0.3 = 75 - 22 = 53 957 958 // lf3 = 200 - 0.5 * 200 = 100 959 // lf3 = 100 - 0.25 * 100 = 75 960 // lf3 = 75 - 75 * 0.3 = 75 - 22 = 53 961 assert.Equal(t, num.NewUint(133), v.Amount.Amount) 962 } 963 if v.Type == types.TransferTypeInfrastructureFeePay { 964 infra++ 965 // inf1 = 50 - 0.5 * 50 = 25 966 // inf1 = 25 - 0.25 * 25 = 25-6 = 19 967 // inf1 = 19 - 19 * 0.3 = 19 - 5 = 14 968 969 // inf2 = 100 - 0.5 * 100 = 50 970 // inf2 = 50 - 0.25 * 50 = 50-12 = 38 971 // inf2 = 38 - 38 * 0.3 = 38 - 11 = 27 972 973 // inf3 = 100 - 0.5 * 100 = 50 974 // inf3 = 50 - 0.25 * 50 = 50-12 = 38 975 // inf3 = 38 - 38 * 0.3 = 38 - 11 = 27 976 assert.Equal(t, num.NewUint(68), v.Amount.Amount) 977 } 978 if v.Type == types.TransferTypeMakerFeePay { 979 pay++ 980 totalPaidMakerFee.AddSum(v.Amount.Amount) 981 } 982 if v.Type == types.TransferTypeMakerFeeReceive { 983 recv++ 984 totalReceivedMakerFee.AddSum(v.Amount.Amount) 985 } 986 if v.Type == types.TransferTypeFeeReferrerRewardPay { 987 reward++ 988 // 55 + 27 + 13 989 assert.Equal(t, num.NewUint(95), v.Amount.Amount) 990 } 991 if v.Type == types.TransferTypeFeeReferrerRewardDistribute { 992 reward++ 993 // 55 + 27 + 13 994 assert.Equal(t, num.NewUint(95), v.Amount.Amount) 995 } 996 } 997 998 // mf1 = 25 - 0.5 * 25 = 13 999 // mf1 = 13 - 0.25 * 13 = 13-3=10 1000 // mf1 = 10 - 10 * 0.3 = 10 - 3 = 7 1001 // mf2 = 50 - 0.5 * 50 = 25 1002 // mf2 = 25 - 0.25 * 25 = 25-6 = 19 1003 // mf2 = 19 - 19 * 0.3 = 19 - 5 = 14 1004 // mf3 = 50 - 0.5 * 50 = 25 1005 // mf3 = 25 - 0.25 * 25 = 25-6 = 19 1006 // mf3 = 19 - 19 * 0.3 = 19 - 5 = 14 1007 assert.Equal(t, num.NewUint(35), totalPaidMakerFee) 1008 assert.Equal(t, num.NewUint(35), totalReceivedMakerFee) 1009 1010 assert.Equal(t, liquidity, 1) 1011 assert.Equal(t, infra, 1) 1012 assert.Equal(t, recv, len(trades)) 1013 assert.Equal(t, pay, len(trades)) 1014 assert.Equal(t, reward, 2) 1015 } 1016 1017 func TestCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewards(t *testing.T) { 1018 testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t, types.SideSell) 1019 testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySide(t, types.SideBuy) 1020 } 1021 1022 func TestCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsMultiMakers(t *testing.T) { 1023 testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t, types.SideSell) 1024 testCalcContinuousTradingAndCheckAmountsWithDiscountsAndRewardsBySideMultipeMakers(t, types.SideBuy) 1025 } 1026 1027 func testBuildLiquidityFeesRemainder(t *testing.T) { 1028 eng := getTestFee(t) 1029 shares := map[string]num.Decimal{ 1030 "lp1": num.DecimalFromFloat(0.8), 1031 "lp2": num.DecimalFromFloat(0.15), 1032 "lp3": num.DecimalFromFloat(0.05), 1033 } 1034 // amount to distribute 1035 acc := &types.Account{ 1036 Balance: num.NewUint(1002), 1037 } 1038 // 1002 * .8 = 801.6 == 801 1039 // 1002 * .15 = 150.3 = 150 1040 // 1002 * 0.05 = 50.1 = 50 1041 // 801 + 150 + 50 = 1001 -> remainder is 1 1042 expRemainder := num.NewUint(1) 1043 expFees := map[string]*num.Uint{ 1044 "lp1": num.NewUint(801), 1045 "lp2": num.NewUint(150), 1046 "lp3": num.NewUint(50), 1047 } 1048 ft := eng.BuildLiquidityFeeDistributionTransfer(shares, acc) 1049 got := ft.TotalFeesAmountPerParty() 1050 for p, amt := range got { 1051 require.True(t, amt.EQ(expFees[p])) 1052 } 1053 // get the total transfer amount from the transfers 1054 total := num.UintZero() 1055 for _, t := range ft.Transfers() { 1056 total.AddSum(t.Amount.Amount) 1057 } 1058 rem := num.UintZero().Sub(acc.Balance, total) 1059 require.True(t, rem.EQ(expRemainder)) 1060 } 1061 1062 func testCalcContinuousTrading(t *testing.T) { 1063 eng := getTestFee(t) 1064 ctrl := gomock.NewController(t) 1065 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1066 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1067 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1068 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1069 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1070 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1071 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1072 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes() 1073 1074 trades := []*types.Trade{ 1075 { 1076 Aggressor: types.SideSell, 1077 Seller: "party1", 1078 Buyer: "party2", 1079 Size: 10, 1080 Price: num.NewUint(10000), 1081 }, 1082 { 1083 Aggressor: types.SideSell, 1084 Seller: "party1", 1085 Buyer: "party3", 1086 Size: 1, 1087 Price: num.NewUint(10300), 1088 }, 1089 { 1090 Aggressor: types.SideSell, 1091 Seller: "party1", 1092 Buyer: "party4", 1093 Size: 7, 1094 Price: num.NewUint(10300), 1095 }, 1096 { 1097 Aggressor: types.SideSell, 1098 Seller: "party1", 1099 Buyer: "party2", 1100 Size: 2, 1101 Price: num.NewUint(10500), 1102 }, 1103 { 1104 Aggressor: types.SideSell, 1105 Seller: "party1", 1106 Buyer: "party5", 1107 Size: 5, 1108 Price: num.NewUint(11000), 1109 }, 1110 } 1111 1112 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1113 assert.NotNil(t, ft) 1114 assert.Nil(t, err) 1115 1116 // get the amounts map 1117 feeAmounts := ft.TotalFeesAmountPerParty() 1118 party1Amount, ok := feeAmounts["party1"] 1119 assert.True(t, ok) 1120 assert.Equal(t, num.NewUint(43928), party1Amount) 1121 1122 // get the transfer and check we have enough of each types 1123 transfers := ft.Transfers() 1124 var pay, recv, infra, liquidity int 1125 for _, v := range transfers { 1126 if v.Type == types.TransferTypeLiquidityFeePay { 1127 liquidity++ 1128 } 1129 if v.Type == types.TransferTypeInfrastructureFeePay { 1130 infra++ 1131 } 1132 if v.Type == types.TransferTypeMakerFeeReceive { 1133 recv++ 1134 } 1135 if v.Type == types.TransferTypeMakerFeePay { 1136 pay++ 1137 } 1138 } 1139 1140 assert.Equal(t, liquidity, 1) 1141 assert.Equal(t, infra, 1) 1142 assert.Equal(t, recv, len(trades)) 1143 assert.Equal(t, pay, len(trades)) 1144 } 1145 1146 func testCalcContinuousTradingExtended(t *testing.T) { 1147 eng := getExtendedTestFee(t) 1148 ctrl := gomock.NewController(t) 1149 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1150 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1151 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1152 volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party2")).Return(num.DecimalFromFloat(0.00005)).AnyTimes() 1153 volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party3")).Return(num.DecimalZero()).AnyTimes() 1154 volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party4")).Return(num.DecimalZero()).AnyTimes() 1155 volumeRebateService.EXPECT().VolumeRebateFactorForParty(types.PartyID("party5")).Return(num.DecimalZero()).AnyTimes() 1156 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1157 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1158 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1159 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID("party1"), errors.New("not a referrer")).AnyTimes() 1160 1161 trades := []*types.Trade{ 1162 { 1163 Aggressor: types.SideSell, 1164 Seller: "party1", 1165 Buyer: "party2", 1166 Size: 10, 1167 Price: num.NewUint(10000), 1168 }, 1169 { 1170 Aggressor: types.SideSell, 1171 Seller: "party1", 1172 Buyer: "party3", 1173 Size: 1, 1174 Price: num.NewUint(10300), 1175 }, 1176 { 1177 Aggressor: types.SideSell, 1178 Seller: "party1", 1179 Buyer: "party4", 1180 Size: 7, 1181 Price: num.NewUint(10300), 1182 }, 1183 { 1184 Aggressor: types.SideSell, 1185 Seller: "party1", 1186 Buyer: "party2", 1187 Size: 2, 1188 Price: num.NewUint(10500), 1189 }, 1190 { 1191 Aggressor: types.SideSell, 1192 Seller: "party1", 1193 Buyer: "party5", 1194 Size: 5, 1195 Price: num.NewUint(11000), 1196 }, 1197 } 1198 1199 ft, err := eng.CalculateForContinuousMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1200 assert.NotNil(t, ft) 1201 assert.Nil(t, err) 1202 1203 // get the amounts map 1204 feeAmounts := ft.TotalFeesAmountPerParty() 1205 party1Amount, ok := feeAmounts["party1"] 1206 assert.True(t, ok) 1207 assert.Equal(t, num.NewUint(45221), party1Amount) 1208 1209 // get the transfer and check we have enough of each types 1210 transfers := ft.Transfers() 1211 var pay, recv, infra, liquidity, highMakerPay, highMakerReceive, bb, treasury int 1212 for _, v := range transfers { 1213 if v.Type == types.TransferTypeLiquidityFeePay { 1214 liquidity++ 1215 } 1216 if v.Type == types.TransferTypeInfrastructureFeePay { 1217 infra++ 1218 } 1219 if v.Type == types.TransferTypeMakerFeeReceive { 1220 recv++ 1221 } 1222 if v.Type == types.TransferTypeMakerFeePay { 1223 pay++ 1224 } 1225 if v.Type == types.TransferTypeHighMakerRebatePay { 1226 highMakerPay++ 1227 } 1228 if v.Type == types.TransferTypeHighMakerRebateReceive { 1229 highMakerReceive++ 1230 } 1231 if v.Type == types.TransferTypeBuyBackFeePay { 1232 bb++ 1233 } 1234 if v.Type == types.TransferTypeTreasuryPay { 1235 treasury++ 1236 } 1237 } 1238 1239 assert.Equal(t, liquidity, 1) 1240 assert.Equal(t, infra, 1) 1241 assert.Equal(t, highMakerPay, 2) 1242 assert.Equal(t, highMakerReceive, 2) 1243 assert.Equal(t, recv, len(trades)) 1244 assert.Equal(t, pay, len(trades)) 1245 assert.Equal(t, bb, len(trades)) 1246 assert.Equal(t, treasury, len(trades)) 1247 } 1248 1249 func testCalcAuctionTradingErrorEmptyTrade(t *testing.T) { 1250 eng := getTestFee(t) 1251 ctrl := gomock.NewController(t) 1252 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1253 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1254 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1255 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1256 _, err := eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 1257 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 1258 1259 eng = getExtendedTestFee(t) 1260 _, err = eng.CalculateForAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 1261 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 1262 } 1263 1264 func testCalcAuctionTrading(t *testing.T) { 1265 eng := getTestFee(t) 1266 ctrl := gomock.NewController(t) 1267 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1268 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1269 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1270 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1271 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1272 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1273 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1274 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 1275 trades := []*types.Trade{ 1276 { 1277 Aggressor: types.SideSell, 1278 Seller: "party1", 1279 Buyer: "party2", 1280 Size: 1, 1281 Price: num.NewUint(100), 1282 }, 1283 } 1284 1285 ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1286 assert.NotNil(t, ft) 1287 assert.Nil(t, err) 1288 1289 // get the amounts map 1290 feeAmounts := ft.TotalFeesAmountPerParty() 1291 // fees are (100 * 0.1 + 100 * 0.05) = 15 1292 // 15 / 2 = 7.5 1293 // internally the engine Ceil all fees. 1294 // so here we will expect 8 for each 1295 party1Amount, ok := feeAmounts["party1"] 1296 assert.True(t, ok) 1297 assert.Equal(t, num.NewUint(8), party1Amount) 1298 party2Amount, ok := feeAmounts["party2"] 1299 assert.True(t, ok) 1300 assert.Equal(t, num.NewUint(8), party2Amount) 1301 1302 // get the transfer and check we have enough of each types 1303 transfers := ft.Transfers() 1304 var pay, recv, infra, liquidity int 1305 for _, v := range transfers { 1306 if v.Type == types.TransferTypeLiquidityFeePay { 1307 liquidity++ 1308 } 1309 if v.Type == types.TransferTypeInfrastructureFeePay { 1310 infra++ 1311 } 1312 if v.Type == types.TransferTypeMakerFeeReceive { 1313 recv++ 1314 } 1315 if v.Type == types.TransferTypeMakerFeePay { 1316 pay++ 1317 } 1318 } 1319 1320 assert.Equal(t, liquidity, 2) 1321 assert.Equal(t, infra, 2) 1322 assert.Equal(t, recv, 0) 1323 assert.Equal(t, pay, 0) 1324 } 1325 1326 func testCalcAuctionTradingExtended(t *testing.T) { 1327 eng := getExtendedTestFee(t) 1328 ctrl := gomock.NewController(t) 1329 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1330 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1331 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1332 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalFromFloat(0.0025)).AnyTimes() 1333 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1334 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1335 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1336 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 1337 trades := []*types.Trade{ 1338 { 1339 Aggressor: types.SideSell, 1340 Seller: "party1", 1341 Buyer: "party2", 1342 Size: 1, 1343 Price: num.NewUint(100), 1344 }, 1345 } 1346 1347 ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1348 assert.NotNil(t, ft) 1349 assert.Nil(t, err) 1350 1351 // get the amounts map 1352 feeAmounts := ft.TotalFeesAmountPerParty() 1353 // fees are (100 * 0.1 + 100 * 0.05) = 15 1354 // 15 / 2 = 7.5 1355 // internally the engine Ceil all fees. 1356 // so here we will expect 8 for each 1357 party1Amount, ok := feeAmounts["party1"] 1358 assert.True(t, ok) 1359 assert.Equal(t, num.NewUint(8), party1Amount) 1360 party2Amount, ok := feeAmounts["party2"] 1361 assert.True(t, ok) 1362 assert.Equal(t, num.NewUint(8), party2Amount) 1363 1364 // get the transfer and check we have enough of each types 1365 transfers := ft.Transfers() 1366 var pay, recv, infra, liquidity, hmp, hmr, bb, treasury int 1367 for _, v := range transfers { 1368 if v.Type == types.TransferTypeLiquidityFeePay { 1369 liquidity++ 1370 } 1371 if v.Type == types.TransferTypeInfrastructureFeePay { 1372 infra++ 1373 } 1374 if v.Type == types.TransferTypeMakerFeeReceive { 1375 recv++ 1376 } 1377 if v.Type == types.TransferTypeMakerFeePay { 1378 pay++ 1379 } 1380 if v.Type == types.TransferTypeHighMakerRebatePay { 1381 hmp++ 1382 } 1383 if v.Type == types.TransferTypeHighMakerRebateReceive { 1384 hmr++ 1385 } 1386 if v.Type == types.TransferTypeBuyBackFeePay { 1387 bb++ 1388 } 1389 if v.Type == types.TransferTypeTreasuryPay { 1390 treasury++ 1391 } 1392 } 1393 1394 assert.Equal(t, 2, liquidity) 1395 assert.Equal(t, 2, infra) 1396 assert.Equal(t, 0, recv) 1397 assert.Equal(t, 0, pay) 1398 assert.Equal(t, 0, hmp) 1399 assert.Equal(t, 0, hmr) 1400 assert.Equal(t, 2, bb) 1401 assert.Equal(t, 2, treasury) 1402 } 1403 1404 func TestCalcAuctionTradingWithDiscountsAndRewards(t *testing.T) { 1405 eng := getTestFee(t) 1406 ctrl := gomock.NewController(t) 1407 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1408 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1409 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1410 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1411 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { 1412 if p == types.PartyID("party1") { 1413 return types.EmptyFactors 1414 } else { 1415 return types.Factors{ 1416 Infra: num.NewDecimalFromFloat(0.5), 1417 Maker: num.NewDecimalFromFloat(0.5), 1418 Liquidity: num.NewDecimalFromFloat(0.5), 1419 } 1420 } 1421 }).AnyTimes() 1422 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).DoAndReturn(func(p types.PartyID) types.Factors { 1423 if p == types.PartyID("party1") { 1424 return types.Factors{ 1425 Infra: num.NewDecimalFromFloat(0.2), 1426 Maker: num.NewDecimalFromFloat(0.2), 1427 Liquidity: num.NewDecimalFromFloat(0.2), 1428 } 1429 } else { 1430 return types.Factors{ 1431 Infra: num.NewDecimalFromFloat(0.3), 1432 Maker: num.NewDecimalFromFloat(0.3), 1433 Liquidity: num.NewDecimalFromFloat(0.3), 1434 } 1435 } 1436 }).AnyTimes() 1437 discountRewardService.EXPECT().GetReferrer(gomock.Any()).DoAndReturn(func(p types.PartyID) (types.PartyID, error) { 1438 if p == types.PartyID("party1") { 1439 return types.PartyID("referrer"), nil 1440 } else { 1441 return types.PartyID(""), errors.New("No referrer") 1442 } 1443 }).AnyTimes() 1444 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party1")).Return(types.Factors{ 1445 Infra: num.NewDecimalFromFloat(0.5), 1446 Maker: num.NewDecimalFromFloat(0.5), 1447 Liquidity: num.NewDecimalFromFloat(0.5), 1448 }).AnyTimes() 1449 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(types.PartyID("party2")).Return(types.EmptyFactors).AnyTimes() 1450 1451 trades := []*types.Trade{ 1452 { 1453 Aggressor: types.SideSell, 1454 Seller: "party1", 1455 Buyer: "party2", 1456 Size: 1, 1457 Price: num.NewUint(100), 1458 }, 1459 } 1460 1461 ft, err := eng.CalculateForAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1462 assert.NotNil(t, ft) 1463 assert.Nil(t, err) 1464 1465 // get the amounts map 1466 feeAmounts := ft.TotalFeesAmountPerParty() 1467 1468 // liquidity fee before discounts = 0.5 * 0.1 * 100 = 5 1469 // party1 1470 // lfAfterRefDiscount = 5 - 0 1471 // lfAfterVolDiscount = 5 - 0.2*5 = 5-1 = 4 1472 // lfAfterReward = 4 - 0.5*4 = 2 1473 1474 // party2 1475 // lfAfterRefDiscount = 5 - 0.5*5 = 3 1476 // lfAfterVolDiscount = 3 - 0.3*3 = 3-0 = 3 1477 // lfAfterReward = 3 (no referrer) 1478 1479 // infra fee before discounts = 0.5 * 0.05 * 100 = 3 1480 // party1 1481 // infAfterRefDiscount = 3 - 0 1482 // infAfterVolDiscount = 3 - 0.2*3 = 3 1483 // infAfterReward = 3 - 0.5*3 = 2 1484 1485 // party2 1486 // infAfterRefDiscount = 3 - 0.5*3 = 2 1487 // infAfterVolDiscount = 2 - 0.3*2 = 2 1488 // infAfterReward = 2 (no referrer) 1489 1490 party1Amount, ok := feeAmounts["party1"] 1491 require.True(t, ok) 1492 require.Equal(t, num.NewUint(4), party1Amount) 1493 party2Amount, ok := feeAmounts["party2"] 1494 require.True(t, ok) 1495 require.Equal(t, num.NewUint(5), party2Amount) 1496 1497 // get the transfer and check we have enough of each types 1498 transfers := ft.Transfers() 1499 var pay, recv, infra, liquidity, reward int 1500 totalReward := num.UintZero() 1501 for _, v := range transfers { 1502 if v.Type == types.TransferTypeLiquidityFeePay { 1503 liquidity++ 1504 } 1505 if v.Type == types.TransferTypeInfrastructureFeePay { 1506 infra++ 1507 } 1508 if v.Type == types.TransferTypeMakerFeeReceive { 1509 recv++ 1510 } 1511 if v.Type == types.TransferTypeMakerFeePay { 1512 pay++ 1513 } 1514 if v.Type == types.TransferTypeFeeReferrerRewardPay { 1515 reward++ 1516 totalReward.AddSum(v.Amount.Amount) 1517 } 1518 if v.Type == types.TransferTypeFeeReferrerRewardDistribute { 1519 reward++ 1520 } 1521 } 1522 require.Equal(t, num.NewUint(3), totalReward) 1523 require.Equal(t, 2, liquidity) 1524 require.Equal(t, 2, infra) 1525 require.Equal(t, 0, recv) 1526 require.Equal(t, 0, pay) 1527 require.Equal(t, 2, reward) 1528 } 1529 1530 func testCalcBatchAuctionTradingErrorEmptyTrade(t *testing.T) { 1531 eng := getTestFee(t) 1532 ctrl := gomock.NewController(t) 1533 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1534 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1535 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1536 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1537 _, err := eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 1538 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 1539 1540 eng = getExtendedTestFee(t) 1541 _, err = eng.CalculateForFrequentBatchesAuctionMode([]*types.Trade{}, discountRewardService, volumeDiscountService, volumeRebateService) 1542 assert.EqualError(t, err, fee.ErrEmptyTrades.Error()) 1543 } 1544 1545 func testCalcBatchAuctionTradingSameBatch(t *testing.T) { 1546 eng := getTestFee(t) 1547 ctrl := gomock.NewController(t) 1548 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1549 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1550 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1551 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1552 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1553 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1554 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1555 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 1556 trades := []*types.Trade{ 1557 { 1558 Aggressor: types.SideSell, 1559 Seller: "party1", 1560 Buyer: "party2", 1561 Size: 1, 1562 Price: num.NewUint(100), 1563 SellerAuctionBatch: 10, 1564 BuyerAuctionBatch: 10, 1565 }, 1566 } 1567 1568 ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1569 assert.NotNil(t, ft) 1570 assert.Nil(t, err) 1571 1572 // get the amounts map 1573 feeAmounts := ft.TotalFeesAmountPerParty() 1574 // fees are (100 * 0.1 + 100 * 0.05) = 15 1575 // 15 / 2 = 7.5 1576 // internally the engine Ceil all fees. 1577 // so here we will expect 8 for each 1578 party1Amount, ok := feeAmounts["party1"] 1579 assert.True(t, ok) 1580 assert.Equal(t, num.NewUint(8), party1Amount) 1581 party2Amount, ok := feeAmounts["party2"] 1582 assert.True(t, ok) 1583 assert.Equal(t, num.NewUint(8), party2Amount) 1584 1585 // get the transfer and check we have enough of each types 1586 transfers := ft.Transfers() 1587 var pay, recv, infra, liquidity int 1588 for _, v := range transfers { 1589 if v.Type == types.TransferTypeLiquidityFeePay { 1590 liquidity++ 1591 } 1592 if v.Type == types.TransferTypeInfrastructureFeePay { 1593 infra++ 1594 } 1595 if v.Type == types.TransferTypeMakerFeeReceive { 1596 recv++ 1597 } 1598 if v.Type == types.TransferTypeMakerFeePay { 1599 pay++ 1600 } 1601 } 1602 1603 assert.Equal(t, liquidity, 2) 1604 assert.Equal(t, infra, 2) 1605 assert.Equal(t, recv, 0) 1606 assert.Equal(t, pay, 0) 1607 } 1608 1609 func testCalcBatchAuctionTradingDifferentBatches(t *testing.T) { 1610 eng := getTestFee(t) 1611 ctrl := gomock.NewController(t) 1612 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1613 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1614 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1615 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1616 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1617 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1618 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1619 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 1620 trades := []*types.Trade{ 1621 { 1622 Aggressor: types.SideSell, 1623 Seller: "party1", 1624 Buyer: "party2", 1625 Size: 1, 1626 Price: num.NewUint(100), 1627 SellerAuctionBatch: 11, 1628 BuyerAuctionBatch: 10, 1629 }, 1630 } 1631 1632 ft, err := eng.CalculateForFrequentBatchesAuctionMode(trades, discountRewardService, volumeDiscountService, volumeRebateService) 1633 assert.NotNil(t, ft) 1634 assert.Nil(t, err) 1635 1636 // get the amounts map 1637 feeAmounts := ft.TotalFeesAmountPerParty() 1638 // fees are (100 * 0.1 + 100 * 0.05 + 100 *0.02) = 17 1639 party1Amount, ok := feeAmounts["party1"] 1640 assert.True(t, ok) 1641 assert.Equal(t, num.NewUint(17), party1Amount) 1642 party2Amount, ok := feeAmounts["party2"] 1643 assert.True(t, ok) 1644 assert.True(t, party2Amount.IsZero()) 1645 1646 // get the transfer and check we have enough of each types 1647 transfers := ft.Transfers() 1648 var pay, recv, infra, liquidity int 1649 for _, v := range transfers { 1650 if v.Type == types.TransferTypeLiquidityFeePay { 1651 liquidity++ 1652 } 1653 if v.Type == types.TransferTypeInfrastructureFeePay { 1654 infra++ 1655 } 1656 if v.Type == types.TransferTypeMakerFeeReceive { 1657 recv++ 1658 } 1659 if v.Type == types.TransferTypeMakerFeePay { 1660 pay++ 1661 } 1662 } 1663 1664 assert.Equal(t, liquidity, 1) 1665 assert.Equal(t, infra, 1) 1666 assert.Equal(t, recv, 1) 1667 assert.Equal(t, pay, 1) 1668 } 1669 1670 func testCloseoutFees(t *testing.T) { 1671 eng := getTestFee(t) 1672 ctrl := gomock.NewController(t) 1673 referralDiscountService := mocks.NewMockReferralDiscountRewardService(ctrl) 1674 referralDiscountService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1675 discountRewardService := mocks.NewMockReferralDiscountRewardService(ctrl) 1676 volumeDiscountService := mocks.NewMockVolumeDiscountService(ctrl) 1677 volumeRebateService := mocks.NewMockVolumeRebateService(ctrl) 1678 discountRewardService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1679 discountRewardService.EXPECT().RewardsFactorsMultiplierAppliedForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1680 volumeDiscountService.EXPECT().VolumeDiscountFactorForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1681 discountRewardService.EXPECT().GetReferrer(gomock.Any()).Return(types.PartyID(""), errors.New("not a referrer")).AnyTimes() 1682 referralDiscountService.EXPECT().ReferralDiscountFactorsForParty(gomock.Any()).Return(types.EmptyFactors).AnyTimes() 1683 volumeRebateService.EXPECT().VolumeRebateFactorForParty(gomock.Any()).Return(num.DecimalZero()).AnyTimes() 1684 trades := []*types.Trade{ 1685 { 1686 Aggressor: types.SideSell, 1687 Seller: types.NetworkParty, 1688 Buyer: "party1", 1689 Size: 1, 1690 Price: num.NewUint(100), 1691 }, 1692 { 1693 Aggressor: types.SideSell, 1694 Seller: types.NetworkParty, 1695 Buyer: "party2", 1696 Size: 1, 1697 Price: num.NewUint(100), 1698 }, 1699 { 1700 Aggressor: types.SideSell, 1701 Seller: types.NetworkParty, 1702 Buyer: "party3", 1703 Size: 1, 1704 Price: num.NewUint(100), 1705 }, 1706 { 1707 Aggressor: types.SideSell, 1708 Seller: types.NetworkParty, 1709 Buyer: "party4", 1710 Size: 1, 1711 Price: num.NewUint(100), 1712 }, 1713 } 1714 1715 ft, fee := eng.GetFeeForPositionResolution(trades, referralDiscountService, volumeDiscountService, volumeRebateService) 1716 assert.NotNil(t, fee) 1717 allTransfers := ft.Transfers() 1718 // first we have the network -> pay transfers , then 1 transfer per good party 1719 // two additional transfers for the buy back and treasury fees 1720 assert.Equal(t, len(trades), len(allTransfers)-5) 1721 goodPartyTransfers := allTransfers[5:] 1722 networkTransfers := allTransfers[:5] 1723 1724 numTrades := num.NewUint(uint64(len(trades))) 1725 // maker fee is 100 * 0.02 == 2 1726 // total network fee is 2 * len(trades) 1727 feeAmt := num.NewUint(2) 1728 total := num.UintZero().Mul(feeAmt, numTrades) 1729 // this must be the total fee for the network 1730 require.True(t, total.EQ(fee.MakerFee)) 1731 1732 // now check the transfers for the fee payouts 1733 for _, trans := range goodPartyTransfers { 1734 require.True(t, trans.Amount.Amount.EQ(feeAmt)) 1735 require.True(t, trans.MinAmount.EQ(num.UintZero())) 1736 } 1737 // other fees below, making these transfers may be a bit silly at times... 1738 // liquidity fees are 100 * 0.1 -> 10 * 4 = 40 1739 liqFee := num.UintZero().Mul(numTrades, num.NewUint(10)) 1740 require.True(t, liqFee.EQ(fee.LiquidityFee)) 1741 // infraFee is 100 * 0.05 -> 5 * 4 == 20 1742 infraFee := num.UintZero().Mul(numTrades, num.NewUint(5)) 1743 require.True(t, infraFee.EQ(fee.InfrastructureFee)) 1744 for _, tf := range networkTransfers { 1745 require.True(t, num.UintZero().EQ(tf.MinAmount)) 1746 switch tf.Type { 1747 case types.TransferTypeMakerFeePay: 1748 require.True(t, tf.Amount.Amount.EQ(fee.MakerFee)) 1749 case types.TransferTypeLiquidityFeePay: 1750 require.True(t, tf.Amount.Amount.EQ(fee.LiquidityFee)) 1751 case types.TransferTypeInfrastructureFeePay: 1752 require.True(t, tf.Amount.Amount.EQ(fee.InfrastructureFee)) 1753 } 1754 } 1755 }