diff --git a/consensus/posv/posv.go b/consensus/posv/posv.go index 2a292c3de..e3789d178 100644 --- a/consensus/posv/posv.go +++ b/consensus/posv/posv.go @@ -71,6 +71,7 @@ type TomoXService interface { IsSDKNode() bool SyncDataToSDKNode(txDataMatch tomox_state.TxDataMatch, txHash common.Hash, txMatchTime time.Time, statedb *state.StateDB, dirtyOrderCount *uint64) error RollbackReorgTxMatch(txhash common.Hash) + GetTokenDecimal(chain consensus.ChainContext, statedb *state.StateDB, coinbase common.Address, tokenAddr common.Address) (*big.Int, error) } // Posv proof-of-stake-voting protocol constants. diff --git a/core/order_pool.go b/core/order_pool.go index 01c3c09d5..361ceac6b 100644 --- a/core/order_pool.go +++ b/core/order_pool.go @@ -19,6 +19,8 @@ package core import ( "errors" "fmt" + "github.com/tomochain/tomochain/consensus" + "github.com/tomochain/tomochain/consensus/posv" "github.com/tomochain/tomochain/tomox/tomox_state" "math/big" "sort" @@ -82,6 +84,13 @@ type blockChainTomox interface { OrderStateAt(block *types.Block) (*tomox_state.TomoXStateDB, error) StateAt(root common.Hash) (*state.StateDB, error) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription + Engine() consensus.Engine + // GetHeader returns the hash corresponding to their hash. + GetHeader(common.Hash, uint64) *types.Header + // CurrentHeader retrieves the current header from the local chain. + CurrentHeader() *types.Header + // Config retrieves the blockchain's chain configuration. + Config() *params.ChainConfig } // DefaultOrderPoolConfig contains the default configurations for the transaction @@ -453,16 +462,34 @@ func (pool *OrderPool) validateOrder(tx *types.OrderTransaction) error { return ErrInvalidOrderUserAddress } - statedb, err := pool.chain.StateAt(pool.chain.CurrentBlock().Root()) - if err != nil { - return fmt.Errorf("failed to get statedb Error: %v", err) - } - if !tomox_state.IsValidRelayer(statedb, tx.ExchangeAddress()) { + cloneStateDb := pool.currentRootState.Copy() + cloneTomoXStateDb := pool.currentOrderState.Copy() + + if !tomox_state.IsValidRelayer(cloneStateDb, tx.ExchangeAddress()) { return fmt.Errorf("invalid relayer. ExchangeAddress: %s", tx.ExchangeAddress().Hex()) } - if err := tomox_state.VerifyPair(statedb, tx.ExchangeAddress(), tx.BaseToken(), tx.QuoteToken()); err != nil { + if err := tomox_state.VerifyPair(cloneStateDb, tx.ExchangeAddress(), tx.BaseToken(), tx.QuoteToken()); err != nil { return err } + posvEngine, ok := pool.chain.Engine().(*posv.Posv) + if !ok { + return ErrNotPoSV + } + tomoXServ := posvEngine.GetTomoXService() + if tomoXServ == nil { + return fmt.Errorf("tomox not found in order validation") + } + baseDecimal, err := tomoXServ.GetTokenDecimal(pool.chain, cloneStateDb, pool.chain.CurrentBlock().Header().Coinbase, tx.BaseToken()) + if err != nil { + return fmt.Errorf("validateOrder: failed to get baseDecimal. err: %v", err) + } + quoteDecimal, err := tomoXServ.GetTokenDecimal(pool.chain, cloneStateDb, pool.chain.CurrentBlock().Header().Coinbase, tx.QuoteToken()) + if err != nil { + return fmt.Errorf("validateOrder: failed to get quoteDecimal. err: %v", err) + } + if err := tomox_state.VerifyBalance(cloneStateDb, cloneTomoXStateDb, tx, baseDecimal, quoteDecimal); err != nil { + return fmt.Errorf("not enough balance to make this order. OrderHash: %s. UserAddress: %s. PairName: %s. Err: %v", tx.Hash().Hex(), tx.UserAddress().Hex(), tx.PairName(), err) + } return nil } diff --git a/tomox/order_processor.go b/tomox/order_processor.go index dffc442cd..2f7053337 100644 --- a/tomox/order_processor.go +++ b/tomox/order_processor.go @@ -1,8 +1,6 @@ package tomox import ( - "encoding/json" - "errors" "github.com/tomochain/tomochain/consensus" "math/big" "strconv" @@ -15,9 +13,6 @@ import ( "github.com/tomochain/tomochain/tomox/tomox_state" ) -var emptyAddress = common.StringToAddress("") -var errQuantityTradeTooSmall = errors.New("Quantity trade too small") - func (tomox *TomoX) CommitOrder(coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tomoXstatedb *tomox_state.TomoXStateDB, orderBook common.Hash, order *tomox_state.OrderItem) ([]map[string]string, []*tomox_state.OrderItem, error) { snap := tomoXstatedb.Snapshot() trades, rejects, err := tomox.ApplyOrder(coinbase, chain, statedb, tomoXstatedb, orderBook, order) @@ -221,7 +216,7 @@ func (tomox *TomoX) processOrderList(coinbase common.Address, chain consensus.Ch quotePrice = tomoXstatedb.GetPrice(tomox_state.GetOrderBookHash(oldestOrder.QuoteToken, common.HexToAddress(common.TomoNativeAddress))) } tradedQuantity, rejectMaker, err := tomox.getTradeQuantity(quotePrice, coinbase, chain, statedb, order, &oldestOrder, maxTradedQuantity) - if err != nil && err == errQuantityTradeTooSmall { + if err != nil && err == tomox_state.ErrQuantityTradeTooSmall { if tradedQuantity.Cmp(maxTradedQuantity) == 0 { if quantityToTrade.Cmp(amount) == 0 { // reject Taker & maker rejects = append(rejects, order) @@ -346,10 +341,10 @@ func (tomox *TomoX) getTradeQuantity(quotePrice *big.Int, coinbase common.Addres log.Debug("GetTradeQuantity", "side", takerOrder.Side, "takerBalance", takerBalance, "makerBalance", makerBalance, "BaseToken", makerOrder.BaseToken, "QuoteToken", makerOrder.QuoteToken, "quantity", quantity, "rejectMaker", rejectMaker, "quotePrice", quotePrice) if quantity.Sign() > 0 { // Apply Match Order - setteBalance, err := GetSettleBalance(quotePrice, takerOrder.Side, takerFeeRate, makerOrder.BaseToken, makerOrder.QuoteToken, makerOrder.Price, makerFeeRate, baseTokenDecimal, quoteTokenDecimal, quantity) - log.Debug("GetSettleBalance", "setteBalance", setteBalance, "err", err) + settleBalanceResult, err := tomox_state.GetSettleBalance(quotePrice, takerOrder.Side, takerFeeRate, makerOrder.BaseToken, makerOrder.QuoteToken, makerOrder.Price, makerFeeRate, baseTokenDecimal, quoteTokenDecimal, quantity) + log.Debug("GetSettleBalance", "settleBalanceResult", settleBalanceResult, "err", err) if err == nil { - err = SetteBalance(coinbase, takerOrder, makerOrder, setteBalance, statedb) + err = DoSettleBalance(coinbase, takerOrder, makerOrder, settleBalanceResult, statedb) } return quantity, rejectMaker, err } @@ -452,156 +447,7 @@ func GetTradeQuantity(takerSide string, takerFeeRate *big.Int, takerBalance *big } } -type TradeResult struct { - Fee *big.Int - InToken common.Address - InQuantity *big.Int - InTotal *big.Int - OutToken common.Address - OutQuantity *big.Int - OutTotal *big.Int -} -type SettleBalance struct { - Taker TradeResult - Maker TradeResult -} - -func (settleBalance *SettleBalance) String() string { - json, _ := json.Marshal(settleBalance) - return string(json) -} - -func GetSettleBalance(quotePrice *big.Int, takerSide string, takerFeeRate *big.Int, baseToken, quoteToken common.Address, makerPrice *big.Int, makerFeeRate *big.Int, baseTokenDecimal *big.Int, quoteTokenDecimal *big.Int, quantityToTrade *big.Int) (*SettleBalance, error) { - log.Debug("GetSettleBalance", "takerSide", takerSide, "takerFeeRate", takerFeeRate, "baseToken", baseToken, "quoteToken", quoteToken, "makerPrice", makerPrice, "makerFeeRate", makerFeeRate, "baseTokenDecimal", baseTokenDecimal, "quantityToTrade", quantityToTrade) - var result *SettleBalance - //result = map[common.Address]map[string]interface{}{} - if takerSide == tomox_state.Bid { - // maker InQuantity quoteTokenQuantity=(quantityToTrade*maker.Price/baseTokenDecimal) - quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) - quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal) - // Fee - // charge on the token he/she has before the trade, in this case: quoteToken - // charge on the token he/she has before the trade, in this case: baseToken - // takerFee = quoteTokenQuantity*takerFeeRate/baseFee=(quantityToTrade*maker.Price/baseTokenDecimal) * makerFeeRate/baseFee - takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate) - takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee) - // charge on the token he/she has before the trade, in this case: baseToken - makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate) - makerFee = new(big.Int).Div(makerFee, common.TomoXBaseFee) - if quoteTokenQuantity.Cmp(makerFee) <= 0 { - log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee) - return result, errQuantityTradeTooSmall - } - if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 { - exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice) - exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal) - log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) - if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 { - log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) - return result, errQuantityTradeTooSmall - } - exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice) - exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal) - log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) - if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 { - log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) - return result, errQuantityTradeTooSmall - } - } - inTotal := new(big.Int).Sub(quoteTokenQuantity, makerFee) - //takerOutTotal= quoteTokenQuantity + takerFee = quantityToTrade*maker.Price/baseTokenDecimal + quantityToTrade*maker.Price/baseTokenDecimal * takerFeeRate/baseFee - // = quantityToTrade * maker.Price/baseTokenDecimal ( 1 + takerFeeRate/baseFee) - // = quantityToTrade * maker.Price * (baseFee + takerFeeRate ) / ( baseTokenDecimal * baseFee) - takerOutTotal := new(big.Int).Add(quoteTokenQuantity, takerFee) - - result = &SettleBalance{ - Taker: TradeResult{ - Fee: takerFee, - InToken: baseToken, - InQuantity: quantityToTrade, - InTotal: quantityToTrade, - OutToken: quoteToken, - OutQuantity: quoteTokenQuantity, - OutTotal: takerOutTotal, - }, - Maker: TradeResult{ - Fee: makerFee, - InToken: quoteToken, - InQuantity: quoteTokenQuantity, - InTotal: inTotal, - OutToken: baseToken, - OutQuantity: quantityToTrade, - OutTotal: quantityToTrade, - }, - } - } else { - // Taker InQuantity - // quoteTokenQuantity = quantityToTrade * makerPrice / baseTokenDecimal - quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) - quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal) - // maker InQuantity - - // Fee - // charge on the token he/she has before the trade, in this case: baseToken - // makerFee = quoteTokenQuantity * makerFeeRate / baseFee = quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee - // charge on the token he/she has before the trade, in this case: quoteToken - makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate) - makerFee = makerFee.Div(makerFee, common.TomoXBaseFee) - - // charge on the token he/she has before the trade, in this case: baseToken - takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate) - takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee) - if quoteTokenQuantity.Cmp(takerFee) <= 0 { - log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee) - return result, errQuantityTradeTooSmall - } - if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 { - exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice) - exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal) - log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) - if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 { - log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) - return result, errQuantityTradeTooSmall - } - exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice) - exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal) - log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) - if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 { - log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) - return result, errQuantityTradeTooSmall - } - } - inTotal := new(big.Int).Sub(quoteTokenQuantity, takerFee) - // makerOutTotal = quoteTokenQuantity + makerFee = quantityToTrade * makerPrice / baseTokenDecimal + quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee - // = quantityToTrade * makerPrice / baseTokenDecimal * (1+makerFeeRate / baseFee) - // = quantityToTrade * makerPrice * (baseFee + makerFeeRate) / ( baseTokenDecimal * baseFee ) - makerOutTotal := new(big.Int).Add(quoteTokenQuantity, makerFee) - // Fee - result = &SettleBalance{ - Taker: TradeResult{ - Fee: takerFee, - InToken: quoteToken, - InQuantity: quoteTokenQuantity, - InTotal: inTotal, - OutToken: baseToken, - OutQuantity: quantityToTrade, - OutTotal: quantityToTrade, - }, - Maker: TradeResult{ - Fee: makerFee, - InToken: baseToken, - InQuantity: quantityToTrade, - InTotal: quantityToTrade, - OutToken: quoteToken, - OutQuantity: quoteTokenQuantity, - OutTotal: makerOutTotal, - }, - } - } - return result, nil -} - -func SetteBalance(coinbase common.Address, takerOrder, makerOrder *tomox_state.OrderItem, settleBalance *SettleBalance, statedb *state.StateDB) error { +func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tomox_state.OrderItem, settleBalance *tomox_state.SettleBalance, statedb *state.StateDB) error { takerExOwner := tomox_state.GetRelayerOwner(takerOrder.ExchangeAddress, statedb) makerExOwner := tomox_state.GetRelayerOwner(makerOrder.ExchangeAddress, statedb) matchingFee := big.NewInt(0) diff --git a/tomox/tomox_state/orderitem.go b/tomox/tomox_state/orderitem.go index b39ec08e7..3e53850fd 100644 --- a/tomox/tomox_state/orderitem.go +++ b/tomox/tomox_state/orderitem.go @@ -7,6 +7,7 @@ import ( "github.com/globalsign/mgo/bson" "github.com/tomochain/tomochain/common" "github.com/tomochain/tomochain/core/state" + "github.com/tomochain/tomochain/core/types" "github.com/tomochain/tomochain/crypto" "github.com/tomochain/tomochain/crypto/sha3" "github.com/tomochain/tomochain/log" @@ -376,6 +377,24 @@ func VerifyPair(statedb *state.StateDB, exchangeAddress, baseToken, quoteToken c return fmt.Errorf("invalid exchange pair. Base: %s. Quote: %s. Exchange: %s", baseToken.Hex(), quoteToken.Hex(), exchangeAddress.Hex()) } +func VerifyBalance(statedb *state.StateDB, tomoxStateDb *TomoXStateDB, order *types.OrderTransaction, baseDecimal, quoteDecimal *big.Int) error { + var quotePrice *big.Int + if order.QuoteToken().String() != common.TomoNativeAddress { + quotePrice = tomoxStateDb.GetPrice(GetOrderBookHash(order.QuoteToken(), common.HexToAddress(common.TomoNativeAddress))) + } + feeRate := GetExRelayerFee(order.ExchangeAddress(), statedb) + balanceResult, err := GetSettleBalance(quotePrice, order.Side(), feeRate, order.BaseToken(), order.QuoteToken(), order.Price(), common.Big0, baseDecimal, quoteDecimal, order.Quantity()) + if err != nil { + return err + } + expectedBalance := balanceResult.Taker.OutTotal + actualBalance := GetTokenBalance(order.UserAddress(), balanceResult.Taker.OutToken, statedb) + if actualBalance.Cmp(expectedBalance) < 0 { + return fmt.Errorf("token: %s . ExpectedBalance: %s . ActualBalance: %s", balanceResult.Taker.OutToken.Hex(), expectedBalance.String(), actualBalance.String()) + } + return nil +} + // MarshalSignature marshals the signature struct to []byte func (s *Signature) MarshalSignature() ([]byte, error) { sigBytes1 := s.R.Bytes() diff --git a/tomox/tomox_state/settle_balance.go b/tomox/tomox_state/settle_balance.go new file mode 100644 index 000000000..ec714ac5c --- /dev/null +++ b/tomox/tomox_state/settle_balance.go @@ -0,0 +1,160 @@ +package tomox_state + +import ( + "encoding/json" + "errors" + "github.com/tomochain/tomochain/common" + "github.com/tomochain/tomochain/log" + "math/big" +) +var ErrQuantityTradeTooSmall = errors.New("quantity trade too small") + + +type TradeResult struct { + Fee *big.Int + InToken common.Address + InQuantity *big.Int + InTotal *big.Int + OutToken common.Address + OutQuantity *big.Int + OutTotal *big.Int +} +type SettleBalance struct { + Taker TradeResult + Maker TradeResult +} + +func (settleBalance *SettleBalance) String() string { + jsonData, _ := json.Marshal(settleBalance) + return string(jsonData) +} + +func GetSettleBalance(quotePrice *big.Int, takerSide string, takerFeeRate *big.Int, baseToken, quoteToken common.Address, makerPrice *big.Int, makerFeeRate *big.Int, baseTokenDecimal *big.Int, quoteTokenDecimal *big.Int, quantityToTrade *big.Int) (*SettleBalance, error) { + log.Debug("GetSettleBalance", "takerSide", takerSide, "takerFeeRate", takerFeeRate, "baseToken", baseToken, "quoteToken", quoteToken, "makerPrice", makerPrice, "makerFeeRate", makerFeeRate, "baseTokenDecimal", baseTokenDecimal, "quantityToTrade", quantityToTrade) + var result *SettleBalance + //result = map[common.Address]map[string]interface{}{} + if takerSide == Bid { + // maker InQuantity quoteTokenQuantity=(quantityToTrade*maker.Price/baseTokenDecimal) + quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) + quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal) + // Fee + // charge on the token he/she has before the trade, in this case: quoteToken + // charge on the token he/she has before the trade, in this case: baseToken + // takerFee = quoteTokenQuantity*takerFeeRate/baseFee=(quantityToTrade*maker.Price/baseTokenDecimal) * makerFeeRate/baseFee + takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate) + takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee) + // charge on the token he/she has before the trade, in this case: baseToken + makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate) + makerFee = new(big.Int).Div(makerFee, common.TomoXBaseFee) + if quoteTokenQuantity.Cmp(makerFee) <= 0 { + log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee) + return result, ErrQuantityTradeTooSmall + } + if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 { + exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice) + exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal) + log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) + if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 { + log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) + return result, ErrQuantityTradeTooSmall + } + exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice) + exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal) + log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) + if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 { + log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) + return result, ErrQuantityTradeTooSmall + } + } + inTotal := new(big.Int).Sub(quoteTokenQuantity, makerFee) + //takerOutTotal= quoteTokenQuantity + takerFee = quantityToTrade*maker.Price/baseTokenDecimal + quantityToTrade*maker.Price/baseTokenDecimal * takerFeeRate/baseFee + // = quantityToTrade * maker.Price/baseTokenDecimal ( 1 + takerFeeRate/baseFee) + // = quantityToTrade * maker.Price * (baseFee + takerFeeRate ) / ( baseTokenDecimal * baseFee) + takerOutTotal := new(big.Int).Add(quoteTokenQuantity, takerFee) + + result = &SettleBalance{ + Taker: TradeResult{ + Fee: takerFee, + InToken: baseToken, + InQuantity: quantityToTrade, + InTotal: quantityToTrade, + OutToken: quoteToken, + OutQuantity: quoteTokenQuantity, + OutTotal: takerOutTotal, + }, + Maker: TradeResult{ + Fee: makerFee, + InToken: quoteToken, + InQuantity: quoteTokenQuantity, + InTotal: inTotal, + OutToken: baseToken, + OutQuantity: quantityToTrade, + OutTotal: quantityToTrade, + }, + } + } else { + // Taker InQuantity + // quoteTokenQuantity = quantityToTrade * makerPrice / baseTokenDecimal + quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) + quoteTokenQuantity = quoteTokenQuantity.Div(quoteTokenQuantity, baseTokenDecimal) + // maker InQuantity + + // Fee + // charge on the token he/she has before the trade, in this case: baseToken + // makerFee = quoteTokenQuantity * makerFeeRate / baseFee = quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee + // charge on the token he/she has before the trade, in this case: quoteToken + makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate) + makerFee = makerFee.Div(makerFee, common.TomoXBaseFee) + + // charge on the token he/she has before the trade, in this case: baseToken + takerFee := new(big.Int).Mul(quoteTokenQuantity, takerFeeRate) + takerFee = new(big.Int).Div(takerFee, common.TomoXBaseFee) + if quoteTokenQuantity.Cmp(takerFee) <= 0 { + log.Debug("quantity trade too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee) + return result, ErrQuantityTradeTooSmall + } + if quotePrice != nil && quotePrice.Cmp(common.Big0) > 0 { + exMakerReceivedFee := new(big.Int).Mul(makerFee, quotePrice) + exMakerReceivedFee = exMakerReceivedFee.Div(exMakerReceivedFee, quoteTokenDecimal) + log.Debug("exMakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) + if exMakerReceivedFee.Cmp(common.RelayerFee) <= 0 { + log.Debug("makerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "makerFee", makerFee, "exMakerReceivedFee", exMakerReceivedFee, "quotePrice", quotePrice) + return result, ErrQuantityTradeTooSmall + } + exTakerReceivedFee := new(big.Int).Mul(takerFee, quotePrice) + exTakerReceivedFee = exTakerReceivedFee.Div(exTakerReceivedFee, quoteTokenDecimal) + log.Debug("exTakerReceivedFee", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) + if exTakerReceivedFee.Cmp(common.RelayerFee) <= 0 { + log.Debug("takerFee too small", "quoteTokenQuantity", quoteTokenQuantity, "takerFee", takerFee, "exTakerReceivedFee", exTakerReceivedFee, "quotePrice", quotePrice) + return result, ErrQuantityTradeTooSmall + } + } + inTotal := new(big.Int).Sub(quoteTokenQuantity, takerFee) + // makerOutTotal = quoteTokenQuantity + makerFee = quantityToTrade * makerPrice / baseTokenDecimal + quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee + // = quantityToTrade * makerPrice / baseTokenDecimal * (1+makerFeeRate / baseFee) + // = quantityToTrade * makerPrice * (baseFee + makerFeeRate) / ( baseTokenDecimal * baseFee ) + makerOutTotal := new(big.Int).Add(quoteTokenQuantity, makerFee) + // Fee + result = &SettleBalance{ + Taker: TradeResult{ + Fee: takerFee, + InToken: quoteToken, + InQuantity: quoteTokenQuantity, + InTotal: inTotal, + OutToken: baseToken, + OutQuantity: quantityToTrade, + OutTotal: quantityToTrade, + }, + Maker: TradeResult{ + Fee: makerFee, + InToken: baseToken, + InQuantity: quantityToTrade, + InTotal: quantityToTrade, + OutToken: quoteToken, + OutQuantity: quoteTokenQuantity, + OutTotal: makerOutTotal, + }, + } + } + return result, nil +}