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) {
32 for _, assetAmount := range r[index] {
33 if *assetAmount.AssetId == asset {
34 assetAmount.Amount += amount
40 r[index] = append(r[index], &bc.AssetAmount{AssetId: &asset, Amount: amount})
44 // FeeStrategy used to indicate how to charge a matching fee
45 type FeeStrategy interface {
46 // Allocate will allocate the price differential in matching transaction to the participants and the fee
47 // @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
48 // @param priceDiffs price differential of matching transaction
49 // @return reallocated assets after calculating fees
50 Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
52 // Validate verify that the fee charged for a matching transaction is correct
53 Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
56 // DefaultFeeStrategy represent the default fee charge strategy
57 type DefaultFeeStrategy struct {}
59 // NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
60 func NewDefaultFeeStrategy() *DefaultFeeStrategy {
61 return &DefaultFeeStrategy{}
64 // Allocate will allocate the price differential in matching transaction to the participants and the fee
65 func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
66 feeMap := make(map[bc.AssetID]uint64)
67 for _, priceDiff := range priceDiffs {
68 feeMap[*priceDiff.AssetId] = priceDiff.Amount
71 var fees []*bc.AssetAmount
72 refunds := make([][]*bc.AssetAmount, len(receiveAmounts))
73 receives := make([]*bc.AssetAmount, len(receiveAmounts))
75 for i, receiveAmount := range receiveAmounts {
76 amount := receiveAmount.Amount
77 minFeeAmount := d.calcMinFeeAmount(amount)
78 receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
79 feeMap[*receiveAmount.AssetId] += minFeeAmount
81 maxFeeAmount := d.calcMaxFeeAmount(amount)
82 feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
83 if feeAmount > maxFeeAmount {
84 reminder = feeAmount - maxFeeAmount
85 feeAmount = maxFeeAmount
88 fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
90 // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
91 averageAmount := reminder / uint64(len(receiveAmounts))
92 if averageAmount == 0 {
96 for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
97 refundAmount := averageAmount
98 if j == len(receiveAmounts)-1 {
99 refundAmount = reminder
101 refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
102 reminder -= averageAmount
105 return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
108 // Validate verify that the fee charged for a matching transaction is correct
109 func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
110 for _, receiveAmount := range receiveAmounts {
111 feeAmount := feeAmounts[*receiveAmount.AssetId]
112 maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount)
113 minFeeAmount := d.calcMinFeeAmount(receiveAmount.Amount)
114 if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
115 return ErrAmountOfFeeOutOfRange
121 func (d *DefaultFeeStrategy) calcMinFeeAmount(amount uint64) uint64 {
122 return uint64(math.Ceil(float64(amount) / 1000))
125 func (d *DefaultFeeStrategy) calcMaxFeeAmount(amount uint64) uint64 {
126 return uint64(math.Ceil(float64(amount) * 0.05))