receivedAmounts, priceDiffs := CalcReceivedAmount(orders)
allocatedAssets := e.feeStrategy.Allocate(receivedAmounts, priceDiffs)
- if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets.Received); err != nil {
+ if err := addMatchTxOutput(txData, orders, receivedAmounts, allocatedAssets.Receives); err != nil {
return nil, err
}
return types.NewTx(*txData), nil
}
-func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts, receivedAfterDeductFee []*bc.AssetAmount) error {
+func addMatchTxOutput(txData *types.TxData, orders []*common.Order, receivedAmounts, deductFeeReceives []*bc.AssetAmount) error {
for i, order := range orders {
contractArgs, err := segwit.DecodeP2WMCProgram(order.Utxo.ControlProgram)
if err != nil {
shouldPayAmount := calcShouldPayAmount(receivedAmount, contractArgs.RatioNumerator, contractArgs.RatioDenominator)
isPartialTrade := requestAmount > receivedAmount
- setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAfterDeductFee[i].Amount)
- txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, receivedAfterDeductFee[i].Amount, contractArgs.SellerProgram))
+ setMatchTxArguments(txData.Inputs[i], isPartialTrade, len(txData.Outputs), receivedAmounts[i].Amount)
+ txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.ToAssetID, deductFeeReceives[i].Amount, contractArgs.SellerProgram))
if isPartialTrade {
txData.Outputs = append(txData.Outputs, types.NewIntraChainOutput(*order.FromAssetID, order.Utxo.Amount-shouldPayAmount, order.Utxo.ControlProgram))
}
}
// CalcReceivedAmount return amount of assets received by each participant in the matching transaction and the price difference
-func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, map[bc.AssetID]int64) {
- priceDiffs := make(map[bc.AssetID]int64)
- var receivedAmounts, shouldPayAmounts []*bc.AssetAmount
+func CalcReceivedAmount(orders []*common.Order) ([]*bc.AssetAmount, []*bc.AssetAmount) {
+ var receivedAmounts, priceDiffs, shouldPayAmounts []*bc.AssetAmount
for i, order := range orders {
requestAmount := CalcRequestAmount(order.Utxo.Amount, order.RatioNumerator, order.RatioDenominator)
oppositeOrder := orders[calcOppositeIndex(len(orders), i)]
if oppositeShouldPayAmount.Amount > receivedAmount.Amount {
assetId := oppositeShouldPayAmount.AssetId
amount := oppositeShouldPayAmount.Amount - receivedAmount.Amount
- priceDiffs[*assetId] = int64(amount)
+ priceDiffs = append(priceDiffs, &bc.AssetAmount{AssetId: assetId, Amount: amount})
}
}
return receivedAmounts, priceDiffs
)
var (
- // ErrAmountOfFeeExceedMaximum represent The fee charged is exceeded the maximum
- ErrAmountOfFeeExceedMaximum = errors.New("amount of fee greater than max fee amount")
+ // ErrAmountOfFeeOutOfRange represent The fee charged is out of range
+ ErrAmountOfFeeOutOfRange = errors.New("amount of fee is out of range")
)
// AllocatedAssets represent reallocated assets after calculating fees
type AllocatedAssets struct {
- Received []*bc.AssetAmount
+ Receives []*bc.AssetAmount
Refunds []RefundAssets
Fees []*bc.AssetAmount
}
// @param receiveAmounts the amount of assets that the participants in the matching transaction can received when no fee is considered
// @param priceDiffs price differential of matching transaction
// @return reallocated assets after calculating fees
- Allocate(receiveAmounts []*bc.AssetAmount, priceDiffs map[bc.AssetID]int64) *AllocatedAssets
+ Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets
// Validate verify that the fee charged for a matching transaction is correct
- Validate(receiveAmounts []*bc.AssetAmount, priceDiffs, feeAmounts map[bc.AssetID]int64) error
+ Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error
}
// DefaultFeeStrategy represent the default fee charge strategy
}
// Allocate will allocate the price differential in matching transaction to the participants and the fee
-func (d *DefaultFeeStrategy) Allocate(receiveAmounts []*bc.AssetAmount, priceDiffs map[bc.AssetID]int64) *AllocatedAssets {
- var feeAmounts []*bc.AssetAmount
- refundAmounts := make([]RefundAssets, len(receiveAmounts))
+func (d *DefaultFeeStrategy) Allocate(receiveAmounts, priceDiffs []*bc.AssetAmount) *AllocatedAssets {
+ feeMap := make(map[bc.AssetID]uint64)
+ for _, priceDiff := range priceDiffs {
+ feeMap[*priceDiff.AssetId] = priceDiff.Amount
+ }
- for _, receiveAmount := range receiveAmounts {
- if _, ok := priceDiffs[*receiveAmount.AssetId]; !ok {
- continue
- }
+ var fees []*bc.AssetAmount
+ refunds := make([]RefundAssets, len(receiveAmounts))
+ receives := make([]*bc.AssetAmount, len(receiveAmounts))
- priceDiff := priceDiffs[*receiveAmount.AssetId]
- maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount, d.maxFeeRate)
+ for i, receiveAmount := range receiveAmounts {
+ amount := receiveAmount.Amount
+ minFeeAmount := calcMinFeeAmount(amount)
+ receives[i] = &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: amount - minFeeAmount}
+ feeMap[*receiveAmount.AssetId] += minFeeAmount
- feeAmount, reminder := priceDiff, int64(0)
- if priceDiff > maxFeeAmount {
+ maxFeeAmount := calcMaxFeeAmount(amount, d.maxFeeRate)
+ feeAmount, reminder := feeMap[*receiveAmount.AssetId], uint64(0)
+ if feeAmount > maxFeeAmount {
+ reminder = feeAmount - maxFeeAmount
feeAmount = maxFeeAmount
- reminder = priceDiff - maxFeeAmount
}
- feeAmounts = append(feeAmounts, &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: uint64(feeAmount)})
+ if feeAmount > 0 {
+ 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 / int64(len(receiveAmounts))
+ averageAmount := reminder / uint64(len(receiveAmounts))
if averageAmount == 0 {
averageAmount = 1
}
- for i := 0; i < len(receiveAmounts) && reminder > 0; i++ {
- amount := averageAmount
- if i == len(receiveAmounts)-1 {
- amount = reminder
+ for j := 0; j < len(receiveAmounts) && reminder > 0; j++ {
+ refundAmount := averageAmount
+ if j == len(receiveAmounts)-1 {
+ refundAmount = reminder
}
- refundAmounts[i] = append(refundAmounts[i], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: uint64(amount)})
+ refunds[j] = append(refunds[j], &bc.AssetAmount{AssetId: receiveAmount.AssetId, Amount: refundAmount})
reminder -= averageAmount
}
}
-
- receivedAfterDeductFee := make([]*bc.AssetAmount, len(receiveAmounts))
- copy(receivedAfterDeductFee, receiveAmounts)
- return &AllocatedAssets{Received: receivedAfterDeductFee, Refunds: refundAmounts, Fees: feeAmounts}
+ return &AllocatedAssets{Receives: receives, Refunds: refunds, Fees: fees}
}
// Validate verify that the fee charged for a matching transaction is correct
-func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts, priceDiffs map[bc.AssetID]int64) error {
+func (d *DefaultFeeStrategy) Validate(receiveAmounts []*bc.AssetAmount, feeAmounts map[bc.AssetID]uint64) error {
for _, receiveAmount := range receiveAmounts {
- if feeAmount, ok := feeAmounts[*receiveAmount.AssetId]; ok {
- if feeAmount > calcMaxFeeAmount(receiveAmount.Amount+uint64(priceDiffs[*receiveAmount.AssetId]), d.maxFeeRate) {
- return ErrAmountOfFeeExceedMaximum
- }
+ feeAmount := feeAmounts[*receiveAmount.AssetId]
+ maxFeeAmount := calcMaxFeeAmount(receiveAmount.Amount, d.maxFeeRate)
+ minFeeAmount := calcMinFeeAmount(receiveAmount.Amount)
+ if feeAmount < minFeeAmount || feeAmount > maxFeeAmount {
+ return ErrAmountOfFeeOutOfRange
}
}
return nil
}
-func calcMaxFeeAmount(amount uint64, maxFeeRate float64) int64 {
- return int64(math.Ceil(float64(amount) * maxFeeRate))
+func calcMinFeeAmount(amount uint64) uint64 {
+ return uint64(math.Ceil(float64(amount) / 1000))
+}
+
+func calcMaxFeeAmount(amount uint64, maxFeeRate float64) uint64 {
+ return uint64(math.Ceil(float64(amount) * maxFeeRate))
}
types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[1].Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, Eth2BtcOrders[1].Utxo.SourcePos, Eth2BtcOrders[1].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
// re-order
types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
- types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[0].Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, Eth2BtcOrders[0].Utxo.SourcePos, Eth2BtcOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, RewardProgram),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 11, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
// re-order
types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 270, Eth2BtcOrders[2].Utxo.ControlProgram),
// fee
types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 25, RewardProgram),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 1, RewardProgram),
// refund
- types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
types.NewIntraChainOutput(*Eth2BtcOrders[2].FromAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, testutil.MustDecodeHash("39bdb7058a0c31fb740af8e3c382bf608efff1b041cd4dd461332722ad24552a"), *Eth2BtcOrders[2].FromAssetID, 270, 2, Eth2BtcOrders[2].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 270, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
+ types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 269, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
// re-order
types.NewIntraChainOutput(*Btc2EthOrders[1].FromAssetID, 15, Btc2EthOrders[1].Utxo.ControlProgram),
- types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 5, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 4, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[1].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[1], 0).Utxo.SourceID, *Eth2BtcOrders[1].FromAssetID, Eth2BtcOrders[1].Utxo.Amount, 0, Eth2BtcOrders[1].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 416, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 415, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
// re-order
types.NewIntraChainOutput(*Btc2EthOrders[0].FromAssetID, 2, Btc2EthOrders[0].Utxo.ControlProgram),
- types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 8, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 7, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19254")),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[1].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Etc2EosMakerTxs[0], 0).Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
- types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+ types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+ // fee
+ types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eos2BtcOrders[0].Utxo.SourceID, *Eos2BtcOrders[0].FromAssetID, Eos2BtcOrders[0].Utxo.Amount, Eos2BtcOrders[0].Utxo.SourcePos, Eos2BtcOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(ETH, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(EOS, 1000, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
- types.NewIntraChainOutput(BTC, 10, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+ types.NewIntraChainOutput(ETH, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(EOS, 999, testutil.MustDecodeHexString("0014e3178c0f294a9a8f4b304236406507913091df86")),
+ types.NewIntraChainOutput(BTC, 9, testutil.MustDecodeHexString("00144d0dfc8a0c5ce41d31d4f61d99aff70588bff8bc")),
+ // fee
+ types.NewIntraChainOutput(ETH, 1, RewardProgram),
+ types.NewIntraChainOutput(EOS, 1, RewardProgram),
+ types.NewIntraChainOutput(BTC, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
// re-order
types.NewIntraChainOutput(*Eth2BtcOrders[0].FromAssetID, 404, Eth2BtcOrders[0].Utxo.ControlProgram),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(2), vm.Int64Bytes(1)}, *Eth2BtcOrders[2].Utxo.SourceID, *Eth2BtcOrders[2].FromAssetID, Eth2BtcOrders[2].Utxo.Amount, Eth2BtcOrders[2].Utxo.SourcePos, Eth2BtcOrders[2].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 810, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
+ types.NewIntraChainOutput(*Btc2EthOrders[3].ToAssetID, 809, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19252")),
// re-order
types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, Btc2EthOrders[3].Utxo.ControlProgram),
- types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 15, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 14, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
// fee
- types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Btc2EthOrders[3].FromAssetID, 2, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[2].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Etc2EosOrders[0].Utxo.SourceID, *Etc2EosOrders[0].FromAssetID, Etc2EosOrders[0].Utxo.Amount, Etc2EosOrders[0].Utxo.SourcePos, Etc2EosOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 50, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
- types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 100, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+ types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 49, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 99, testutil.MustDecodeHexString("0014df7a97e53bbe278e4e44810b0a760fb472daa9a3")),
+ // fee
+ types.NewIntraChainOutput(*Eos2EtcOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Etc2EosOrders[0].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *MustNewOrderFromOutput(Eth2BtcMakerTxs[0], 0).Utxo.SourceID, *Eth2BtcOrders[0].FromAssetID, Eth2BtcOrders[0].Utxo.Amount, 0, Eth2BtcOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19253")),
types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 10, RewardProgram),
+ // fee
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 1, RewardProgram),
+ types.NewIntraChainOutput(*Eth2BtcOrders[0].ToAssetID, 1, RewardProgram),
},
}),
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *Eth2BtcOrders[3].Utxo.SourceID, *Eth2BtcOrders[3].FromAssetID, Eth2BtcOrders[3].Utxo.Amount, Eth2BtcOrders[3].Utxo.SourcePos, Eth2BtcOrders[3].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 4, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
+ types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 3, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19256")),
// fee
types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 25, RewardProgram),
types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 1, RewardProgram),
// refund
- types.NewIntraChainOutput(*Btc2EthOrders[0].ToAssetID, 37, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*Eth2BtcOrders[3].ToAssetID, 2, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ 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")),
},
// matchedTxFee is object to record the mov tx's fee information
type matchedTxFee struct {
rewardProgram []byte
- amount int64
+ amount uint64
}
// calcFeeAmount return the amount of fee in the matching transaction
dealProgMaps := make(map[string]bool)
for _, input := range matchedTx.Inputs {
- assetFeeMap[input.AssetID()] = &matchedTxFee{amount: int64(input.AssetAmount().Amount)}
+ assetFeeMap[input.AssetID()] = &matchedTxFee{amount: input.AssetAmount().Amount}
contractArgs, err := segwit.DecodeP2WMCProgram(input.ControlProgram())
if err != nil {
return nil, err
for _, output := range matchedTx.Outputs {
assetAmount := output.AssetAmount()
if _, ok := dealProgMaps[hex.EncodeToString(output.ControlProgram())]; ok || segwit.IsP2WMCScript(output.ControlProgram()) {
- assetFeeMap[*assetAmount.AssetId].amount -= int64(assetAmount.Amount)
+ assetFeeMap[*assetAmount.AssetId].amount -= assetAmount.Amount
if assetFeeMap[*assetAmount.AssetId].amount <= 0 {
delete(assetFeeMap, *assetAmount.AssetId)
}
return err
}
- feeAmounts := make(map[bc.AssetID]int64)
+ feeAmounts := make(map[bc.AssetID]uint64)
for assetID, fee := range matchedTxFees {
feeAmounts[assetID] = fee.amount
}
- receivedAmount, priceDiffs := match.CalcReceivedAmount(orders)
+ receivedAmount, _ := match.CalcReceivedAmount(orders)
feeStrategy := match.NewDefaultFeeStrategy(maxFeeRate)
- return feeStrategy.Validate(receivedAmount, feeAmounts, priceDiffs)
+ return feeStrategy.Validate(receivedAmount, feeAmounts)
}
func (m *MovCore) validateMatchedTxSequence(txs []*types.Tx) error {
types.NewSpendInput([][]byte{vm.Int64Bytes(1), vm.Int64Bytes(1)}, *mock.Eth2BtcOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Eth2BtcOrders[0].Utxo.Amount, mock.Eth2BtcOrders[0].Utxo.SourcePos, mock.Eth2BtcOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+ // fee
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
},
}),
},
types.NewSpendInput(nil, testutil.MustDecodeHash("28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21"), *consensus.BTMAssetID, 100, 0, []byte{0x51}),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
types.NewIntraChainOutput(*consensus.BTMAssetID, 100, []byte{0x51}),
},
}),
types.NewSpendInput([][]byte{{}, {}, vm.Int64Bytes(2)}, *mock.Btc2EthOrders[0].Utxo.SourceID, *mock.Btc2EthOrders[0].FromAssetID, mock.Btc2EthOrders[0].Utxo.Amount, mock.Btc2EthOrders[0].Utxo.SourcePos, mock.Btc2EthOrders[0].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("51")),
- types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 10, testutil.MustDecodeHexString("53")),
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 10, []byte{0x51}),
- types.NewIntraChainOutput(*consensus.BTMAssetID, 100, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("51")),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 9, testutil.MustDecodeHexString("53")),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 11, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[0].ToAssetID, 1, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].FromAssetID, 10, testutil.MustDecodeHexString("51")),
},
}),
},
types.NewSpendInput([][]byte{vm.Int64Bytes(10), vm.Int64Bytes(1), vm.Int64Bytes(0)}, *mock.Eth2BtcOrders[2].Utxo.SourceID, *mock.Eth2BtcOrders[2].FromAssetID, mock.Eth2BtcOrders[2].Utxo.Amount, mock.Eth2BtcOrders[2].Utxo.SourcePos, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
},
Outputs: []*types.TxOutput{
- types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 500, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
- types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 10, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[0].ToAssetID, 499, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19251")),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 9, testutil.MustDecodeHexString("0014f928b723999312df4ed51cb275a2644336c19255")),
// re-order
types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 270, mock.Eth2BtcOrders[2].Utxo.ControlProgram),
// fee
- types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].FromAssetID, 40, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Btc2EthOrders[2].ToAssetID, 41, mock.RewardProgram),
+ types.NewIntraChainOutput(*mock.Eth2BtcOrders[2].ToAssetID, 1, mock.RewardProgram),
},
}),
},
},
verifyResults: []*bc.TxVerifyResult{{StatusFail: false}},
- wantError: match.ErrAmountOfFeeExceedMaximum,
+ wantError: match.ErrAmountOfFeeOutOfRange,
},
{
desc: "ratio numerator is zero",
wantMatchedTxFee map[bc.AssetID]*matchedTxFee
}{
{
- desc: "fee less than max fee",
- maxFeeRate: 0.05,
- wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 10, rewardProgram: mock.RewardProgram}},
- tx: mock.MatchedTxs[1].TxData,
+ desc: "fee less than max fee",
+ maxFeeRate: 0.05,
+ wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+ mock.ETH: {amount: 11, rewardProgram: mock.RewardProgram},
+ mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+ },
+ tx: mock.MatchedTxs[1].TxData,
},
{
desc: "fee refund in tx",
maxFeeRate: 0.05,
- wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram}},
+ wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+ mock.ETH: {amount: 25, rewardProgram: mock.RewardProgram},
+ mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+ },
tx: mock.MatchedTxs[2].TxData,
},
{
- desc: "fee is zero",
+ desc: "no price diff",
maxFeeRate: 0.05,
- wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{},
+ wantMatchedTxFee: map[bc.AssetID]*matchedTxFee{
+ mock.ETH: {amount: 1, rewardProgram: mock.RewardProgram},
+ mock.BTC: {amount: 1, rewardProgram: mock.RewardProgram},
+ },
tx: mock.MatchedTxs[0].TxData,
},
}