github.com/XinFinOrg/XDPoSChain@v1.6.0/XDCx/order_processor.go (about) 1 package XDCx 2 3 import ( 4 "encoding/json" 5 "math/big" 6 "strconv" 7 "time" 8 9 "github.com/XinFinOrg/XDPoSChain/core/types" 10 11 "github.com/XinFinOrg/XDPoSChain/consensus" 12 13 "fmt" 14 15 "github.com/XinFinOrg/XDPoSChain/XDCx/tradingstate" 16 "github.com/XinFinOrg/XDPoSChain/common" 17 "github.com/XinFinOrg/XDPoSChain/core/state" 18 "github.com/XinFinOrg/XDPoSChain/log" 19 ) 20 21 func (XDCx *XDCX) CommitOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDB *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error) { 22 XDCxSnap := tradingStateDB.Snapshot() 23 dbSnap := statedb.Snapshot() 24 trades, rejects, err := XDCx.ApplyOrder(header, coinbase, chain, statedb, tradingStateDB, orderBook, order) 25 if err != nil { 26 tradingStateDB.RevertToSnapshot(XDCxSnap) 27 statedb.RevertToSnapshot(dbSnap) 28 return nil, nil, err 29 } 30 return trades, rejects, err 31 } 32 33 func (XDCx *XDCX) ApplyOrder(header *types.Header, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDB *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error) { 34 var ( 35 rejects []*tradingstate.OrderItem 36 trades []map[string]string 37 err error 38 ) 39 nonce := tradingStateDB.GetNonce(order.UserAddress.Hash()) 40 log.Debug("ApplyOrder", "addr", order.UserAddress, "statenonce", nonce, "ordernonce", order.Nonce) 41 if big.NewInt(int64(nonce)).Cmp(order.Nonce) == -1 { 42 log.Debug("ApplyOrder ErrNonceTooHigh", "nonce", order.Nonce) 43 return nil, nil, ErrNonceTooHigh 44 } else if big.NewInt(int64(nonce)).Cmp(order.Nonce) == 1 { 45 log.Debug("ApplyOrder ErrNonceTooLow", "nonce", order.Nonce) 46 return nil, nil, ErrNonceTooLow 47 } 48 // increase nonce 49 log.Debug("ApplyOrder set nonce", "nonce", nonce+1, "addr", order.UserAddress.Hex(), "status", order.Status, "oldnonce", nonce) 50 tradingStateDB.SetNonce(order.UserAddress.Hash(), nonce+1) 51 XDCxSnap := tradingStateDB.Snapshot() 52 dbSnap := statedb.Snapshot() 53 defer func() { 54 if err != nil { 55 tradingStateDB.RevertToSnapshot(XDCxSnap) 56 statedb.RevertToSnapshot(dbSnap) 57 } 58 }() 59 60 if err := order.VerifyOrder(statedb); err != nil { 61 rejects = append(rejects, order) 62 return trades, rejects, nil 63 } 64 if order.Status == tradingstate.OrderStatusCancelled { 65 err, reject := XDCx.ProcessCancelOrder(header, tradingStateDB, statedb, chain, coinbase, orderBook, order) 66 if err != nil || reject { 67 log.Debug("Reject cancelled order", "err", err) 68 rejects = append(rejects, order) 69 } 70 return trades, rejects, nil 71 } 72 if order.Type != tradingstate.Market { 73 if order.Price.Sign() == 0 || common.BigToHash(order.Price).Big().Cmp(order.Price) != 0 { 74 log.Debug("Reject order price invalid", "price", order.Price) 75 rejects = append(rejects, order) 76 return trades, rejects, nil 77 } 78 } 79 if order.Quantity.Sign() == 0 || common.BigToHash(order.Quantity).Big().Cmp(order.Quantity) != 0 { 80 log.Debug("Reject order quantity invalid", "quantity", order.Quantity) 81 rejects = append(rejects, order) 82 return trades, rejects, nil 83 } 84 orderType := order.Type 85 // if we do not use auto-increment orderid, we must set price slot to avoid conflict 86 if orderType == tradingstate.Market { 87 log.Debug("Process maket order", "side", order.Side, "quantity", order.Quantity, "price", order.Price) 88 trades, rejects, err = XDCx.processMarketOrder(coinbase, chain, statedb, tradingStateDB, orderBook, order) 89 if err != nil { 90 log.Debug("Reject market order", "err", err, "order", tradingstate.ToJSON(order)) 91 trades = []map[string]string{} 92 rejects = append(rejects, order) 93 } 94 } else { 95 log.Debug("Process limit order", "side", order.Side, "quantity", order.Quantity, "price", order.Price) 96 trades, rejects, err = XDCx.processLimitOrder(coinbase, chain, statedb, tradingStateDB, orderBook, order) 97 if err != nil { 98 log.Debug("Reject limit order", "err", err, "order", tradingstate.ToJSON(order)) 99 trades = []map[string]string{} 100 rejects = append(rejects, order) 101 } 102 } 103 104 return trades, rejects, nil 105 } 106 107 // processMarketOrder : process the market order 108 func (XDCx *XDCX) processMarketOrder(coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDB *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error) { 109 var ( 110 trades []map[string]string 111 newTrades []map[string]string 112 rejects []*tradingstate.OrderItem 113 newRejects []*tradingstate.OrderItem 114 err error 115 ) 116 quantityToTrade := order.Quantity 117 side := order.Side 118 // speedup the comparison, do not assign because it is pointer 119 zero := tradingstate.Zero 120 if side == tradingstate.Bid { 121 bestPrice, volume := tradingStateDB.GetBestAskPrice(orderBook) 122 log.Debug("processMarketOrder ", "side", side, "bestPrice", bestPrice, "quantityToTrade", quantityToTrade, "volume", volume) 123 for quantityToTrade.Cmp(zero) > 0 && bestPrice.Cmp(zero) > 0 { 124 quantityToTrade, newTrades, newRejects, err = XDCx.processOrderList(coinbase, chain, statedb, tradingStateDB, tradingstate.Ask, orderBook, bestPrice, quantityToTrade, order) 125 if err != nil { 126 return nil, nil, err 127 } 128 trades = append(trades, newTrades...) 129 rejects = append(rejects, newRejects...) 130 bestPrice, volume = tradingStateDB.GetBestAskPrice(orderBook) 131 log.Debug("processMarketOrder ", "side", side, "bestPrice", bestPrice, "quantityToTrade", quantityToTrade, "volume", volume) 132 } 133 } else { 134 bestPrice, volume := tradingStateDB.GetBestBidPrice(orderBook) 135 log.Debug("processMarketOrder ", "side", side, "bestPrice", bestPrice, "quantityToTrade", quantityToTrade, "volume", volume) 136 for quantityToTrade.Cmp(zero) > 0 && bestPrice.Cmp(zero) > 0 { 137 quantityToTrade, newTrades, newRejects, err = XDCx.processOrderList(coinbase, chain, statedb, tradingStateDB, tradingstate.Bid, orderBook, bestPrice, quantityToTrade, order) 138 if err != nil { 139 return nil, nil, err 140 } 141 trades = append(trades, newTrades...) 142 rejects = append(rejects, newRejects...) 143 bestPrice, volume = tradingStateDB.GetBestBidPrice(orderBook) 144 log.Debug("processMarketOrder ", "side", side, "bestPrice", bestPrice, "quantityToTrade", quantityToTrade, "volume", volume) 145 } 146 } 147 return trades, rejects, nil 148 } 149 150 // processLimitOrder : process the limit order, can change the quote 151 // If not care for performance, we should make a copy of quote to prevent further reference problem 152 func (XDCx *XDCX) processLimitOrder(coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDB *tradingstate.TradingStateDB, orderBook common.Hash, order *tradingstate.OrderItem) ([]map[string]string, []*tradingstate.OrderItem, error) { 153 var ( 154 trades []map[string]string 155 newTrades []map[string]string 156 rejects []*tradingstate.OrderItem 157 newRejects []*tradingstate.OrderItem 158 err error 159 ) 160 quantityToTrade := order.Quantity 161 side := order.Side 162 price := order.Price 163 164 // speedup the comparison, do not assign because it is pointer 165 zero := tradingstate.Zero 166 167 if side == tradingstate.Bid { 168 minPrice, volume := tradingStateDB.GetBestAskPrice(orderBook) 169 log.Debug("processLimitOrder ", "side", side, "minPrice", minPrice, "orderPrice", price, "volume", volume) 170 for quantityToTrade.Cmp(zero) > 0 && price.Cmp(minPrice) >= 0 && minPrice.Cmp(zero) > 0 { 171 log.Debug("Min price in asks tree", "price", minPrice.String()) 172 quantityToTrade, newTrades, newRejects, err = XDCx.processOrderList(coinbase, chain, statedb, tradingStateDB, tradingstate.Ask, orderBook, minPrice, quantityToTrade, order) 173 if err != nil { 174 return nil, nil, err 175 } 176 trades = append(trades, newTrades...) 177 rejects = append(rejects, newRejects...) 178 log.Debug("New trade found", "newTrades", newTrades, "quantityToTrade", quantityToTrade) 179 minPrice, volume = tradingStateDB.GetBestAskPrice(orderBook) 180 log.Debug("processLimitOrder ", "side", side, "minPrice", minPrice, "orderPrice", price, "volume", volume) 181 } 182 } else { 183 maxPrice, volume := tradingStateDB.GetBestBidPrice(orderBook) 184 log.Debug("processLimitOrder ", "side", side, "maxPrice", maxPrice, "orderPrice", price, "volume", volume) 185 for quantityToTrade.Cmp(zero) > 0 && price.Cmp(maxPrice) <= 0 && maxPrice.Cmp(zero) > 0 { 186 log.Debug("Max price in bids tree", "price", maxPrice.String()) 187 quantityToTrade, newTrades, newRejects, err = XDCx.processOrderList(coinbase, chain, statedb, tradingStateDB, tradingstate.Bid, orderBook, maxPrice, quantityToTrade, order) 188 if err != nil { 189 return nil, nil, err 190 } 191 trades = append(trades, newTrades...) 192 rejects = append(rejects, newRejects...) 193 log.Debug("New trade found", "newTrades", newTrades, "quantityToTrade", quantityToTrade) 194 maxPrice, volume = tradingStateDB.GetBestBidPrice(orderBook) 195 log.Debug("processLimitOrder ", "side", side, "maxPrice", maxPrice, "orderPrice", price, "volume", volume) 196 } 197 } 198 if quantityToTrade.Cmp(zero) > 0 { 199 orderId := tradingStateDB.GetNonce(orderBook) 200 order.OrderID = orderId + 1 201 order.Quantity = quantityToTrade 202 tradingStateDB.SetNonce(orderBook, orderId+1) 203 orderIdHash := common.BigToHash(new(big.Int).SetUint64(order.OrderID)) 204 tradingStateDB.InsertOrderItem(orderBook, orderIdHash, *order) 205 log.Debug("After matching, order (unmatched part) is now added to tree", "side", order.Side, "order", order) 206 } 207 return trades, rejects, nil 208 } 209 210 // processOrderList : process the order list 211 func (XDCx *XDCX) processOrderList(coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, tradingStateDB *tradingstate.TradingStateDB, side string, orderBook common.Hash, price *big.Int, quantityStillToTrade *big.Int, order *tradingstate.OrderItem) (*big.Int, []map[string]string, []*tradingstate.OrderItem, error) { 212 quantityToTrade := tradingstate.CloneBigInt(quantityStillToTrade) 213 log.Debug("Process matching between order and orderlist", "quantityToTrade", quantityToTrade) 214 var ( 215 trades []map[string]string 216 217 rejects []*tradingstate.OrderItem 218 ) 219 for quantityToTrade.Sign() > 0 { 220 orderId, amount, _ := tradingStateDB.GetBestOrderIdAndAmount(orderBook, price, side) 221 var oldestOrder tradingstate.OrderItem 222 if amount.Sign() > 0 { 223 oldestOrder = tradingStateDB.GetOrder(orderBook, orderId) 224 } 225 log.Debug("found order ", "orderId ", orderId, "side", oldestOrder.Side, "amount", amount) 226 if oldestOrder.Quantity == nil || oldestOrder.Quantity.Sign() == 0 && amount.Sign() == 0 { 227 break 228 } 229 var ( 230 tradedQuantity *big.Int 231 maxTradedQuantity *big.Int 232 ) 233 if quantityToTrade.Cmp(amount) <= 0 { 234 maxTradedQuantity = tradingstate.CloneBigInt(quantityToTrade) 235 } else { 236 maxTradedQuantity = tradingstate.CloneBigInt(amount) 237 } 238 var quotePrice *big.Int 239 if oldestOrder.QuoteToken.String() != common.XDCNativeAddress { 240 quotePrice = tradingStateDB.GetLastPrice(tradingstate.GetTradingOrderBookHash(oldestOrder.QuoteToken, common.HexToAddress(common.XDCNativeAddress))) 241 log.Debug("TryGet quotePrice QuoteToken/XDC", "quotePrice", quotePrice) 242 if quotePrice == nil || quotePrice.Sign() == 0 { 243 inversePrice := tradingStateDB.GetLastPrice(tradingstate.GetTradingOrderBookHash(common.HexToAddress(common.XDCNativeAddress), oldestOrder.QuoteToken)) 244 quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, oldestOrder.QuoteToken) 245 if err != nil || quoteTokenDecimal.Sign() == 0 { 246 return nil, nil, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", oldestOrder.QuoteToken.String(), err) 247 } 248 log.Debug("TryGet inversePrice XDC/QuoteToken", "inversePrice", inversePrice) 249 if inversePrice != nil && inversePrice.Sign() > 0 { 250 quotePrice = new(big.Int).Mul(common.BasePrice, quoteTokenDecimal) 251 quotePrice = new(big.Int).Div(quotePrice, inversePrice) 252 log.Debug("TryGet quotePrice after get inversePrice XDC/QuoteToken", "quotePrice", quotePrice, "quoteTokenDecimal", quoteTokenDecimal) 253 } 254 } 255 } else { 256 quotePrice = common.BasePrice 257 } 258 tradedQuantity, rejectMaker, settleBalanceResult, err := XDCx.getTradeQuantity(quotePrice, coinbase, chain, statedb, order, &oldestOrder, maxTradedQuantity) 259 if err != nil && err == tradingstate.ErrQuantityTradeTooSmall { 260 if tradedQuantity.Cmp(maxTradedQuantity) == 0 { 261 if quantityToTrade.Cmp(amount) == 0 { // reject Taker & maker 262 rejects = append(rejects, order) 263 quantityToTrade = tradingstate.Zero 264 rejects = append(rejects, &oldestOrder) 265 err = tradingStateDB.CancelOrder(orderBook, &oldestOrder) 266 if err != nil { 267 return nil, nil, nil, err 268 } 269 break 270 } else if quantityToTrade.Cmp(amount) < 0 { // reject Taker 271 rejects = append(rejects, order) 272 quantityToTrade = tradingstate.Zero 273 break 274 } else { // reject maker 275 rejects = append(rejects, &oldestOrder) 276 err = tradingStateDB.CancelOrder(orderBook, &oldestOrder) 277 if err != nil { 278 return nil, nil, nil, err 279 } 280 continue 281 } 282 } else { 283 if rejectMaker { // reject maker 284 rejects = append(rejects, &oldestOrder) 285 err = tradingStateDB.CancelOrder(orderBook, &oldestOrder) 286 if err != nil { 287 return nil, nil, nil, err 288 } 289 continue 290 } else { // reject Taker 291 rejects = append(rejects, order) 292 quantityToTrade = tradingstate.Zero 293 break 294 } 295 } 296 } else if err != nil { 297 return nil, nil, nil, err 298 } 299 if tradedQuantity.Sign() == 0 && !rejectMaker { 300 log.Debug("Reject order Taker ", "tradedQuantity", tradedQuantity, "rejectMaker", rejectMaker) 301 rejects = append(rejects, order) 302 quantityToTrade = tradingstate.Zero 303 break 304 } 305 if tradedQuantity.Sign() > 0 { 306 quantityToTrade = tradingstate.Sub(quantityToTrade, tradedQuantity) 307 err := tradingStateDB.SubAmountOrderItem(orderBook, orderId, price, tradedQuantity, side) 308 if err != nil { 309 log.Warn("processOrderList SubAmountOrderItem", "err", err, "orderBook", orderBook, "orderId", orderId, "price", *price, "tradedQuantity", *tradedQuantity, "side", side) 310 } 311 tradingStateDB.SetLastPrice(orderBook, price) 312 log.Debug("Update quantity for orderId", "orderId", orderId.Hex()) 313 log.Debug("TRADE", "orderBook", orderBook, "Taker price", price, "maker price", order.Price, "Amount", tradedQuantity, "orderId", orderId, "side", side) 314 315 tradeRecord := make(map[string]string) 316 tradeRecord[tradingstate.TradeTakerOrderHash] = order.Hash.Hex() 317 tradeRecord[tradingstate.TradeMakerOrderHash] = oldestOrder.Hash.Hex() 318 tradeRecord[tradingstate.TradeTimestamp] = strconv.FormatInt(time.Now().Unix(), 10) 319 tradeRecord[tradingstate.TradeQuantity] = tradedQuantity.String() 320 tradeRecord[tradingstate.TradeMakerExchange] = oldestOrder.ExchangeAddress.String() 321 tradeRecord[tradingstate.TradeMaker] = oldestOrder.UserAddress.String() 322 tradeRecord[tradingstate.TradeBaseToken] = oldestOrder.BaseToken.String() 323 tradeRecord[tradingstate.TradeQuoteToken] = oldestOrder.QuoteToken.String() 324 if settleBalanceResult != nil { 325 tradeRecord[tradingstate.MakerFee] = settleBalanceResult.Maker.Fee.Text(10) 326 tradeRecord[tradingstate.TakerFee] = settleBalanceResult.Taker.Fee.Text(10) 327 } 328 // maker price is actual price 329 // Taker price is offer price 330 // tradedPrice is always actual price 331 tradeRecord[tradingstate.TradePrice] = oldestOrder.Price.String() 332 tradeRecord[tradingstate.MakerOrderType] = oldestOrder.Type 333 trades = append(trades, tradeRecord) 334 335 oldAveragePrice, oldTotalQuantity := tradingStateDB.GetMediumPriceAndTotalAmount(orderBook) 336 337 var newAveragePrice, newTotalQuantity *big.Int 338 if oldAveragePrice == nil || oldAveragePrice.Sign() <= 0 || oldTotalQuantity == nil || oldTotalQuantity.Sign() <= 0 { 339 newAveragePrice = price 340 newTotalQuantity = tradedQuantity 341 } else { 342 //volume = price * quantity 343 //=> price = volume /quantity 344 // averagePrice = totalVolume / totalQuantity 345 // averagePrice = (oldVolume + newTradeVolume) / (oldQuantity + newTradeQuantity) 346 // FIXME: average price formula 347 // https://user-images.githubusercontent.com/17243442/72722447-ecb83700-3bb0-11ea-9273-1c1028dbade0.jpg 348 349 oldVolume := new(big.Int).Mul(oldAveragePrice, oldTotalQuantity) 350 newTradeVolume := new(big.Int).Mul(price, tradedQuantity) 351 newTotalQuantity = new(big.Int).Add(oldTotalQuantity, tradedQuantity) 352 newAveragePrice = new(big.Int).Div(new(big.Int).Add(oldVolume, newTradeVolume), newTotalQuantity) 353 } 354 355 tradingStateDB.SetMediumPrice(orderBook, newAveragePrice, newTotalQuantity) 356 } 357 if rejectMaker { 358 rejects = append(rejects, &oldestOrder) 359 err := tradingStateDB.CancelOrder(orderBook, &oldestOrder) 360 if err != nil { 361 return nil, nil, nil, err 362 } 363 } 364 } 365 return quantityToTrade, trades, rejects, nil 366 } 367 368 func (XDCx *XDCX) getTradeQuantity(quotePrice *big.Int, coinbase common.Address, chain consensus.ChainContext, statedb *state.StateDB, takerOrder *tradingstate.OrderItem, makerOrder *tradingstate.OrderItem, quantityToTrade *big.Int) (*big.Int, bool, *tradingstate.SettleBalance, error) { 369 baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.BaseToken) 370 if err != nil || baseTokenDecimal.Sign() == 0 { 371 return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.BaseToken.String(), err) 372 } 373 quoteTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, makerOrder.QuoteToken) 374 if err != nil || quoteTokenDecimal.Sign() == 0 { 375 return tradingstate.Zero, false, nil, fmt.Errorf("Fail to get tokenDecimal. Token: %v . Err: %v", makerOrder.QuoteToken.String(), err) 376 } 377 if makerOrder.QuoteToken.String() == common.XDCNativeAddress { 378 quotePrice = quoteTokenDecimal 379 } 380 if takerOrder.ExchangeAddress.String() == makerOrder.ExchangeAddress.String() { 381 if err := tradingstate.CheckRelayerFee(takerOrder.ExchangeAddress, new(big.Int).Mul(common.RelayerFee, big.NewInt(2)), statedb); err != nil { 382 log.Debug("Reject order Taker Exchnage = Maker Exchange , relayer not enough fee ", "err", err) 383 return tradingstate.Zero, false, nil, nil 384 } 385 } else { 386 if err := tradingstate.CheckRelayerFee(takerOrder.ExchangeAddress, common.RelayerFee, statedb); err != nil { 387 log.Debug("Reject order Taker , relayer not enough fee ", "err", err) 388 return tradingstate.Zero, false, nil, nil 389 } 390 if err := tradingstate.CheckRelayerFee(makerOrder.ExchangeAddress, common.RelayerFee, statedb); err != nil { 391 log.Debug("Reject order maker , relayer not enough fee ", "err", err) 392 return tradingstate.Zero, true, nil, nil 393 } 394 } 395 takerFeeRate := tradingstate.GetExRelayerFee(takerOrder.ExchangeAddress, statedb) 396 makerFeeRate := tradingstate.GetExRelayerFee(makerOrder.ExchangeAddress, statedb) 397 var takerBalance, makerBalance *big.Int 398 switch takerOrder.Side { 399 case tradingstate.Bid: 400 takerBalance = tradingstate.GetTokenBalance(takerOrder.UserAddress, makerOrder.QuoteToken, statedb) 401 makerBalance = tradingstate.GetTokenBalance(makerOrder.UserAddress, makerOrder.BaseToken, statedb) 402 case tradingstate.Ask: 403 takerBalance = tradingstate.GetTokenBalance(takerOrder.UserAddress, makerOrder.BaseToken, statedb) 404 makerBalance = tradingstate.GetTokenBalance(makerOrder.UserAddress, makerOrder.QuoteToken, statedb) 405 default: 406 takerBalance = big.NewInt(0) 407 makerBalance = big.NewInt(0) 408 } 409 quantity, rejectMaker := GetTradeQuantity(takerOrder.Side, takerFeeRate, takerBalance, makerOrder.Price, makerFeeRate, makerBalance, baseTokenDecimal, quantityToTrade) 410 log.Debug("GetTradeQuantity", "side", takerOrder.Side, "takerBalance", takerBalance, "makerBalance", makerBalance, "BaseToken", makerOrder.BaseToken, "QuoteToken", makerOrder.QuoteToken, "quantity", quantity, "rejectMaker", rejectMaker, "quotePrice", quotePrice) 411 var settleBalanceResult *tradingstate.SettleBalance 412 if quantity.Sign() > 0 { 413 // Apply Match Order 414 settleBalanceResult, err = tradingstate.GetSettleBalance(quotePrice, takerOrder.Side, takerFeeRate, makerOrder.BaseToken, makerOrder.QuoteToken, makerOrder.Price, makerFeeRate, baseTokenDecimal, quoteTokenDecimal, quantity) 415 log.Debug("GetSettleBalance", "settleBalanceResult", settleBalanceResult, "err", err) 416 if err == nil { 417 err = DoSettleBalance(coinbase, takerOrder, makerOrder, settleBalanceResult, statedb) 418 } 419 return quantity, rejectMaker, settleBalanceResult, err 420 } 421 return quantity, rejectMaker, settleBalanceResult, nil 422 } 423 424 func GetTradeQuantity(takerSide string, takerFeeRate *big.Int, takerBalance *big.Int, makerPrice *big.Int, makerFeeRate *big.Int, makerBalance *big.Int, baseTokenDecimal *big.Int, quantityToTrade *big.Int) (*big.Int, bool) { 425 if takerSide == tradingstate.Bid { 426 // maker InQuantity quoteTokenQuantity=(quantityToTrade*maker.Price/baseTokenDecimal) 427 quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) 428 quoteTokenQuantity = new(big.Int).Div(quoteTokenQuantity, baseTokenDecimal) 429 // Fee 430 // charge on the token he/she has before the trade, in this case: quoteToken 431 // charge on the token he/she has before the trade, in this case: baseToken 432 // takerFee = quoteTokenQuantity*takerFeeRate/baseFee=(quantityToTrade*maker.Price/baseTokenDecimal) * makerFeeRate/baseFee 433 takerFee := big.NewInt(0).Mul(quoteTokenQuantity, takerFeeRate) 434 takerFee = big.NewInt(0).Div(takerFee, common.XDCXBaseFee) 435 //takerOutTotal= quoteTokenQuantity + takerFee = quantityToTrade*maker.Price/baseTokenDecimal + quantityToTrade*maker.Price/baseTokenDecimal * takerFeeRate/baseFee 436 // = quantityToTrade * maker.Price/baseTokenDecimal ( 1 + takerFeeRate/baseFee) 437 // = quantityToTrade * maker.Price * (baseFee + takerFeeRate ) / ( baseTokenDecimal * baseFee) 438 takerOutTotal := new(big.Int).Add(quoteTokenQuantity, takerFee) 439 makerOutTotal := quantityToTrade 440 if takerBalance.Cmp(takerOutTotal) >= 0 && makerBalance.Cmp(makerOutTotal) >= 0 { 441 return quantityToTrade, false 442 } else if takerBalance.Cmp(takerOutTotal) < 0 && makerBalance.Cmp(makerOutTotal) >= 0 { 443 newQuantityTrade := new(big.Int).Mul(takerBalance, baseTokenDecimal) 444 newQuantityTrade = new(big.Int).Mul(newQuantityTrade, common.XDCXBaseFee) 445 newQuantityTrade = new(big.Int).Div(newQuantityTrade, new(big.Int).Add(common.XDCXBaseFee, takerFeeRate)) 446 newQuantityTrade = new(big.Int).Div(newQuantityTrade, makerPrice) 447 if newQuantityTrade.Sign() == 0 { 448 log.Debug("Reject order Taker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "takerOutTotal", takerOutTotal) 449 } 450 return newQuantityTrade, false 451 } else if takerBalance.Cmp(takerOutTotal) >= 0 && makerBalance.Cmp(makerOutTotal) < 0 { 452 log.Debug("Reject order maker , not enough balance ", "makerBalance", makerBalance, " makerOutTotal", makerOutTotal) 453 return makerBalance, true 454 } else { 455 // takerBalance.Cmp(takerOutTotal) < 0 && makerBalance.Cmp(makerOutTotal) < 0 456 newQuantityTrade := new(big.Int).Mul(takerBalance, baseTokenDecimal) 457 newQuantityTrade = new(big.Int).Mul(newQuantityTrade, common.XDCXBaseFee) 458 newQuantityTrade = new(big.Int).Div(newQuantityTrade, new(big.Int).Add(common.XDCXBaseFee, takerFeeRate)) 459 newQuantityTrade = new(big.Int).Div(newQuantityTrade, makerPrice) 460 if newQuantityTrade.Cmp(makerBalance) <= 0 { 461 if newQuantityTrade.Sign() == 0 { 462 log.Debug("Reject order Taker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "makerBalance", makerBalance, " newQuantityTrade ", newQuantityTrade) 463 } 464 return newQuantityTrade, false 465 } 466 log.Debug("Reject order maker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "makerBalance", makerBalance, " newQuantityTrade ", newQuantityTrade) 467 return makerBalance, true 468 } 469 } else { 470 // Taker InQuantity 471 // quoteTokenQuantity = quantityToTrade * makerPrice / baseTokenDecimal 472 quoteTokenQuantity := new(big.Int).Mul(quantityToTrade, makerPrice) 473 quoteTokenQuantity = new(big.Int).Div(quoteTokenQuantity, baseTokenDecimal) 474 // maker InQuantity 475 476 // Fee 477 // charge on the token he/she has before the trade, in this case: baseToken 478 // makerFee = quoteTokenQuantity * makerFeeRate / baseFee = quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee 479 // charge on the token he/she has before the trade, in this case: quoteToken 480 makerFee := new(big.Int).Mul(quoteTokenQuantity, makerFeeRate) 481 makerFee = new(big.Int).Div(makerFee, common.XDCXBaseFee) 482 483 takerOutTotal := quantityToTrade 484 // makerOutTotal = quoteTokenQuantity + makerFee = quantityToTrade * makerPrice / baseTokenDecimal + quantityToTrade * makerPrice / baseTokenDecimal * makerFeeRate / baseFee 485 // = quantityToTrade * makerPrice / baseTokenDecimal * (1+makerFeeRate / baseFee) 486 // = quantityToTrade * makerPrice * (baseFee + makerFeeRate) / ( baseTokenDecimal * baseFee ) 487 makerOutTotal := new(big.Int).Add(quoteTokenQuantity, makerFee) 488 if takerBalance.Cmp(takerOutTotal) >= 0 && makerBalance.Cmp(makerOutTotal) >= 0 { 489 return quantityToTrade, false 490 } else if takerBalance.Cmp(takerOutTotal) < 0 && makerBalance.Cmp(makerOutTotal) >= 0 { 491 if takerBalance.Sign() == 0 { 492 log.Debug("Reject order Taker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "takerOutTotal", takerOutTotal) 493 } 494 return takerBalance, false 495 } else if takerBalance.Cmp(takerOutTotal) >= 0 && makerBalance.Cmp(makerOutTotal) < 0 { 496 newQuantityTrade := new(big.Int).Mul(makerBalance, baseTokenDecimal) 497 newQuantityTrade = new(big.Int).Mul(newQuantityTrade, common.XDCXBaseFee) 498 newQuantityTrade = new(big.Int).Div(newQuantityTrade, new(big.Int).Add(common.XDCXBaseFee, makerFeeRate)) 499 newQuantityTrade = new(big.Int).Div(newQuantityTrade, makerPrice) 500 log.Debug("Reject order maker , not enough balance ", "makerBalance", makerBalance, " makerOutTotal", makerOutTotal) 501 return newQuantityTrade, true 502 } else { 503 // takerBalance.Cmp(takerOutTotal) < 0 && makerBalance.Cmp(makerOutTotal) < 0 504 newQuantityTrade := new(big.Int).Mul(makerBalance, baseTokenDecimal) 505 newQuantityTrade = new(big.Int).Mul(newQuantityTrade, common.XDCXBaseFee) 506 newQuantityTrade = new(big.Int).Div(newQuantityTrade, new(big.Int).Add(common.XDCXBaseFee, makerFeeRate)) 507 newQuantityTrade = new(big.Int).Div(newQuantityTrade, makerPrice) 508 if newQuantityTrade.Cmp(takerBalance) <= 0 { 509 log.Debug("Reject order maker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "makerBalance", makerBalance, " newQuantityTrade ", newQuantityTrade) 510 return newQuantityTrade, true 511 } 512 if takerBalance.Sign() == 0 { 513 log.Debug("Reject order Taker , not enough balance ", "takerSide", takerSide, "takerBalance", takerBalance, "makerBalance", makerBalance, " newQuantityTrade ", newQuantityTrade) 514 } 515 return takerBalance, false 516 } 517 } 518 } 519 520 func DoSettleBalance(coinbase common.Address, takerOrder, makerOrder *tradingstate.OrderItem, settleBalance *tradingstate.SettleBalance, statedb *state.StateDB) error { 521 takerExOwner := tradingstate.GetRelayerOwner(takerOrder.ExchangeAddress, statedb) 522 makerExOwner := tradingstate.GetRelayerOwner(makerOrder.ExchangeAddress, statedb) 523 matchingFee := big.NewInt(0) 524 // masternodes charges fee of both 2 relayers. If maker and Taker are on same relayer, that relayer is charged fee twice 525 matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee) 526 matchingFee = new(big.Int).Add(matchingFee, common.RelayerFee) 527 528 if common.EmptyHash(takerExOwner.Hash()) || common.EmptyHash(makerExOwner.Hash()) { 529 return fmt.Errorf("Echange owner empty , Taker: %v , maker : %v ", takerExOwner, makerExOwner) 530 } 531 mapBalances := map[common.Address]map[common.Address]*big.Int{} 532 //Checking balance 533 newTakerInTotal, err := tradingstate.CheckAddTokenBalance(takerOrder.UserAddress, settleBalance.Taker.InTotal, settleBalance.Taker.InToken, statedb, mapBalances) 534 if err != nil { 535 return err 536 } 537 if mapBalances[settleBalance.Taker.InToken] == nil { 538 mapBalances[settleBalance.Taker.InToken] = map[common.Address]*big.Int{} 539 } 540 mapBalances[settleBalance.Taker.InToken][takerOrder.UserAddress] = newTakerInTotal 541 newTakerOutTotal, err := tradingstate.CheckSubTokenBalance(takerOrder.UserAddress, settleBalance.Taker.OutTotal, settleBalance.Taker.OutToken, statedb, mapBalances) 542 if err != nil { 543 return err 544 } 545 if mapBalances[settleBalance.Taker.OutToken] == nil { 546 mapBalances[settleBalance.Taker.OutToken] = map[common.Address]*big.Int{} 547 } 548 mapBalances[settleBalance.Taker.OutToken][takerOrder.UserAddress] = newTakerOutTotal 549 newMakerInTotal, err := tradingstate.CheckAddTokenBalance(makerOrder.UserAddress, settleBalance.Maker.InTotal, settleBalance.Maker.InToken, statedb, mapBalances) 550 if err != nil { 551 return err 552 } 553 if mapBalances[settleBalance.Maker.InToken] == nil { 554 mapBalances[settleBalance.Maker.InToken] = map[common.Address]*big.Int{} 555 } 556 mapBalances[settleBalance.Maker.InToken][makerOrder.UserAddress] = newMakerInTotal 557 newMakerOutTotal, err := tradingstate.CheckSubTokenBalance(makerOrder.UserAddress, settleBalance.Maker.OutTotal, settleBalance.Maker.OutToken, statedb, mapBalances) 558 if err != nil { 559 return err 560 } 561 if mapBalances[settleBalance.Maker.OutToken] == nil { 562 mapBalances[settleBalance.Maker.OutToken] = map[common.Address]*big.Int{} 563 } 564 mapBalances[settleBalance.Maker.OutToken][makerOrder.UserAddress] = newMakerOutTotal 565 newTakerFee, err := tradingstate.CheckAddTokenBalance(takerExOwner, settleBalance.Taker.Fee, makerOrder.QuoteToken, statedb, mapBalances) 566 if err != nil { 567 return err 568 } 569 if mapBalances[makerOrder.QuoteToken] == nil { 570 mapBalances[makerOrder.QuoteToken] = map[common.Address]*big.Int{} 571 } 572 mapBalances[makerOrder.QuoteToken][takerExOwner] = newTakerFee 573 newMakerFee, err := tradingstate.CheckAddTokenBalance(makerExOwner, settleBalance.Maker.Fee, makerOrder.QuoteToken, statedb, mapBalances) 574 if err != nil { 575 return err 576 } 577 mapBalances[makerOrder.QuoteToken][makerExOwner] = newMakerFee 578 579 mapRelayerFee := map[common.Address]*big.Int{} 580 newRelayerTakerFee, err := tradingstate.CheckSubRelayerFee(takerOrder.ExchangeAddress, common.RelayerFee, statedb, mapRelayerFee) 581 if err != nil { 582 return err 583 } 584 mapRelayerFee[takerOrder.ExchangeAddress] = newRelayerTakerFee 585 newRelayerMakerFee, err := tradingstate.CheckSubRelayerFee(makerOrder.ExchangeAddress, common.RelayerFee, statedb, mapRelayerFee) 586 if err != nil { 587 return err 588 } 589 mapRelayerFee[makerOrder.ExchangeAddress] = newRelayerMakerFee 590 tradingstate.SetSubRelayerFee(takerOrder.ExchangeAddress, newRelayerTakerFee, common.RelayerFee, statedb) 591 tradingstate.SetSubRelayerFee(makerOrder.ExchangeAddress, newRelayerMakerFee, common.RelayerFee, statedb) 592 593 masternodeOwner := statedb.GetOwner(coinbase) 594 statedb.AddBalance(masternodeOwner, matchingFee) 595 596 err = tradingstate.SetTokenBalance(takerOrder.UserAddress, newTakerInTotal, settleBalance.Taker.InToken, statedb) 597 if err != nil { 598 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "takerOder.UserAddress", takerOrder.UserAddress, "newTakerInTotal", *newTakerInTotal, "settleBalance.Taker.InToken", settleBalance.Taker.InToken) 599 } 600 err = tradingstate.SetTokenBalance(takerOrder.UserAddress, newTakerOutTotal, settleBalance.Taker.OutToken, statedb) 601 if err != nil { 602 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "takerOrder.UserAddress", takerOrder.UserAddress, "newTakerOutTotal", *newTakerOutTotal, "settleBalance.Taker.OutToken", settleBalance.Taker.OutToken) 603 } 604 err = tradingstate.SetTokenBalance(makerOrder.UserAddress, newMakerInTotal, settleBalance.Maker.InToken, statedb) 605 if err != nil { 606 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "makerOrder.UserAddress", makerOrder.UserAddress, "newMakerInTotal", *newMakerInTotal, "settleBalance.Maker.InToken", settleBalance.Maker.InToken) 607 } 608 err = tradingstate.SetTokenBalance(makerOrder.UserAddress, newMakerOutTotal, settleBalance.Maker.OutToken, statedb) 609 if err != nil { 610 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "makerOrder.UserAddress", makerOrder.UserAddress, "newMakerOutTotal", *newMakerOutTotal, "settleBalance.Maker.OutToken", settleBalance.Maker.OutToken) 611 } 612 613 // add balance for relayers 614 //log.Debug("ApplyXDCXMatchedTransaction settle fee for relayers", 615 // "takerRelayerOwner", takerExOwner, 616 // "takerFeeToken", quoteToken, "takerFee", settleBalanceResult[takerAddr][XDCx.Fee].(*big.Int), 617 // "makerRelayerOwner", makerExOwner, 618 // "makerFeeToken", quoteToken, "makerFee", settleBalanceResult[makerAddr][XDCx.Fee].(*big.Int)) 619 // takerFee 620 err = tradingstate.SetTokenBalance(takerExOwner, newTakerFee, makerOrder.QuoteToken, statedb) 621 if err != nil { 622 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "takerExOwner", takerExOwner, "newTakerFee", *newTakerFee, "makerOrder.QuoteToken", makerOrder.QuoteToken) 623 } 624 err = tradingstate.SetTokenBalance(makerExOwner, newMakerFee, makerOrder.QuoteToken, statedb) 625 if err != nil { 626 log.Warn("DoSettleBalance SetTokenBalance", "err", err, "makerExOwner", makerExOwner, "newMakerFee", *newMakerFee, "makerOrder.QuoteToken", makerOrder.QuoteToken) 627 } 628 return nil 629 } 630 631 func (XDCx *XDCX) ProcessCancelOrder(header *types.Header, tradingStateDB *tradingstate.TradingStateDB, statedb *state.StateDB, chain consensus.ChainContext, coinbase common.Address, orderBook common.Hash, order *tradingstate.OrderItem) (error, bool) { 632 if err := tradingstate.CheckRelayerFee(order.ExchangeAddress, common.RelayerCancelFee, statedb); err != nil { 633 log.Debug("Relayer not enough fee when cancel order", "err", err) 634 return nil, true 635 } 636 baseTokenDecimal, err := XDCx.GetTokenDecimal(chain, statedb, order.BaseToken) 637 if err != nil || baseTokenDecimal.Sign() == 0 { 638 log.Debug("Fail to get tokenDecimal ", "Token", order.BaseToken.String(), "err", err) 639 return err, false 640 } 641 // order: basic order information (includes orderId, orderHash, baseToken, quoteToken) which user send to XDCx to cancel order 642 // originOrder: full order information getting from order trie 643 originOrder := tradingStateDB.GetOrder(orderBook, common.BigToHash(new(big.Int).SetUint64(order.OrderID))) 644 if originOrder == tradingstate.EmptyOrder { 645 return fmt.Errorf("order not found. OrderId: %v. Base: %s. Quote: %s", order.OrderID, order.BaseToken.Hex(), order.QuoteToken.Hex()), false 646 } 647 var tokenBalance *big.Int 648 switch originOrder.Side { 649 case tradingstate.Ask: 650 tokenBalance = tradingstate.GetTokenBalance(originOrder.UserAddress, originOrder.BaseToken, statedb) 651 case tradingstate.Bid: 652 tokenBalance = tradingstate.GetTokenBalance(originOrder.UserAddress, originOrder.QuoteToken, statedb) 653 default: 654 log.Debug("Not found order side", "Side", originOrder.Side) 655 return nil, false 656 } 657 log.Debug("ProcessCancelOrder", "baseToken", originOrder.BaseToken, "quoteToken", originOrder.QuoteToken) 658 feeRate := tradingstate.GetExRelayerFee(originOrder.ExchangeAddress, statedb) 659 tokenCancelFee, tokenPriceInXDC := common.Big0, common.Big0 660 if !chain.Config().IsTIPXDCXCancellationFee(header.Number) { 661 tokenCancelFee = getCancelFeeV1(baseTokenDecimal, feeRate, &originOrder) 662 } else { 663 tokenCancelFee, tokenPriceInXDC = XDCx.getCancelFee(chain, statedb, tradingStateDB, &originOrder, feeRate) 664 } 665 if tokenBalance.Cmp(tokenCancelFee) < 0 { 666 log.Debug("User not enough balance when cancel order", "Side", originOrder.Side, "balance", tokenBalance, "fee", tokenCancelFee) 667 return nil, true 668 } 669 670 err = tradingStateDB.CancelOrder(orderBook, order) 671 if err != nil { 672 log.Debug("Error when cancel order", "order", order) 673 return err, false 674 } 675 // relayers pay XDC for masternode 676 err = tradingstate.SubRelayerFee(originOrder.ExchangeAddress, common.RelayerCancelFee, statedb) 677 if err != nil { 678 log.Warn("ProcessCancelOrder SubRelayerFee", "err", err, "originOrder.ExchangeAddress", originOrder.ExchangeAddress, "common.RelayerCancelFee", *common.RelayerCancelFee) 679 } 680 masternodeOwner := statedb.GetOwner(coinbase) 681 // relayers pay XDC for masternode 682 statedb.AddBalance(masternodeOwner, common.RelayerCancelFee) 683 684 relayerOwner := tradingstate.GetRelayerOwner(originOrder.ExchangeAddress, statedb) 685 switch originOrder.Side { 686 case tradingstate.Ask: 687 // users pay token (which they have) for relayer 688 err := tradingstate.SubTokenBalance(originOrder.UserAddress, tokenCancelFee, originOrder.BaseToken, statedb) 689 if err != nil { 690 log.Warn("ProcessCancelOrder SubTokenBalance", "err", err, "originOrder.UserAddress", originOrder.UserAddress, "tokenCancelFee", *tokenCancelFee, "originOrder.BaseToken", originOrder.BaseToken) 691 } 692 err = tradingstate.AddTokenBalance(relayerOwner, tokenCancelFee, originOrder.BaseToken, statedb) 693 if err != nil { 694 log.Warn("ProcessCancelOrder AddTokenBalance", "err", err, "relayerOwner", relayerOwner, "tokenCancelFee", *tokenCancelFee, "originOrder.BaseToken", originOrder.BaseToken) 695 } 696 case tradingstate.Bid: 697 // users pay token (which they have) for relayer 698 err := tradingstate.SubTokenBalance(originOrder.UserAddress, tokenCancelFee, originOrder.QuoteToken, statedb) 699 if err != nil { 700 log.Warn("ProcessCancelOrder SubTokenBalance", "err", err, "originOrder.UserAddress", originOrder.UserAddress, "tokenCancelFee", *tokenCancelFee, "originOrder.QuoteToken", originOrder.QuoteToken) 701 } 702 err = tradingstate.AddTokenBalance(relayerOwner, tokenCancelFee, originOrder.QuoteToken, statedb) 703 if err != nil { 704 log.Warn("ProcessCancelOrder AddTokenBalance", "err", err, "relayerOwner", relayerOwner, "tokenCancelFee", *tokenCancelFee, "originOrder.QuoteToken", originOrder.QuoteToken) 705 } 706 default: 707 } 708 // update cancel fee 709 extraData, _ := json.Marshal(struct { 710 CancelFee string 711 TokenPriceInXDC string 712 }{ 713 CancelFee: tokenCancelFee.Text(10), 714 TokenPriceInXDC: tokenPriceInXDC.Text(10), 715 }) 716 order.ExtraData = string(extraData) 717 718 return nil, false 719 } 720 721 // cancellation fee = 1/10 trading fee 722 // deprecated after hardfork at TIPXDCXCancellationFee 723 func getCancelFeeV1(baseTokenDecimal *big.Int, feeRate *big.Int, order *tradingstate.OrderItem) *big.Int { 724 cancelFee := big.NewInt(0) 725 if order.Side == tradingstate.Ask { 726 // SELL 1 BTC => XDC ,, 727 // order.Quantity =1 && fee rate =2 728 // ==> cancel fee = 2/10000 729 // order.Quantity already included baseToken decimal 730 cancelFee = new(big.Int).Mul(order.Quantity, feeRate) 731 cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee) 732 } else { 733 // BUY 1 BTC => XDC with Price : 10000 734 // quoteTokenQuantity = 10000 && fee rate =2 735 // => cancel fee =2 736 quoteTokenQuantity := new(big.Int).Mul(order.Quantity, order.Price) 737 quoteTokenQuantity = new(big.Int).Div(quoteTokenQuantity, baseTokenDecimal) 738 // Fee 739 // makerFee = quoteTokenQuantity * feeRate / baseFee = quantityToTrade * makerPrice / baseTokenDecimal * feeRate / baseFee 740 cancelFee = new(big.Int).Mul(quoteTokenQuantity, feeRate) 741 cancelFee = new(big.Int).Div(cancelFee, common.XDCXBaseCancelFee) 742 } 743 return cancelFee 744 } 745 746 // return tokenQuantity, tokenPriceInXDC 747 func (XDCx *XDCX) getCancelFee(chain consensus.ChainContext, statedb *state.StateDB, tradingStateDb *tradingstate.TradingStateDB, order *tradingstate.OrderItem, feeRate *big.Int) (*big.Int, *big.Int) { 748 if feeRate == nil || feeRate.Sign() == 0 { 749 return common.Big0, common.Big0 750 } 751 cancelFee := big.NewInt(0) 752 tokenPriceInXDC := big.NewInt(0) 753 var err error 754 if order.Side == tradingstate.Ask { 755 cancelFee, tokenPriceInXDC, err = XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.BaseToken, common.RelayerCancelFee) 756 } else { 757 cancelFee, tokenPriceInXDC, err = XDCx.ConvertXDCToToken(chain, statedb, tradingStateDb, order.QuoteToken, common.RelayerCancelFee) 758 } 759 if err != nil { 760 return common.Big0, common.Big0 761 } 762 return cancelFee, tokenPriceInXDC 763 } 764 765 func (XDCx *XDCX) UpdateMediumPriceBeforeEpoch(epochNumber uint64, tradingStateDB *tradingstate.TradingStateDB, statedb *state.StateDB) error { 766 mapPairs, err := tradingstate.GetAllTradingPairs(statedb) 767 log.Debug("UpdateMediumPriceBeforeEpoch", "len(mapPairs)", len(mapPairs)) 768 769 if err != nil { 770 return err 771 } 772 epochPriceResult := map[common.Hash]*big.Int{} 773 for orderbook := range mapPairs { 774 oldMediumPriceBeforeEpoch := tradingStateDB.GetMediumPriceBeforeEpoch(orderbook) 775 mediumPriceCurrent, _ := tradingStateDB.GetMediumPriceAndTotalAmount(orderbook) 776 777 // if there is no trade in this epoch, use average price of last epoch 778 epochPriceResult[orderbook] = oldMediumPriceBeforeEpoch 779 if mediumPriceCurrent.Sign() > 0 && mediumPriceCurrent.Cmp(oldMediumPriceBeforeEpoch) != 0 { 780 log.Debug("UpdateMediumPriceBeforeEpoch", "mediumPriceCurrent", mediumPriceCurrent) 781 tradingStateDB.SetMediumPriceBeforeEpoch(orderbook, mediumPriceCurrent) 782 epochPriceResult[orderbook] = mediumPriceCurrent 783 } 784 tradingStateDB.SetMediumPrice(orderbook, tradingstate.Zero, tradingstate.Zero) 785 } 786 if XDCx.IsSDKNode() { 787 if err := XDCx.LogEpochPrice(epochNumber, epochPriceResult); err != nil { 788 log.Error("failed to update epochPrice", "err", err) 789 } 790 } 791 return nil 792 } 793 794 // put average price of epoch to mongodb for tracking liquidation trades 795 // epochPriceResult: a map of epoch average price, key is orderbook hash , value is epoch average price 796 // orderbook hash genereted from baseToken, quoteToken at XDPoSChain/XDCx/tradingstate/common.go:214 797 func (XDCx *XDCX) LogEpochPrice(epochNumber uint64, epochPriceResult map[common.Hash]*big.Int) error { 798 db := XDCx.GetMongoDB() 799 db.InitBulk() 800 801 for orderbook, price := range epochPriceResult { 802 if price.Sign() <= 0 { 803 continue 804 } 805 epochPriceItem := &tradingstate.EpochPriceItem{ 806 Epoch: epochNumber, 807 Orderbook: orderbook, 808 Price: price, 809 } 810 epochPriceItem.Hash = epochPriceItem.ComputeHash() 811 if err := db.PutObject(epochPriceItem.Hash, epochPriceItem); err != nil { 812 return err 813 } 814 } 815 if err := db.CommitBulk(); err != nil { 816 return err 817 } 818 return nil 819 }