OSDN Git Service

versoin1.1.9 (#594)
[bytom/vapor.git] / application / mov / match / fee_strategy.go
index 1d307a0..3d0de99 100644 (file)
@@ -8,10 +8,12 @@ import (
 )
 
 var (
-       // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
-       ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
+       // ErrInvalidAmountOfFee represent The fee charged is invalid
+       ErrInvalidAmountOfFee = errors.New("amount of fee is invalid")
 )
 
+const forkBlockHeightAt20201028 = 78968400
+
 // AllocatedAssets represent reallocated assets after calculating fees
 type AllocatedAssets struct {
        Receives []*bc.AssetAmount
@@ -22,12 +24,11 @@ type AllocatedAssets struct {
 type FeeStrategy interface {
        // Allocate will allocate the price differential in matching transaction to the participants and the fee
        // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
-       // @param priceDiffs price differential of matching transaction
        // @return reallocated assets after calculating fees
-       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
+       Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets
 
        // Validate verify that the fee charged for a matching transaction is correct
-       Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
+       Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error
 }
 
 // DefaultFeeStrategy represent the default fee charge strategy
@@ -39,40 +40,84 @@ func NewDefaultFeeStrategy() *DefaultFeeStrategy {
 }
 
 // Allocate will allocate the price differential in matching transaction to the participants and the fee
-func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
+func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets {
        receives := make([]*bc.AssetAmount, len(receiveAmounts))
        fees := make([]*bc.AssetAmount, len(receiveAmounts))
 
        for i, receiveAmount := range receiveAmounts {
-               standFee := d.calcMinFeeAmount(receiveAmount.Amount)
-               fee := standFee + priceDiffs[i].Amount
-               if maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount); fee > maxFeeAmount {
-                       fee = maxFeeAmount
-               }
-
-               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - standFee}
+               fee := calcMinFeeAmount(receiveAmount.Amount)
+               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - fee}
                fees[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: fee}
+
+               if i == takerPos {
+                       for _, priceDiff := range priceDiffs {
+                               if *priceDiff.AssetId == *receiveAmount.AssetId {
+                                       fee = calcMinFeeAmount(priceDiff.Amount)
+                                       priceDiff.Amount -= fee
+                                       fees[i].Amount += fee
+                               }
+                       }
+               }
        }
        return &AllocatedAssets{Receives: receives, Fees: fees}
 }
 
 // Validate verify that the fee charged for a matching transaction is correct
-func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
+func (d *DefaultFeeStrategy) Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error {
+       if blockHeight < forkBlockHeightAt20201028 {
+               return legendValidateFee(receiveAmounts, feeAmounts)
+       }
+       return validateFee(receiveAmounts, priceDiffs, feeAmounts)
+}
+
+func validateFee(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
+       existTaker := false
+       for _, receiveAmount := range receiveAmounts {
+               feeAmount := calcMinFeeAmount(receiveAmount.Amount)
+               realFeeAmount := feeAmounts[*receiveAmount.AssetId]
+               if equalsFeeAmount(realFeeAmount, feeAmount) {
+                       continue
+               }
+
+               if existTaker {
+                       return ErrInvalidAmountOfFee
+               }
+
+               for _, priceDiff := range priceDiffs {
+                       if *priceDiff.AssetId == *receiveAmount.AssetId {
+                               feeAmount += calcMinFeeAmount(priceDiff.Amount)
+                       }
+               }
+
+               if !equalsFeeAmount(realFeeAmount, feeAmount) {
+                       return ErrInvalidAmountOfFee
+               }
+               existTaker = true
+       }
+       return nil
+}
+
+func equalsFeeAmount(realFeeAmount, feeAmount uint64) bool {
+       var tolerance float64 = 5
+       return math.Abs(float64(realFeeAmount)-float64(feeAmount)) < tolerance
+}
+
+func legendValidateFee(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
        for _, receiveAmount := range receiveAmounts {
-               feeAmount := feeAmounts[*receiveAmount.AssetId]
-               maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount)
-               minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount)
-               if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
-                       return ErrAmountOfFeeOutOfRange
+               realFeeAmount := feeAmounts[*receiveAmount.AssetId]
+               minFeeAmount := calcMinFeeAmount(receiveAmount.Amount)
+               maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount)
+               if realFeeAmount < minFeeAmount || realFeeAmount > maxFeeAmount {
+                       return ErrInvalidAmountOfFee
                }
        }
        return nil
 }
 
-func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
+func calcMinFeeAmount(amount uint64) uint64 {
        return uint64(math.Ceil(float64(amount) / 1000))
 }
 
-func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
+func calcMaxFeeAmount(amount uint64) uint64 {
        return uint64(math.Ceil(float64(amount) * 0.05))
 }