return tx, nil
}
-func (e *Engine) addMatchTxFeeOutput(txData *types.TxData, refunds RefundAssets, fees []*bc.AssetAmount) error {
+func (e *Engine) addMatchTxFeeOutput(txData *types.TxData, fees []*bc.AssetAmount) error {
for _, feeAmount := range fees {
txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*feeAmount.AssetId, feeAmount.Amount, e.rewardProgram))
}
- for i, refund := range refunds {
- // each trading participant may be refunded multiple assets
- for _, assetAmount := range refund {
- contractArgs, err := segwit.DecodeP2WMCProgram(txData.Inputs[i].ControlProgram())
- if err != nil {
- return err
- }
+ refoundAmount := map[bc.AssetID]uint64{}
+ assetIDs := []bc.AssetID{}
+ refoundScript := [][]byte{}
+ for _, input := range txData.Inputs {
+ refoundAmount[input.AssetID()] += input.Amount()
+ contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
+ if err != nil {
+ return err
+ }
+
+ assetIDs = append(assetIDs, input.AssetID())
+ refoundScript = append(refoundScript, contractArgs.SellerProgram)
+ }
- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*assetAmount.AssetId, assetAmount.Amount, contractArgs.SellerProgram))
+ for _, output := range txData.Outputs {
+ assetAmount := output.AssetAmount()
+ refoundAmount[*assetAmount.AssetId] -= assetAmount.Amount
+ }
+
+ refoundCount := len(refoundScript)
+ for _, assetID := range assetIDs {
+ amount := refoundAmount[assetID]
+ averageAmount := amount / uint64(refoundCount)
+ if averageAmount == 0 {
+ averageAmount = 1
+ }
+
+ for i := 0; i < refoundCount && amount > 0; i++ {
+ if i == refoundCount-1 {
+ averageAmount = amount
+ }
+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(assetID, averageAmount, refoundScript[i]))
+ amount -= averageAmount
}
}
return nil
return nil, err
}
- if err := e.addMatchTxFeeOutput(txData, allocatedAssets.Refunds, allocatedAssets.Fees); err != nil {
+ if err := e.addMatchTxFeeOutput(txData, allocatedAssets.Fees); err != nil {
return nil, err
}
txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, allocatedAssets.Receives[i].Amount, contractArgs.SellerProgram))
if isPartialTrade {
txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, exchangeAmount, order.Utxo.ControlProgram))
- } else if exchangeAmount > 0 {
- allocatedAssets.Refunds.Add(i, *order.FromAssetID, exchangeAmount)
}
}
return nil
for i, receivedAmount := range receivedAmounts {
oppositeShouldPayAmount := shouldPayAmounts[calcOppositeIndex(len(orders), i)]
+ priceDiffs = append(priceDiffs, &bc.AssetAmount{AssetId: oppositeShouldPayAmount.AssetId, Amount: 0})
if oppositeShouldPayAmount.Amount > receivedAmount.Amount {
- assetID := oppositeShouldPayAmount.AssetId
- amount := oppositeShouldPayAmount.Amount - receivedAmount.Amount
- priceDiffs = append(priceDiffs, &bc.AssetAmount{AssetId: assetID, Amount: amount})
+ priceDiffs[i].Amount = oppositeShouldPayAmount.Amount - receivedAmount.Amount
}
}
return receivedAmounts, priceDiffs
// AllocatedAssets represent reallocated assets after calculating fees
type AllocatedAssets struct {
Receives []*bc.AssetAmount
- Refunds RefundAssets
Fees []*bc.AssetAmount
}
-// RefundAssets represent alias for assetAmount array, because each transaction participant can be refunded multiple assets
-type RefundAssets [][]*bc.AssetAmount
-
-// Add used to add a refund to specify order
-func (r RefundAssets) Add(index int, asset bc.AssetID, amount uint64) {
- if index >= len(r) {
- index = 0
- }
-
- for _, assetAmount := range r[index] {
- if *assetAmount.AssetId == asset {
- assetAmount.Amount += amount
- return
- }
- }
- r[index] = append(r[index], &bc.AssetAmount{AssetId: &asset, Amount: amount})
-}
-
// FeeStrategy used to indicate how to charge a matching fee
type FeeStrategy interface {
// Allocate will allocate the price differential in matching transaction to the participants and the fee
}
// DefaultFeeStrategy represent the default fee charge strategy
-type DefaultFeeStrategy struct {}
+type DefaultFeeStrategy struct{}
// NewDefaultFeeStrategy return a new instance of DefaultFeeStrategy
func NewDefaultFeeStrategy() *DefaultFeeStrategy {
// Allocate will allocate the price differential in matching transaction to the participants and the fee
func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
- feeMap := make(map[bc.AssetID]uint64)
- for _, priceDiff := range priceDiffs {
- feeMap[*priceDiff.AssetId] = priceDiff.Amount
- }
-
- var fees []*bc.AssetAmount
- refunds := make([][]*bc.AssetAmount, len(receiveAmounts))
receives := make([]*bc.AssetAmount, len(receiveAmounts))
+ fees := make([]*bc.AssetAmount, len(receiveAmounts))
for i, receiveAmount := range receiveAmounts {
- amount := receiveAmount.Amount
- minFeeAmount := d.calcMinFeeAmount(amount)
- receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
- feeMap[*receiveAmount.AssetId] += minFeeAmount
-
- maxFeeAmount := d.calcMaxFeeAmount(amount)
- feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
- if feeAmount > maxFeeAmount {
- reminder = feeAmount - maxFeeAmount
- feeAmount = maxFeeAmount
+ standFee := d.calcMinFeeAmount(receiveAmount.Amount)
+ fee := standFee + priceDiffs[i].Amount
+ if maxFeeAmount := d.calcMaxFeeAmount(receiveAmount.Amount); fee > maxFeeAmount {
+ fee = maxFeeAmount
}
- fees = append(fees, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: feeAmount})
-
- // There is the remaining amount after paying the handling fee, assign it evenly to participants in the transaction
- averageAmount := reminder / uint64(len(receiveAmounts))
- if averageAmount == 0 {
- averageAmount = 1
- }
-
- for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
- refundAmount := averageAmount
- if j == len(receiveAmounts)-1 {
- refundAmount = reminder
- }
- refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
- reminder -= averageAmount
- }
+ receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: receiveAmount.Amount - standFee}
+ fees[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: fee}
}
- return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
+ return &AllocatedAssets{Receives: receives, Fees: fees}
}
// Validate verify that the fee charged for a matching transaction is correct
types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 25, RewardProgram),
types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 1, RewardProgram),
// refund
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 38, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
},
}),
}