OSDN Git Service

versoin1.1.9 (#594)
[bytom/vapor.git] / application / mov / match / fee_strategy.go
1 package match
2
3 import (
4         "math"
5
6         "github.com/bytom/vapor/errors"
7         "github.com/bytom/vapor/protocol/bc"
8 )
9
10 var (
11         // ErrInvalidAmountOfFee represent The fee charged is invalid
12         ErrInvalidAmountOfFee = errors.New("amount of fee is invalid")
13 )
14
15 const forkBlockHeightAt20201028 = 78968400
16
17 // AllocatedAssets represent reallocated assets after calculating fees
18 type AllocatedAssets struct {
19         Receives []*bc.AssetAmount
20         Fees     []*bc.AssetAmount
21 }
22
23 // FeeStrategy used to indicate how to charge a matching fee
24 type FeeStrategy interface {
25         // Allocate will allocate the price differential in matching transaction to the participants and the fee
26         // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
27         // @return reallocated assets after calculating fees
28         Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets
29
30         // Validate verify that the fee charged for a matching transaction is correct
31         Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error
32 }
33
34 // DefaultFeeStrategy represent the default fee charge strategy
35 type DefaultFeeStrategy struct{}
36
37 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
38 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
39         return &DefaultFeeStrategy{}
40 }
41
42 // Allocate will allocate the price differential in matching transaction to the participants and the fee
43 func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount, takerPos int) *AllocatedAssets {
44         receives := make([]*bc.AssetAmount, len(receiveAmounts))
45         fees := make([]*bc.AssetAmount, len(receiveAmounts))
46
47         for i, receiveAmount := range receiveAmounts {
48                 fee := calcMinFeeAmount(receiveAmount.Amount)
49                 receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - fee}
50                 fees[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: fee}
51
52                 if i == takerPos {
53                         for _, priceDiff := range priceDiffs {
54                                 if *priceDiff.AssetId == *receiveAmount.AssetId {
55                                         fee = calcMinFeeAmount(priceDiff.Amount)
56                                         priceDiff.Amount -= fee
57                                         fees[i].Amount += fee
58                                 }
59                         }
60                 }
61         }
62         return &AllocatedAssets{Receives: receives, Fees: fees}
63 }
64
65 // Validate verify that the fee charged for a matching transaction is correct
66 func (d *DefaultFeeStrategy) Validate(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64, blockHeight uint64) error {
67         if blockHeight < forkBlockHeightAt20201028 {
68                 return legendValidateFee(receiveAmounts, feeAmounts)
69         }
70         return validateFee(receiveAmounts, priceDiffs, feeAmounts)
71 }
72
73 func validateFee(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
74         existTaker := false
75         for _, receiveAmount := range receiveAmounts {
76                 feeAmount := calcMinFeeAmount(receiveAmount.Amount)
77                 realFeeAmount := feeAmounts[*receiveAmount.AssetId]
78                 if equalsFeeAmount(realFeeAmount, feeAmount) {
79                         continue
80                 }
81
82                 if existTaker {
83                         return ErrInvalidAmountOfFee
84                 }
85
86                 for _, priceDiff := range priceDiffs {
87                         if *priceDiff.AssetId == *receiveAmount.AssetId {
88                                 feeAmount += calcMinFeeAmount(priceDiff.Amount)
89                         }
90                 }
91
92                 if !equalsFeeAmount(realFeeAmount, feeAmount) {
93                         return ErrInvalidAmountOfFee
94                 }
95                 existTaker = true
96         }
97         return nil
98 }
99
100 func equalsFeeAmount(realFeeAmount, feeAmount uint64) bool {
101         var tolerance float64 = 5
102         return math.Abs(float64(realFeeAmount)-float64(feeAmount)) < tolerance
103 }
104
105 func legendValidateFee(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
106         for _, receiveAmount := range receiveAmounts {
107                 realFeeAmount := feeAmounts[*receiveAmount.AssetId]
108                 minFeeAmount := calcMinFeeAmount(receiveAmount.Amount)
109                 maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount)
110                 if realFeeAmount < minFeeAmount || realFeeAmount > maxFeeAmount {
111                         return ErrInvalidAmountOfFee
112                 }
113         }
114         return nil
115 }
116
117 func calcMinFeeAmount(amount uint64) uint64 {
118         return uint64(math.Ceil(float64(amount) / 1000))
119 }
120
121 func calcMaxFeeAmount(amount uint64) uint64 {
122         return uint64(math.Ceil(float64(amount) * 0.05))
123 }