OSDN Git Service

f3d1dfc3d2165e6ae13845a857df078360e0355c
[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         // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
12         ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
13 )
14
15 // AllocatedAssets represent reallocated assets after calculating fees
16 type AllocatedAssets struct {
17         Receives []*bc.AssetAmount
18         Refunds  []RefundAssets
19         Fees     []*bc.AssetAmount
20 }
21
22 // RefundAssets represent alias for assetAmount array, because each transaction participant can be refunded multiple assets
23 type RefundAssets []*bc.AssetAmount
24
25 // FeeStrategy used to indicate how to charge a matching fee
26 type FeeStrategy interface {
27         // Allocate will allocate the price differential in matching transaction to the participants and the fee
28         // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
29         // @param priceDiffs price differential of matching transaction
30         // @return reallocated assets after calculating fees
31         Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
32
33         // Validate verify that the fee charged for a matching transaction is correct
34         Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
35 }
36
37 // DefaultFeeStrategy represent the default fee charge strategy
38 type DefaultFeeStrategy struct {}
39
40 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
41 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
42         return &DefaultFeeStrategy{}
43 }
44
45 // Allocate will allocate the price differential in matching transaction to the participants and the fee
46 func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
47         feeMap := make(map[bc.AssetID]uint64)
48         for _, priceDiff := range priceDiffs {
49                 feeMap[*priceDiff.AssetId] = priceDiff.Amount
50         }
51
52         var fees []*bc.AssetAmount
53         refunds := make([]RefundAssets, len(receiveAmounts))
54         receives := make([]*bc.AssetAmount, len(receiveAmounts))
55
56         for i, receiveAmount := range receiveAmounts {
57                 amount := receiveAmount.Amount
58                 minFeeAmount := d.calcMinFeeAmount(amount)
59                 receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
60                 feeMap[*receiveAmount.AssetId] += minFeeAmount
61
62                 maxFeeAmount := d.calcMaxFeeAmount(amount)
63                 feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
64                 if feeAmount > maxFeeAmount {
65                         reminder = feeAmount - maxFeeAmount
66                         feeAmount = maxFeeAmount
67                 }
68
69                 fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
70
71                 // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
72                 averageAmount := reminder / uint64(len(receiveAmounts))
73                 if averageAmount == 0 {
74                         averageAmount = 1
75                 }
76
77                 for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
78                         refundAmount := averageAmount
79                         if j == len(receiveAmounts)-1 {
80                                 refundAmount = reminder
81                         }
82                         refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
83                         reminder -= averageAmount
84                 }
85         }
86         return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
87 }
88
89 // Validate verify that the fee charged for a matching transaction is correct
90 func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
91         for _, receiveAmount := range receiveAmounts {
92                 feeAmount := feeAmounts[*receiveAmount.AssetId]
93                 maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount)
94                 minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount)
95                 if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
96                         return ErrAmountOfFeeOutOfRange
97                 }
98         }
99         return nil
100 }
101
102 func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
103         return uint64(math.Ceil(float64(amount) / 1000))
104 }
105
106 func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
107         return uint64(math.Ceil(float64(amount) * 0.05))
108 }