6 "github.com/bytom/vapor/errors"
7 "github.com/bytom/vapor/protocol/bc"
11 // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
12 ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
15 // AllocatedAssets represent reallocated assets after calculating fees
16 type AllocatedAssets struct {
17 Receives []*bc.AssetAmount
18 Refunds []RefundAssets
19 Fees []*bc.AssetAmount
22 // RefundAssets represent alias for assetAmount array, because each transaction participant can be refunded multiple assets
23 type RefundAssets []*bc.AssetAmount
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
33 // Validate verify that the fee charged for a matching transaction is correct
34 Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
37 // DefaultFeeStrategy represent the default fee charge strategy
38 type DefaultFeeStrategy struct {}
40 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
41 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
42 return &DefaultFeeStrategy{}
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
52 var fees []*bc.AssetAmount
53 refunds := make([]RefundAssets, len(receiveAmounts))
54 receives := make([]*bc.AssetAmount, len(receiveAmounts))
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
62 maxFeeAmount := d.calcMaxFeeAmount(amount)
63 feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
64 if feeAmount > maxFeeAmount {
65 reminder = feeAmount - maxFeeAmount
66 feeAmount = maxFeeAmount
69 fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
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 {
77 for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
78 refundAmount := averageAmount
79 if j == len(receiveAmounts)-1 {
80 refundAmount = reminder
82 refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
83 reminder -= averageAmount
86 return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
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
102 func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
103 return uint64(math.Ceil(float64(amount) / 1000))
106 func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
107 return uint64(math.Ceil(float64(amount) * 0.05))