OSDN Git Service

versoin1.1.9 (#594)
[bytom/vapor.git] / application / mov / match / fee_strategy.go
index 70a58a6..3d0de99 100644 (file)
@@ -8,49 +8,31 @@ 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
-       Refunds  RefundAssets
        Fees     []*bc.AssetAmount
 }
 
-// RefundAssets represent alias for assetAmount array, because each transaction participant can be refunded multiple assets
-type RefundAssets [][]*bc.AssetAmount
-
-// Add used to add a refund to specify order
-func (r RefundAssets) Add(index int, asset bc.AssetID, amount uint64) {
-       if index >= len(r) {
-               index = 0
-       }
-
-       for _, assetAmount := range r[index] {
-               if *assetAmount.AssetId == asset {
-                       assetAmount.Amount += amount
-                       return
-               }
-       }
-       r[index] = append(r[index], &bc.AssetAmount{AssetId: &asset, Amount: amount})
-}
-
 // FeeStrategy used to indicate how to charge a matching fee
 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
-type DefaultFeeStrategy struct {}
+type DefaultFeeStrategy struct{}
 
 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
@@ -58,66 +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 {
-       feeMap := make(map[bc.AssetID]uint64)
-       for _, priceDiff := range priceDiffs {
-               feeMap[*priceDiff.AssetId] = priceDiff.Amount
-       }
-
-       var fees []*bc.AssetAmount
-       refunds := make([][]*bc.AssetAmount, len(receiveAmounts))
+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 {
-               amount := receiveAmount.Amount
-               minFeeAmount := d.calcMinFeeAmount(amount)
-               receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
-               feeMap[*receiveAmount.AssetId] += minFeeAmount
-
-               maxFeeAmount := d.calcMaxFeeAmount(amount)
-               feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
-               if feeAmount > maxFeeAmount {
-                       reminder = feeAmount - maxFeeAmount
-                       feeAmount = maxFeeAmount
+               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}
+}
 
-               fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
+// Validate verify that the fee charged for a matching transaction is correct
+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
+               }
 
-               // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
-               averageAmount := reminder / uint64(len(receiveAmounts))
-               if averageAmount == 0 {
-                       averageAmount = 1
+               if existTaker {
+                       return ErrInvalidAmountOfFee
                }
 
-               for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
-                       refundAmount := averageAmount
-                       if j == len(receiveAmounts)-1 {
-                               refundAmount = reminder
+               for _, priceDiff := range priceDiffs {
+                       if *priceDiff.AssetId == *receiveAmount.AssetId {
+                               feeAmount += calcMinFeeAmount(priceDiff.Amount)
                        }
-                       refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
-                       reminder -= averageAmount
                }
+
+               if !equalsFeeAmount(realFeeAmount, feeAmount) {
+                       return ErrInvalidAmountOfFee
+               }
+               existTaker = true
        }
-       return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
+       return nil
 }
 
-// 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 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))
 }