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
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 // Add used to add a refund to specify order
26 func (r RefundAssets) Add(index int, asset bc.AssetID, amount uint64) {
31 for _, assetAmount := range r[index] {
32 if *assetAmount.AssetId == asset {
33 assetAmount.Amount += amount
37 r[index] = append(r[index], &bc.AssetAmount{AssetId: &asset, Amount: amount})
40 // FeeStrategy used to indicate how to charge a matching fee
41 type FeeStrategy interface {
42 // Allocate will allocate the price differential in matching transaction to the participants and the fee
43 // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
44 // @param priceDiffs price differential of matching transaction
45 // @return reallocated assets after calculating fees
46 Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
48 // Validate verify that the fee charged for a matching transaction is correct
49 Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
52 // DefaultFeeStrategy represent the default fee charge strategy
53 type DefaultFeeStrategy struct {}
55 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
56 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
57 return &DefaultFeeStrategy{}
60 // Allocate will allocate the price differential in matching transaction to the participants and the fee
61 func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
62 feeMap := make(map[bc.AssetID]uint64)
63 for _, priceDiff := range priceDiffs {
64 feeMap[*priceDiff.AssetId] = priceDiff.Amount
67 var fees []*bc.AssetAmount
68 refunds := make([][]*bc.AssetAmount, len(receiveAmounts))
69 receives := make([]*bc.AssetAmount, len(receiveAmounts))
71 for i, receiveAmount := range receiveAmounts {
72 amount := receiveAmount.Amount
73 minFeeAmount := d.calcMinFeeAmount(amount)
74 receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
75 feeMap[*receiveAmount.AssetId] += minFeeAmount
77 maxFeeAmount := d.calcMaxFeeAmount(amount)
78 feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
79 if feeAmount > maxFeeAmount {
80 reminder = feeAmount - maxFeeAmount
81 feeAmount = maxFeeAmount
84 fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
86 // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
87 averageAmount := reminder / uint64(len(receiveAmounts))
88 if averageAmount == 0 {
92 for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
93 refundAmount := averageAmount
94 if j == len(receiveAmounts)-1 {
95 refundAmount = reminder
97 refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
98 reminder -= averageAmount
101 return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
104 // Validate verify that the fee charged for a matching transaction is correct
105 func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
106 for _, receiveAmount := range receiveAmounts {
107 feeAmount := feeAmounts[*receiveAmount.AssetId]
108 maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount)
109 minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount)
110 if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
111 return ErrAmountOfFeeOutOfRange
117 func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
118 return uint64(math.Ceil(float64(amount) / 1000))
121 func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
122 return uint64(math.Ceil(float64(amount) * 0.05))