code.vegaprotocol.io/vega@v0.79.0/core/fee/engine.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
    17  
    18  import (
    19  	"errors"
    20  	"sort"
    21  
    22  	"code.vegaprotocol.io/vega/core/events"
    23  	"code.vegaprotocol.io/vega/core/types"
    24  	"code.vegaprotocol.io/vega/libs/num"
    25  	"code.vegaprotocol.io/vega/logging"
    26  	eventspb "code.vegaprotocol.io/vega/protos/vega/events/v1"
    27  )
    28  
    29  var (
    30  	ErrEmptyTrades      = errors.New("empty trades slice sent to fees")
    31  	ErrInvalidFeeFactor = errors.New("fee factors must be positive")
    32  )
    33  
    34  //go:generate go run github.com/golang/mock/mockgen -destination mocks/mocks.go -package mocks code.vegaprotocol.io/vega/core/fee ReferralDiscountRewardService,VolumeDiscountService,VolumeRebateService
    35  type ReferralDiscountRewardService interface {
    36  	ReferralDiscountFactorsForParty(party types.PartyID) types.Factors
    37  	RewardsFactorsMultiplierAppliedForParty(party types.PartyID) types.Factors
    38  	GetReferrer(referee types.PartyID) (types.PartyID, error)
    39  }
    40  
    41  type VolumeDiscountService interface {
    42  	VolumeDiscountFactorForParty(party types.PartyID) types.Factors
    43  }
    44  
    45  type VolumeRebateService interface {
    46  	VolumeRebateFactorForParty(party types.PartyID) num.Decimal
    47  }
    48  
    49  type Engine struct {
    50  	log *logging.Logger
    51  	cfg Config
    52  
    53  	asset          string
    54  	feeCfg         types.Fees
    55  	f              factors
    56  	positionFactor num.Decimal
    57  
    58  	feesStats *FeesStats
    59  }
    60  
    61  type factors struct {
    62  	makerFee          num.Decimal
    63  	infrastructureFee num.Decimal
    64  	liquidityFee      num.Decimal
    65  	treasuryFee       num.Decimal
    66  	buyBackFee        num.Decimal
    67  }
    68  
    69  func New(
    70  	log *logging.Logger,
    71  	cfg Config,
    72  	feeCfg types.Fees,
    73  	asset string,
    74  	positionFactor num.Decimal,
    75  ) (*Engine, error) {
    76  	log = log.Named(namedLogger)
    77  	log.SetLevel(cfg.Level.Get())
    78  
    79  	e := &Engine{
    80  		log:            log,
    81  		feeCfg:         feeCfg,
    82  		cfg:            cfg,
    83  		asset:          asset,
    84  		positionFactor: positionFactor,
    85  		feesStats:      NewFeesStats(),
    86  	}
    87  	return e, e.UpdateFeeFactors(e.feeCfg)
    88  }
    89  
    90  func NewFromState(
    91  	log *logging.Logger,
    92  	cfg Config,
    93  	feeCfg types.Fees,
    94  	asset string,
    95  	positionFactor num.Decimal,
    96  	FeesStats *eventspb.FeesStats,
    97  ) (*Engine, error) {
    98  	e, err := New(log, cfg, feeCfg, asset, positionFactor)
    99  	if err != nil {
   100  		return nil, err
   101  	}
   102  
   103  	e.feesStats = NewFeesStatsFromProto(FeesStats)
   104  
   105  	return e, nil
   106  }
   107  
   108  func (e *Engine) GetState(assetQuantum num.Decimal) *eventspb.FeesStats {
   109  	return e.feesStats.ToProto(e.asset, assetQuantum)
   110  }
   111  
   112  func (e *Engine) TotalTradingFeesPerParty() map[string]*num.Uint {
   113  	return e.feesStats.TotalTradingFeesPerParty()
   114  }
   115  
   116  func (e *Engine) GetFeesStatsOnEpochEnd(assetQuantum num.Decimal) (FeesStats *eventspb.FeesStats) {
   117  	FeesStats, e.feesStats = e.feesStats.ToProto(e.asset, assetQuantum), NewFeesStats()
   118  	return
   119  }
   120  
   121  // ReloadConf is used in order to reload the internal configuration of
   122  // the fee engine.
   123  func (e *Engine) ReloadConf(cfg Config) {
   124  	e.log.Info("reloading configuration")
   125  	if e.log.GetLevel() != cfg.Level.Get() {
   126  		e.log.Info("updating log level",
   127  			logging.String("old", e.log.GetLevel().String()),
   128  			logging.String("new", cfg.Level.String()),
   129  		)
   130  		e.log.SetLevel(cfg.Level.Get())
   131  	}
   132  
   133  	e.cfg = cfg
   134  }
   135  
   136  func (e *Engine) UpdateFeeFactors(fees types.Fees) error {
   137  	if fees.Factors.MakerFee.IsNegative() || fees.Factors.InfrastructureFee.IsNegative() || fees.Factors.LiquidityFee.IsNegative() || fees.Factors.BuyBackFee.IsNegative() || fees.Factors.TreasuryFee.IsNegative() {
   138  		return ErrInvalidFeeFactor
   139  	}
   140  	e.f.makerFee = fees.Factors.MakerFee
   141  	e.f.infrastructureFee = fees.Factors.InfrastructureFee
   142  	// not sure we need the IsPositive check here, that ought to be validation
   143  	if !fees.Factors.LiquidityFee.IsZero() && fees.Factors.LiquidityFee.IsPositive() {
   144  		e.f.liquidityFee = fees.Factors.LiquidityFee
   145  	}
   146  	e.f.treasuryFee = fees.Factors.TreasuryFee
   147  	e.f.buyBackFee = fees.Factors.BuyBackFee
   148  
   149  	e.feeCfg = fees
   150  	return nil
   151  }
   152  
   153  func (e *Engine) SetLiquidityFee(v num.Decimal) {
   154  	e.f.liquidityFee = v
   155  }
   156  
   157  // CalculateForContinuousMode calculate the fee for
   158  // trades which were produced from a market running
   159  // in continuous trading mode.
   160  // A single FeesTransfer is produced here as all fees
   161  // are paid by the aggressive order.
   162  func (e *Engine) CalculateForContinuousMode(
   163  	trades []*types.Trade,
   164  	referral ReferralDiscountRewardService,
   165  	volumeDiscountService VolumeDiscountService,
   166  	volumeRebateService VolumeRebateService,
   167  ) (events.FeesTransfer, error) {
   168  	if len(trades) <= 0 {
   169  		return nil, ErrEmptyTrades
   170  	}
   171  
   172  	var (
   173  		taker, maker                 string
   174  		totalFeeAmount               = num.UintZero()
   175  		totalInfrastructureFeeAmount = num.UintZero()
   176  		totalLiquidityFeeAmount      = num.UintZero()
   177  		totalRewardAmount            = num.UintZero()
   178  		// we allocate the len of the trades + 2
   179  		// len(trade) = number of makerFee + 1 infra fee + 1 liquidity fee
   180  		transfers     = make([]*types.Transfer, 0, (len(trades)*2)+2)
   181  		transfersRecv = make([]*types.Transfer, 0, len(trades)+2)
   182  	)
   183  
   184  	for _, trade := range trades {
   185  		taker = trade.Buyer
   186  		maker = trade.Seller
   187  		if trade.Aggressor == types.SideSell {
   188  			taker = trade.Seller
   189  			maker = trade.Buyer
   190  		}
   191  		size := num.NewUint(trade.Size)
   192  		// multiply by size
   193  		tradeValueForFee := size.Mul(trade.Price, size).ToDecimal().Div(e.positionFactor)
   194  		fee, reward := e.applyDiscountsAndRewards(taker, maker, tradeValueForFee, e.calculateContinuousModeFees(trade), referral, volumeDiscountService, volumeRebateService)
   195  
   196  		e.feesStats.RegisterMakerFee(maker, taker, fee.MakerFee)
   197  
   198  		totalTradingFees := num.UintZero().AddSum(fee.MakerFee, fee.InfrastructureFee, fee.LiquidityFee, fee.BuyBackFee, fee.TreasuryFee, fee.HighVolumeMakerFee)
   199  
   200  		switch trade.Aggressor {
   201  		case types.SideBuy:
   202  			trade.BuyerFee = fee
   203  			trade.SellerFee = types.NewFee()
   204  			maker = trade.Seller
   205  		case types.SideSell:
   206  			trade.SellerFee = fee
   207  			trade.BuyerFee = types.NewFee()
   208  			maker = trade.Buyer
   209  		}
   210  
   211  		e.feesStats.RegisterTradingFees(taker, totalTradingFees)
   212  		e.feesStats.RegisterTradingFees(maker, fee.MakerFee)
   213  
   214  		totalFeeAmount.AddSum(totalTradingFees)
   215  		totalInfrastructureFeeAmount.AddSum(fee.InfrastructureFee)
   216  		totalLiquidityFeeAmount.AddSum(fee.LiquidityFee)
   217  		// create a transfer for the aggressor
   218  		transfers = append(transfers, &types.Transfer{
   219  			Owner: taker,
   220  			Amount: &types.FinancialAmount{
   221  				Asset:  e.asset,
   222  				Amount: fee.MakerFee.Clone(),
   223  			},
   224  			Type: types.TransferTypeMakerFeePay,
   225  		})
   226  		// create a transfer for the maker
   227  		transfersRecv = append(transfersRecv, &types.Transfer{
   228  			Owner: maker,
   229  			Amount: &types.FinancialAmount{
   230  				Asset:  e.asset,
   231  				Amount: fee.MakerFee.Clone(),
   232  			},
   233  			Type: types.TransferTypeMakerFeeReceive,
   234  		})
   235  
   236  		if !fee.HighVolumeMakerFee.IsZero() {
   237  			// create a transfer for the aggressor
   238  			transfers = append(transfers, &types.Transfer{
   239  				Owner: taker,
   240  				Amount: &types.FinancialAmount{
   241  					Asset:  e.asset,
   242  					Amount: fee.HighVolumeMakerFee.Clone(),
   243  				},
   244  				Type: types.TransferTypeHighMakerRebatePay,
   245  			})
   246  			// create a transfer for the maker
   247  			transfersRecv = append(transfersRecv, &types.Transfer{
   248  				Owner: maker,
   249  				Amount: &types.FinancialAmount{
   250  					Asset:  e.asset,
   251  					Amount: fee.HighVolumeMakerFee.Clone(),
   252  				},
   253  				Type: types.TransferTypeHighMakerRebateReceive,
   254  			})
   255  		}
   256  
   257  		// create a transfer for the aggressor
   258  		transfers = append(transfers, &types.Transfer{
   259  			Owner: taker,
   260  			Amount: &types.FinancialAmount{
   261  				Asset:  e.asset,
   262  				Amount: fee.BuyBackFee.Clone(),
   263  			},
   264  			Type: types.TransferTypeBuyBackFeePay,
   265  		})
   266  
   267  		// create a transfer for the aggressor
   268  		transfers = append(transfers, &types.Transfer{
   269  			Owner: taker,
   270  			Amount: &types.FinancialAmount{
   271  				Asset:  e.asset,
   272  				Amount: fee.TreasuryFee.Clone(),
   273  			},
   274  			Type: types.TransferTypeTreasuryPay,
   275  		})
   276  
   277  		if reward == nil {
   278  			continue
   279  		}
   280  		totalRewardAmount.AddSum(reward.InfrastructureFeeReferrerReward)
   281  		totalRewardAmount.AddSum(reward.LiquidityFeeReferrerReward)
   282  		totalRewardAmount.AddSum(reward.MakerFeeReferrerReward)
   283  	}
   284  
   285  	// now create transfer for the infrastructure
   286  	transfers = append(transfers, &types.Transfer{
   287  		Owner: taker,
   288  		Amount: &types.FinancialAmount{
   289  			Asset:  e.asset,
   290  			Amount: totalInfrastructureFeeAmount,
   291  		},
   292  		Type: types.TransferTypeInfrastructureFeePay,
   293  	})
   294  	// now create transfer for the liquidity
   295  	transfers = append(transfers, &types.Transfer{
   296  		Owner: taker,
   297  		Amount: &types.FinancialAmount{
   298  			Asset:  e.asset,
   299  			Amount: totalLiquidityFeeAmount,
   300  		},
   301  		Type: types.TransferTypeLiquidityFeePay,
   302  	})
   303  
   304  	// if there's a referral reward - add transfers for it
   305  	if !totalRewardAmount.IsZero() {
   306  		referrer, _ := referral.GetReferrer(types.PartyID(taker))
   307  		transfers = append(transfers, &types.Transfer{
   308  			Owner: taker,
   309  			Amount: &types.FinancialAmount{
   310  				Asset:  e.asset,
   311  				Amount: totalRewardAmount.Clone(),
   312  			},
   313  			Type: types.TransferTypeFeeReferrerRewardPay,
   314  		})
   315  		transfersRecv = append(transfersRecv, &types.Transfer{
   316  			Owner: string(referrer),
   317  			Amount: &types.FinancialAmount{
   318  				Asset:  e.asset,
   319  				Amount: totalRewardAmount.Clone(),
   320  			},
   321  			Type: types.TransferTypeFeeReferrerRewardDistribute,
   322  		})
   323  	}
   324  
   325  	return &feesTransfer{
   326  		totalFeesAmountsPerParty: map[string]*num.Uint{taker: totalFeeAmount, maker: num.UintZero()},
   327  		transfers:                append(transfers, transfersRecv...),
   328  	}, nil
   329  }
   330  
   331  // CalculateForAuctionMode calculate the fee for
   332  // trades which were produced from a market running in
   333  // in auction trading mode.
   334  // A list FeesTransfer is produced each containing fees transfer from a
   335  // single party.
   336  func (e *Engine) CalculateForAuctionMode(
   337  	trades []*types.Trade,
   338  	referral ReferralDiscountRewardService,
   339  	volumeDiscount VolumeDiscountService,
   340  	volumeRebate VolumeRebateService,
   341  ) (events.FeesTransfer, error) {
   342  	if len(trades) <= 0 {
   343  		return nil, ErrEmptyTrades
   344  	}
   345  	var (
   346  		totalFeesAmounts = map[string]*num.Uint{}
   347  		// we allocate for len of trades *4 as all trades generate
   348  		// 2 fees per party
   349  		transfers = make([]*types.Transfer, 0, len(trades)*4)
   350  	)
   351  
   352  	// we iterate over all trades
   353  	// for each trades both party needs to pay half of the fees
   354  	// no maker fees are to be paid here.
   355  	for _, v := range trades {
   356  		buyerFess, sellerFees, newTransfers := e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate)
   357  		transfers = append(transfers, newTransfers...)
   358  
   359  		// increase the total fee for the parties
   360  		if sellerTotalFee, ok := totalFeesAmounts[v.Seller]; !ok {
   361  			totalFeesAmounts[v.Seller] = num.Sum(sellerFees.InfrastructureFee, sellerFees.LiquidityFee)
   362  		} else {
   363  			sellerTotalFee.AddSum(num.Sum(sellerFees.InfrastructureFee, sellerFees.LiquidityFee))
   364  		}
   365  		if buyerTotalFee, ok := totalFeesAmounts[v.Buyer]; !ok {
   366  			totalFeesAmounts[v.Buyer] = num.Sum(buyerFess.InfrastructureFee, buyerFess.LiquidityFee).Clone()
   367  		} else {
   368  			buyerTotalFee.AddSum(num.Sum(buyerFess.InfrastructureFee, buyerFess.LiquidityFee).Clone())
   369  		}
   370  
   371  		v.BuyerFee = buyerFess
   372  		v.SellerFee = sellerFees
   373  	}
   374  
   375  	return &feesTransfer{
   376  		totalFeesAmountsPerParty: totalFeesAmounts,
   377  		transfers:                transfers,
   378  	}, nil
   379  }
   380  
   381  // CalculateForFrequentBatchesAuctionMode calculate the fee for
   382  // trades which were produced from a market running
   383  // in auction trading mode.
   384  // A list FeesTransfer is produced each containing fees transfer from a
   385  // single party.
   386  func (e *Engine) CalculateForFrequentBatchesAuctionMode(
   387  	trades []*types.Trade,
   388  	referral ReferralDiscountRewardService,
   389  	volumeDiscount VolumeDiscountService,
   390  	volumeRebate VolumeRebateService,
   391  ) (events.FeesTransfer, error) {
   392  	if len(trades) <= 0 {
   393  		return nil, ErrEmptyTrades
   394  	}
   395  
   396  	var (
   397  		totalFeesAmounts = map[string]*num.Uint{}
   398  		// we allocate for len of trades *4 as all trades generate
   399  		// at lest2 fees per party
   400  		transfers = make([]*types.Transfer, 0, len(trades)*4)
   401  	)
   402  
   403  	// we iterate over all trades
   404  	// if the parties submitted the order in the same batches,
   405  	// auction mode fees apply.
   406  	// if not then the aggressor is the party which submitted
   407  	// the order last, and continuous trading fees apply
   408  	for _, v := range trades {
   409  		var (
   410  			sellerTotalFee, buyerTotalFee *num.Uint
   411  			newTransfers                  []*types.Transfer
   412  		)
   413  		// we are in the same auction, normal auction fees applies
   414  		if v.BuyerAuctionBatch == v.SellerAuctionBatch {
   415  			v.BuyerFee, v.SellerFee, newTransfers = e.getAuctionModeFeesAndTransfers(v, referral, volumeDiscount, volumeRebate)
   416  			sellerTotalFee = num.Sum(v.BuyerFee.InfrastructureFee, v.BuyerFee.LiquidityFee)
   417  			buyerTotalFee = num.Sum(v.SellerFee.InfrastructureFee, v.SellerFee.LiquidityFee)
   418  		} else {
   419  			// set the aggressor to be the side of the party
   420  			// entering the later auction
   421  			v.Aggressor = types.SideSell
   422  			if v.BuyerAuctionBatch > v.SellerAuctionBatch {
   423  				v.Aggressor = types.SideBuy
   424  			}
   425  			// fees are being assign to the trade directly
   426  			// no need to do add them there as well
   427  			ftrnsfr, _ := e.CalculateForContinuousMode([]*types.Trade{v}, referral, volumeDiscount, volumeRebate)
   428  			newTransfers = ftrnsfr.Transfers()
   429  			buyerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Buyer]
   430  			sellerTotalFee = ftrnsfr.TotalFeesAmountPerParty()[v.Seller]
   431  		}
   432  
   433  		transfers = append(transfers, newTransfers...)
   434  
   435  		// increase the total fee for the parties
   436  		if prevTotalFee, ok := totalFeesAmounts[v.Seller]; !ok {
   437  			totalFeesAmounts[v.Seller] = sellerTotalFee.Clone()
   438  		} else {
   439  			prevTotalFee.AddSum(sellerTotalFee)
   440  		}
   441  		if prevTotalFee, ok := totalFeesAmounts[v.Buyer]; !ok {
   442  			totalFeesAmounts[v.Buyer] = buyerTotalFee.Clone()
   443  		} else {
   444  			prevTotalFee.AddSum(buyerTotalFee)
   445  		}
   446  	}
   447  
   448  	return &feesTransfer{
   449  		totalFeesAmountsPerParty: totalFeesAmounts,
   450  		transfers:                transfers,
   451  	}, nil
   452  }
   453  
   454  func (e *Engine) GetFeeForPositionResolution(trades []*types.Trade,
   455  	referral ReferralDiscountRewardService,
   456  	volumeDiscount VolumeDiscountService,
   457  	volumeRebate VolumeRebateService,
   458  ) (events.FeesTransfer, *types.Fee) {
   459  	if len(trades) == 0 {
   460  		return nil, nil
   461  	}
   462  	var (
   463  		netFee *types.Fee
   464  		gt     []*types.Transfer
   465  	)
   466  	transfers := make([]*types.Transfer, 0, len(trades))
   467  	for _, t := range trades {
   468  		fees := e.calculateContinuousModeFees(t)
   469  
   470  		maker := t.Buyer
   471  		if t.Buyer == types.NetworkParty {
   472  			maker = t.Seller
   473  		}
   474  		size := num.NewUint(t.Size)
   475  		// multiply by size
   476  		tradeValueForFee := size.Mul(t.Price, size).ToDecimal().Div(e.positionFactor)
   477  		postRewardDiscountFees, _ := e.applyDiscountsAndRewards(types.NetworkParty, maker, tradeValueForFee, fees, referral, volumeDiscount, volumeRebate)
   478  		e.feesStats.RegisterMakerFee(maker, types.NetworkParty, postRewardDiscountFees.MakerFee)
   479  
   480  		goodParty := t.Buyer
   481  		t.SellerFee = postRewardDiscountFees
   482  		if t.Buyer == types.NetworkParty {
   483  			goodParty = t.Seller
   484  			t.SellerFee = types.NewFee()
   485  			t.BuyerFee = postRewardDiscountFees
   486  		}
   487  		netFee, gt = e.getNetworkFeeWithMakerTransfer(postRewardDiscountFees, netFee, goodParty)
   488  		transfers = append(transfers, gt...)
   489  	}
   490  	netTf, total := e.getNetworkFeeTransfers(netFee)
   491  	// calculate the
   492  	return &feesTransfer{
   493  		totalFeesAmountsPerParty: map[string]*num.Uint{
   494  			types.NetworkParty: total,
   495  		},
   496  		transfers: append(netTf, transfers...),
   497  	}, netFee
   498  }
   499  
   500  // BuildLiquidityFeeDistributionTransfer returns the set of transfers that will
   501  // be used by the collateral engine to distribute the fees.  As shares are
   502  // represented in float64 and fees are uint64, shares are floored and the
   503  // remainder is assigned to the last party on the share map. Note that the map
   504  // is sorted lexicographically to keep determinism.
   505  func (e *Engine) BuildLiquidityFeeDistributionTransfer(shares map[string]num.Decimal, acc *types.Account) events.FeesTransfer {
   506  	return e.buildLiquidityFeesTransfer(shares, acc, types.TransferTypeLiquidityFeeDistribute)
   507  }
   508  
   509  // BuildLiquidityFeeAllocationTransfer returns the set of transfers that will
   510  // be used by the collateral engine to allocate the fees to liquidity providers per market fee accounts.
   511  // As shares are represented in float64 and fees are uint64, shares are floored and the
   512  // remainder is assigned to the last party on the share map. Note that the map
   513  // is sorted lexicographically to keep determinism.
   514  func (e *Engine) BuildLiquidityFeeAllocationTransfer(shares map[string]num.Decimal, acc *types.Account) events.FeesTransfer {
   515  	return e.buildLiquidityFeesTransfer(shares, acc, types.TransferTypeLiquidityFeeAllocate)
   516  }
   517  
   518  func (e *Engine) buildLiquidityFeesTransfer(
   519  	shares map[string]num.Decimal,
   520  	acc *types.Account,
   521  	transferType types.TransferType,
   522  ) events.FeesTransfer {
   523  	if len(shares) == 0 {
   524  		return nil
   525  	}
   526  
   527  	ft := &feesTransfer{
   528  		totalFeesAmountsPerParty: map[string]*num.Uint{},
   529  		transfers:                make([]*types.Transfer, 0, len(shares)),
   530  	}
   531  
   532  	// Get all the map keys
   533  	keys := make([]string, 0, len(shares))
   534  
   535  	for key := range shares {
   536  		keys = append(keys, key)
   537  		ft.totalFeesAmountsPerParty[key] = num.UintZero()
   538  	}
   539  	sort.Strings(keys)
   540  
   541  	feeBal := acc.Balance.ToDecimal()
   542  	var floored num.Decimal
   543  	for _, key := range keys {
   544  		share := shares[key]
   545  		cs := feeBal.Mul(share).Floor()
   546  		floored = floored.Add(cs)
   547  
   548  		amount, _ := num.UintFromDecimal(cs)
   549  		// populate the return value
   550  		ft.totalFeesAmountsPerParty[key].AddSum(amount)
   551  		ft.transfers = append(ft.transfers, &types.Transfer{
   552  			Owner: key,
   553  			Amount: &types.FinancialAmount{
   554  				Amount: amount,
   555  				Asset:  acc.Asset,
   556  			},
   557  			MinAmount: amount.Clone(),
   558  			Type:      transferType,
   559  		})
   560  	}
   561  
   562  	// if there is a remainder, just keep it in the fee account, will be used next time we pay out fees
   563  	// last is the party who will get the remaining from ceil
   564  	return ft
   565  }
   566  
   567  func (e *Engine) getNetworkFeeWithMakerTransfer(fees *types.Fee, current *types.Fee, goodParty string) (*types.Fee, []*types.Transfer) {
   568  	transfers := []*types.Transfer{}
   569  	transfers = append(transfers, &types.Transfer{
   570  		Owner: goodParty,
   571  		Amount: &types.FinancialAmount{
   572  			Asset:  e.asset,
   573  			Amount: fees.MakerFee.Clone(),
   574  		},
   575  		MinAmount: num.UintZero(),
   576  		Type:      types.TransferTypeMakerFeeReceive,
   577  	})
   578  	if !fees.HighVolumeMakerFee.IsZero() {
   579  		transfers = append(transfers, &types.Transfer{
   580  			Owner: goodParty,
   581  			Amount: &types.FinancialAmount{
   582  				Asset:  e.asset,
   583  				Amount: fees.HighVolumeMakerFee,
   584  			},
   585  			MinAmount: num.UintZero(),
   586  			Type:      types.TransferTypeHighMakerRebateReceive,
   587  		})
   588  	}
   589  
   590  	if current == nil {
   591  		return fees.Clone(), transfers
   592  	}
   593  	current.MakerFee.AddSum(fees.MakerFee)
   594  	current.LiquidityFee.AddSum(fees.LiquidityFee)
   595  	current.InfrastructureFee.AddSum(fees.InfrastructureFee)
   596  	current.BuyBackFee.AddSum(fees.BuyBackFee)
   597  	current.TreasuryFee.AddSum(fees.TreasuryFee)
   598  	current.HighVolumeMakerFee.AddSum(fees.HighVolumeMakerFee)
   599  
   600  	return current, transfers
   601  }
   602  
   603  func (e *Engine) getNetworkFeeTransfers(fees *types.Fee) ([]*types.Transfer, *num.Uint) {
   604  	transfers := []*types.Transfer{
   605  		{
   606  			Owner: types.NetworkParty,
   607  			Amount: &types.FinancialAmount{
   608  				Asset:  e.asset,
   609  				Amount: fees.MakerFee.Clone(),
   610  			},
   611  			MinAmount: num.UintZero(),
   612  			Type:      types.TransferTypeMakerFeePay,
   613  		},
   614  		{
   615  			Owner: types.NetworkParty,
   616  			Amount: &types.FinancialAmount{
   617  				Asset:  e.asset,
   618  				Amount: fees.InfrastructureFee.Clone(),
   619  			},
   620  			MinAmount: num.UintZero(),
   621  			Type:      types.TransferTypeInfrastructureFeePay,
   622  		},
   623  		{
   624  			Owner: types.NetworkParty,
   625  			Amount: &types.FinancialAmount{
   626  				Asset:  e.asset,
   627  				Amount: fees.LiquidityFee.Clone(),
   628  			},
   629  			MinAmount: num.UintZero(),
   630  			Type:      types.TransferTypeLiquidityFeePay,
   631  		},
   632  		{
   633  			Owner: types.NetworkParty,
   634  			Amount: &types.FinancialAmount{
   635  				Asset:  e.asset,
   636  				Amount: fees.BuyBackFee.Clone(),
   637  			},
   638  			MinAmount: num.UintZero(),
   639  			Type:      types.TransferTypeBuyBackFeePay,
   640  		},
   641  		{
   642  			Owner: types.NetworkParty,
   643  			Amount: &types.FinancialAmount{
   644  				Asset:  e.asset,
   645  				Amount: fees.TreasuryFee.Clone(),
   646  			},
   647  			MinAmount: num.UintZero(),
   648  			Type:      types.TransferTypeTreasuryPay,
   649  		},
   650  	}
   651  	if !fees.HighVolumeMakerFee.IsZero() {
   652  		transfers = append(transfers, &types.Transfer{
   653  			Owner: types.NetworkParty,
   654  			Amount: &types.FinancialAmount{
   655  				Asset:  e.asset,
   656  				Amount: fees.HighVolumeMakerFee.Clone(),
   657  			},
   658  			MinAmount: num.UintZero(),
   659  			Type:      types.TransferTypeHighMakerRebatePay,
   660  		})
   661  	}
   662  
   663  	return transfers, num.Sum(fees.MakerFee, fees.InfrastructureFee, fees.LiquidityFee, fees.BuyBackFee, fees.HighVolumeMakerFee)
   664  }
   665  
   666  func (e *Engine) applyDiscountsAndRewards(taker string, maker string, tradeValueForFeePurposes num.Decimal, fees *types.Fee, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.ReferrerReward) {
   667  	referralDiscountFactors := referral.ReferralDiscountFactorsForParty(types.PartyID(taker))
   668  	volumeDiscountFactors := volumeDiscount.VolumeDiscountFactorForParty(types.PartyID(taker))
   669  	highVolumeMakerFee := volumeRebate.VolumeRebateFactorForParty(types.PartyID(maker)).Mul(tradeValueForFeePurposes)
   670  	highVolumeMakerFeeI, _ := num.UintFromDecimal(highVolumeMakerFee)
   671  
   672  	mf := fees.MakerFee.Clone()
   673  	inf := fees.InfrastructureFee.Clone()
   674  	lf := fees.LiquidityFee.Clone()
   675  
   676  	// calculate referral discounts
   677  	referralMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(referralDiscountFactors.Maker).Floor())
   678  	referralInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(referralDiscountFactors.Infra).Floor())
   679  	referralLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(referralDiscountFactors.Liquidity).Floor())
   680  
   681  	// apply referral discounts
   682  	mf = mf.Sub(mf, referralMakerDiscount)
   683  	inf = inf.Sub(inf, referralInfDiscount)
   684  	lf = lf.Sub(lf, referralLfDiscount)
   685  
   686  	// calculate volume discounts
   687  	volumeMakerDiscount, _ := num.UintFromDecimal(mf.ToDecimal().Mul(volumeDiscountFactors.Maker).Floor())
   688  	volumeInfDiscount, _ := num.UintFromDecimal(inf.ToDecimal().Mul(volumeDiscountFactors.Infra).Floor())
   689  	volumeLfDiscount, _ := num.UintFromDecimal(lf.ToDecimal().Mul(volumeDiscountFactors.Liquidity).Floor())
   690  
   691  	var rebateDiscountFactor num.Decimal
   692  	bbAndTreasury := num.Sum(fees.BuyBackFee, fees.TreasuryFee).ToDecimal()
   693  	if !bbAndTreasury.IsZero() {
   694  		rebateDiscountFactor = num.DecimalOne().Sub(highVolumeMakerFee.Div(bbAndTreasury))
   695  	}
   696  
   697  	treasuryFee, _ := num.UintFromDecimal(fees.TreasuryFee.ToDecimal().Mul(rebateDiscountFactor))
   698  	buyBackFee, _ := num.UintFromDecimal(fees.BuyBackFee.ToDecimal().Mul(rebateDiscountFactor))
   699  
   700  	// apply volume discounts
   701  	mf = mf.Sub(mf, volumeMakerDiscount)
   702  	inf = inf.Sub(inf, volumeInfDiscount)
   703  	lf = lf.Sub(lf, volumeLfDiscount)
   704  
   705  	f := &types.Fee{
   706  		HighVolumeMakerFee:                highVolumeMakerFeeI,
   707  		MakerFee:                          mf,
   708  		LiquidityFee:                      lf,
   709  		InfrastructureFee:                 inf,
   710  		BuyBackFee:                        buyBackFee.Clone(),
   711  		TreasuryFee:                       treasuryFee.Clone(),
   712  		MakerFeeVolumeDiscount:            volumeMakerDiscount,
   713  		InfrastructureFeeVolumeDiscount:   volumeInfDiscount,
   714  		LiquidityFeeVolumeDiscount:        volumeLfDiscount,
   715  		MakerFeeReferrerDiscount:          referralMakerDiscount,
   716  		InfrastructureFeeReferrerDiscount: referralInfDiscount,
   717  		LiquidityFeeReferrerDiscount:      referralLfDiscount,
   718  	}
   719  
   720  	e.feesStats.RegisterRefereeDiscount(
   721  		taker,
   722  		num.Sum(
   723  			referralMakerDiscount,
   724  			referralInfDiscount,
   725  			referralLfDiscount,
   726  		),
   727  	)
   728  
   729  	e.feesStats.RegisterVolumeDiscount(
   730  		taker,
   731  		num.Sum(
   732  			volumeMakerDiscount,
   733  			volumeInfDiscount,
   734  			volumeLfDiscount,
   735  		),
   736  	)
   737  
   738  	// calculate rewards
   739  	factors := referral.RewardsFactorsMultiplierAppliedForParty(types.PartyID(taker))
   740  	if factors.IsEmpty() {
   741  		return f, nil
   742  	}
   743  
   744  	referrerReward := types.NewReferrerReward()
   745  
   746  	referrerReward.MakerFeeReferrerReward, _ = num.UintFromDecimal(factors.Maker.Mul(mf.ToDecimal()).Floor())
   747  	referrerReward.InfrastructureFeeReferrerReward, _ = num.UintFromDecimal(factors.Infra.Mul(inf.ToDecimal()).Floor())
   748  	referrerReward.LiquidityFeeReferrerReward, _ = num.UintFromDecimal(factors.Liquidity.Mul(lf.ToDecimal()).Floor())
   749  
   750  	mf = mf.Sub(mf, referrerReward.MakerFeeReferrerReward)
   751  	inf = inf.Sub(inf, referrerReward.InfrastructureFeeReferrerReward)
   752  	lf = lf.Sub(lf, referrerReward.LiquidityFeeReferrerReward)
   753  
   754  	referrer, err := referral.GetReferrer(types.PartyID(taker))
   755  	if err != nil {
   756  		e.log.Error("could not load referrer from taker of trade", logging.PartyID(taker))
   757  	} else {
   758  		e.feesStats.RegisterReferrerReward(
   759  			string(referrer),
   760  			taker,
   761  			num.Sum(
   762  				referrerReward.MakerFeeReferrerReward,
   763  				referrerReward.InfrastructureFeeReferrerReward,
   764  				referrerReward.LiquidityFeeReferrerReward,
   765  			),
   766  		)
   767  	}
   768  
   769  	f.MakerFee = mf
   770  	f.InfrastructureFee = inf
   771  	f.LiquidityFee = lf
   772  	return f, referrerReward
   773  }
   774  
   775  func (e *Engine) getAuctionModeFeesAndTransfers(t *types.Trade, referral ReferralDiscountRewardService, volumeDiscount VolumeDiscountService, volumeRebate VolumeRebateService) (*types.Fee, *types.Fee, []*types.Transfer) {
   776  	fee := e.calculateAuctionModeFees(t)
   777  	// in auction there is no maker so there is no rebate, so passing 0 as the trade value
   778  	buyerFees, buyerReferrerRewards := e.applyDiscountsAndRewards(t.Buyer, t.Buyer, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate)
   779  	sellerFees, sellerReferrerRewards := e.applyDiscountsAndRewards(t.Seller, t.Seller, num.DecimalZero(), fee, referral, volumeDiscount, volumeRebate)
   780  
   781  	transfers := make([]*types.Transfer, 0, 12)
   782  	transfers = append(transfers,
   783  		e.getAuctionModeFeeTransfers(
   784  			sellerFees.InfrastructureFee, sellerFees.LiquidityFee, sellerFees.BuyBackFee, sellerFees.TreasuryFee, t.Seller)...)
   785  	transfers = append(transfers,
   786  		e.getAuctionModeFeeTransfers(
   787  			buyerFees.InfrastructureFee, buyerFees.LiquidityFee, buyerFees.BuyBackFee, buyerFees.TreasuryFee, t.Buyer)...)
   788  
   789  	if buyerReferrerRewards != nil {
   790  		referrerParty, _ := referral.GetReferrer(types.PartyID(t.Buyer))
   791  		transfers = append(transfers,
   792  			e.getAuctionModeFeeReferrerRewardTransfers(
   793  				num.Sum(buyerReferrerRewards.InfrastructureFeeReferrerReward, buyerReferrerRewards.LiquidityFeeReferrerReward), t.Buyer, string(referrerParty))...)
   794  	}
   795  
   796  	if sellerReferrerRewards != nil {
   797  		referrerParty, _ := referral.GetReferrer(types.PartyID(t.Seller))
   798  		transfers = append(transfers,
   799  			e.getAuctionModeFeeReferrerRewardTransfers(
   800  				num.Sum(sellerReferrerRewards.InfrastructureFeeReferrerReward, sellerReferrerRewards.LiquidityFeeReferrerReward), t.Seller, string(referrerParty))...)
   801  	}
   802  
   803  	return buyerFees, sellerFees, transfers
   804  }
   805  
   806  func (e *Engine) calculateContinuousModeFees(trade *types.Trade) *types.Fee {
   807  	size := num.NewUint(trade.Size)
   808  	// multiply by size
   809  	total := size.Mul(trade.Price, size).ToDecimal().Div(e.positionFactor)
   810  	mf, _ := num.UintFromDecimal(total.Mul(e.f.makerFee).Ceil())
   811  	inf, _ := num.UintFromDecimal(total.Mul(e.f.infrastructureFee).Ceil())
   812  	lf, _ := num.UintFromDecimal(total.Mul(e.f.liquidityFee).Ceil())
   813  	bbf, _ := num.UintFromDecimal(total.Mul(e.f.buyBackFee).Ceil())
   814  	tf, _ := num.UintFromDecimal(total.Mul(e.f.treasuryFee).Ceil())
   815  	return &types.Fee{
   816  		MakerFee:           mf,
   817  		InfrastructureFee:  inf,
   818  		LiquidityFee:       lf,
   819  		BuyBackFee:         bbf,
   820  		TreasuryFee:        tf,
   821  		HighVolumeMakerFee: num.UintZero(),
   822  	}
   823  }
   824  
   825  func (e *Engine) calculateAuctionModeFees(trade *types.Trade) *types.Fee {
   826  	fee := e.calculateContinuousModeFees(trade)
   827  	two := num.DecimalFromInt64(2)
   828  	inf, _ := num.UintFromDecimal(fee.InfrastructureFee.ToDecimal().Div(two).Ceil())
   829  	lf, _ := num.UintFromDecimal(fee.LiquidityFee.ToDecimal().Div(two).Ceil())
   830  	bbf, _ := num.UintFromDecimal(fee.BuyBackFee.ToDecimal().Div(two).Ceil())
   831  	tf, _ := num.UintFromDecimal(fee.TreasuryFee.ToDecimal().Div(two).Ceil())
   832  	return &types.Fee{
   833  		MakerFee:          num.UintZero(),
   834  		InfrastructureFee: inf,
   835  		LiquidityFee:      lf,
   836  		TreasuryFee:       tf,
   837  		BuyBackFee:        bbf,
   838  	}
   839  }
   840  
   841  func (e *Engine) getAuctionModeFeeReferrerRewardTransfers(reward *num.Uint, p, referrer string) []*types.Transfer {
   842  	return []*types.Transfer{
   843  		{
   844  			Owner: p,
   845  			Amount: &types.FinancialAmount{
   846  				Amount: reward.Clone(),
   847  				Asset:  e.asset,
   848  			},
   849  			Type:      types.TransferTypeFeeReferrerRewardPay,
   850  			MinAmount: reward.Clone(),
   851  		}, {
   852  			Owner: referrer,
   853  			Amount: &types.FinancialAmount{
   854  				Amount: reward.Clone(),
   855  				Asset:  e.asset,
   856  			},
   857  			Type:      types.TransferTypeFeeReferrerRewardDistribute,
   858  			MinAmount: reward.Clone(),
   859  		},
   860  	}
   861  }
   862  
   863  func (e *Engine) getAuctionModeFeeTransfers(infraFee, liquiFee, buyBackFee, treasuryFee *num.Uint, p string) []*types.Transfer {
   864  	// we return both transfer for the party in a slice
   865  	// always the infrastructure fee first
   866  	return []*types.Transfer{
   867  		{
   868  			Owner: p,
   869  			Amount: &types.FinancialAmount{
   870  				Asset:  e.asset,
   871  				Amount: infraFee.Clone(),
   872  			},
   873  			Type: types.TransferTypeInfrastructureFeePay,
   874  		},
   875  		{
   876  			Owner: p,
   877  			Amount: &types.FinancialAmount{
   878  				Asset:  e.asset,
   879  				Amount: liquiFee.Clone(),
   880  			},
   881  			Type: types.TransferTypeLiquidityFeePay,
   882  		},
   883  		{
   884  			Owner: p,
   885  			Amount: &types.FinancialAmount{
   886  				Asset:  e.asset,
   887  				Amount: buyBackFee.Clone(),
   888  			},
   889  			Type: types.TransferTypeBuyBackFeePay,
   890  		},
   891  		{
   892  			Owner: p,
   893  			Amount: &types.FinancialAmount{
   894  				Asset:  e.asset,
   895  				Amount: treasuryFee.Clone(),
   896  			},
   897  			Type: types.TransferTypeTreasuryPay,
   898  		},
   899  	}
   900  }
   901  
   902  type feesTransfer struct {
   903  	totalFeesAmountsPerParty map[string]*num.Uint
   904  	transfers                []*types.Transfer
   905  }
   906  
   907  func (f *feesTransfer) TotalFeesAmountPerParty() map[string]*num.Uint {
   908  	ret := make(map[string]*num.Uint, len(f.totalFeesAmountsPerParty))
   909  	for k, v := range f.totalFeesAmountsPerParty {
   910  		ret[k] = v.Clone()
   911  	}
   912  	return ret
   913  }
   914  func (f *feesTransfer) Transfers() []*types.Transfer { return f.transfers }
   915  
   916  func (e *Engine) OnFeeFactorsMakerFeeUpdate(f num.Decimal) {
   917  	e.feeCfg.Factors.MakerFee = f
   918  	e.f.makerFee = f
   919  }
   920  
   921  func (e *Engine) OnFeeFactorsBuyBackFeeUpdate(f num.Decimal) {
   922  	e.feeCfg.Factors.BuyBackFee = f
   923  	e.f.buyBackFee = f
   924  }
   925  
   926  func (e *Engine) OnFeeFactorsTreasuryFeeUpdate(f num.Decimal) {
   927  	e.feeCfg.Factors.TreasuryFee = f
   928  	e.f.treasuryFee = f
   929  }
   930  
   931  func (e *Engine) OnFeeFactorsInfrastructureFeeUpdate(f num.Decimal) {
   932  	e.feeCfg.Factors.InfrastructureFee = f
   933  	e.f.infrastructureFee = f
   934  }
   935  
   936  func (e *Engine) GetLiquidityFee() num.Decimal {
   937  	return e.f.liquidityFee
   938  }