6 "github.com/bytom/vapor/errors"
7 "github.com/bytom/vapor/protocol/bc"
11 // ErrInvalidAmountOfFee represent The fee charged is invalid
12 ErrInvalidAmountOfFee = errors.New("amount of fee is invalid")
15 const forkBlockHeightAt20201028 = 78968400
17 // AllocatedAssets represent reallocated assets after calculating fees
18 type AllocatedAssets struct {
19 Receives []*bc.AssetAmount
20 Fees []*bc.AssetAmount
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
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
34 // DefaultFeeStrategy represent the default fee charge strategy
35 type DefaultFeeStrategy struct{}
37 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
38 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
39 return &DefaultFeeStrategy{}
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))
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}
53 for _, priceDiff := range priceDiffs {
54 if *priceDiff.AssetId == *receiveAmount.AssetId {
55 fee = calcMinFeeAmount(priceDiff.Amount)
56 priceDiff.Amount -= fee
62 return &AllocatedAssets{Receives: receives, Fees: fees}
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)
70 return validateFee(receiveAmounts, priceDiffs, feeAmounts)
73 func validateFee(receiveAmounts, priceDiffs []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
75 for _, receiveAmount := range receiveAmounts {
76 feeAmount := calcMinFeeAmount(receiveAmount.Amount)
77 realFeeAmount := feeAmounts[*receiveAmount.AssetId]
78 if equalsFeeAmount(realFeeAmount, feeAmount) {
83 return ErrInvalidAmountOfFee
86 for _, priceDiff := range priceDiffs {
87 if *priceDiff.AssetId == *receiveAmount.AssetId {
88 feeAmount += calcMinFeeAmount(priceDiff.Amount)
92 if !equalsFeeAmount(realFeeAmount, feeAmount) {
93 return ErrInvalidAmountOfFee
100 func equalsFeeAmount(realFeeAmount, feeAmount uint64) bool {
101 var tolerance float64 = 5
102 return math.Abs(float64(realFeeAmount)-float64(feeAmount)) < tolerance
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
117 func calcMinFeeAmount(amount uint64) uint64 {
118 return uint64(math.Ceil(float64(amount) / 1000))
121 func calcMaxFeeAmount(amount uint64) uint64 {
122 return uint64(math.Ceil(float64(amount) * 0.05))