public ExchangeOrder(ExchangeOrder order, BigInteger newPrice, BigInteger newOrderSize) { Uid = order.Uid; Timestamp = order.Timestamp; Creator = order.Creator; Provider = order.Provider; Amount = newOrderSize; BaseSymbol = order.BaseSymbol; Price = newOrderSize; QuoteSymbol = order.QuoteSymbol; Side = order.Side; Type = order.Type; }
public ExchangeOrder GetExchangeOrder(BigInteger uid) { Runtime.Expect(_orderMap.ContainsKey <BigInteger>(uid), "order not found"); var key = _orderMap.Get <BigInteger, string>(uid); StorageList orderList = _orders.Get <string, StorageList>(key); var count = orderList.Count(); var order = new ExchangeOrder(); for (int i = 0; i < count; i++) { order = orderList.Get <ExchangeOrder>(i); if (order.Uid == uid) { //Runtime.Expect(Runtime.IsWitness(order.Creator), "invalid witness"); break; } } return(order); }
private void OpenOrder(Address from, Address provider, string baseSymbol, string quoteSymbol, ExchangeOrderSide side, ExchangeOrderType orderType, BigInteger orderSize, BigInteger price) { Runtime.Expect(Runtime.IsWitness(from), "invalid witness"); Runtime.Expect(Runtime.GasTarget == provider, "invalid gas target"); Runtime.Expect(baseSymbol != quoteSymbol, "invalid base/quote pair"); Runtime.Expect(Runtime.TokenExists(baseSymbol), "invalid base token"); var baseToken = Runtime.GetToken(baseSymbol); Runtime.Expect(baseToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); Runtime.Expect(Runtime.TokenExists(quoteSymbol), "invalid quote token"); var quoteToken = Runtime.GetToken(quoteSymbol); Runtime.Expect(quoteToken.Flags.HasFlag(TokenFlags.Fungible), "token must be fungible"); if (orderType != Market) { Runtime.Expect(orderSize >= GetMinimumTokenQuantity(baseToken), "order size is not sufficient"); Runtime.Expect(price >= GetMinimumTokenQuantity(quoteToken), "order price is not sufficient"); } var uid = Runtime.GenerateUID(); //-------------- //perform escrow for non-market orders string orderEscrowSymbol = CalculateEscrowSymbol(baseToken, quoteToken, side); IToken orderEscrowToken = orderEscrowSymbol == baseSymbol ? baseToken : quoteToken; BigInteger orderEscrowAmount; BigInteger orderEscrowUsage = 0; if (orderType == Market) { orderEscrowAmount = orderSize; Runtime.Expect(orderEscrowAmount >= GetMinimumTokenQuantity(orderEscrowToken), "market order size is not sufficient"); } else { orderEscrowAmount = CalculateEscrowAmount(orderSize, price, baseToken, quoteToken, side); } //BigInteger baseTokensUnfilled = orderSize; var balance = Runtime.GetBalance(orderEscrowSymbol, from); Runtime.Expect(balance >= orderEscrowAmount, "not enough balance"); Runtime.TransferTokens(orderEscrowSymbol, from, this.Address, orderEscrowAmount); //------------ var thisOrder = new ExchangeOrder(); StorageList orderList; BigInteger orderIndex = 0; thisOrder = new ExchangeOrder(uid, Runtime.Time, from, provider, orderSize, baseSymbol, price, quoteSymbol, side, orderType); Runtime.Notify(EventKind.OrderCreated, from, uid); var key = BuildOrderKey(side, quoteSymbol, baseSymbol); orderList = _orders.Get <string, StorageList>(key); orderIndex = orderList.Add <ExchangeOrder>(thisOrder); _orderMap.Set <BigInteger, string>(uid, key); var makerSide = side == Buy ? Sell : Buy; var makerKey = BuildOrderKey(makerSide, quoteSymbol, baseSymbol); var makerOrders = _orders.Get <string, StorageList>(makerKey); do { int bestIndex = -1; BigInteger bestPrice = 0; Timestamp bestPriceTimestamp = 0; ExchangeOrder takerOrder = thisOrder; var makerOrdersCount = makerOrders.Count(); for (int i = 0; i < makerOrdersCount; i++) { var makerOrder = makerOrders.Get <ExchangeOrder>(i); if (side == Buy) { if (makerOrder.Price > takerOrder.Price && orderType != Market) // too expensive, we wont buy at this price { continue; } if (bestIndex == -1 || makerOrder.Price < bestPrice || (makerOrder.Price == bestPrice && makerOrder.Timestamp < bestPriceTimestamp)) { bestIndex = i; bestPrice = makerOrder.Price; bestPriceTimestamp = makerOrder.Timestamp; } } else { if (makerOrder.Price < takerOrder.Price && orderType != Market) // too cheap, we wont sell at this price { continue; } if (bestIndex == -1 || makerOrder.Price > bestPrice || (makerOrder.Price == bestPrice && makerOrder.Timestamp < bestPriceTimestamp)) { bestIndex = i; bestPrice = makerOrder.Price; bestPriceTimestamp = makerOrder.Timestamp; } } } if (bestIndex >= 0) { //since order "uid" has found a match, the creator of this order will be a taker as he will remove liquidity from the market //and the creator of the "bestIndex" order is the maker as he is providing liquidity to the taker var takerAvailableEscrow = orderEscrowAmount - orderEscrowUsage; var takerEscrowUsage = BigInteger.Zero; var takerEscrowSymbol = orderEscrowSymbol; var makerOrder = makerOrders.Get <ExchangeOrder>(bestIndex); var makerEscrow = _escrows.Get <BigInteger, BigInteger>(makerOrder.Uid); var makerEscrowUsage = BigInteger.Zero;; var makerEscrowSymbol = orderEscrowSymbol == baseSymbol ? quoteSymbol : baseSymbol; //Get fulfilled order size in base tokens //and then calculate the corresponding fulfilled order size in quote tokens if (takerEscrowSymbol == baseSymbol) { var makerEscrowBaseEquivalent = Runtime.ConvertQuoteToBase(makerEscrow, makerOrder.Price, baseToken, quoteToken); takerEscrowUsage = takerAvailableEscrow < makerEscrowBaseEquivalent ? takerAvailableEscrow : makerEscrowBaseEquivalent; makerEscrowUsage = CalculateEscrowAmount(takerEscrowUsage, makerOrder.Price, baseToken, quoteToken, Buy); } else { var takerEscrowBaseEquivalent = Runtime.ConvertQuoteToBase(takerAvailableEscrow, makerOrder.Price, baseToken, quoteToken); makerEscrowUsage = makerEscrow < takerEscrowBaseEquivalent ? makerEscrow : takerEscrowBaseEquivalent; takerEscrowUsage = CalculateEscrowAmount(makerEscrowUsage, makerOrder.Price, baseToken, quoteToken, Buy); } Runtime.Expect(takerEscrowUsage <= takerAvailableEscrow, "Taker tried to use more escrow than available"); Runtime.Expect(makerEscrowUsage <= makerEscrow, "Maker tried to use more escrow than available"); if (takerEscrowUsage < GetMinimumSymbolQuantity(takerEscrowSymbol) || makerEscrowUsage < GetMinimumSymbolQuantity(makerEscrowSymbol)) { break; } Runtime.TransferTokens(takerEscrowSymbol, this.Address, makerOrder.Creator, takerEscrowUsage); Runtime.TransferTokens(makerEscrowSymbol, this.Address, takerOrder.Creator, makerEscrowUsage); orderEscrowUsage += takerEscrowUsage; Runtime.Notify(EventKind.OrderFilled, takerOrder.Creator, takerOrder.Uid); Runtime.Notify(EventKind.OrderFilled, makerOrder.Creator, makerOrder.Uid); if (makerEscrowUsage == makerEscrow) { makerOrders.RemoveAt <ExchangeOrder>(bestIndex); _orderMap.Remove(makerOrder.Uid); Runtime.Expect(_escrows.ContainsKey(makerOrder.Uid), "An orderbook entry must have registered escrow"); _escrows.Remove(makerOrder.Uid); Runtime.Notify(EventKind.OrderClosed, makerOrder.Creator, makerOrder.Uid); } else { _escrows.Set(makerOrder.Uid, makerEscrow - makerEscrowUsage); } } else { break; } } while (orderEscrowUsage < orderEscrowAmount); var leftoverEscrow = orderEscrowAmount - orderEscrowUsage; if (leftoverEscrow == 0 || orderType != Limit) { orderList.RemoveAt <ExchangeOrder>(orderIndex); _orderMap.Remove(thisOrder.Uid); _escrows.Remove(thisOrder.Uid); if (leftoverEscrow > 0) { Runtime.TransferTokens(orderEscrowSymbol, this.Address, thisOrder.Creator, leftoverEscrow); Runtime.Notify(EventKind.OrderCancelled, thisOrder.Creator, thisOrder.Uid); } else { Runtime.Notify(EventKind.OrderClosed, thisOrder.Creator, thisOrder.Uid); } } else { _escrows.Set(uid, leftoverEscrow); } //TODO: ADD FEES, SEND THEM TO this.Address FOR NOW }