예제 #1
0
        private List <TradeMatch> FindMatchedOrdersByAvailableBalance(string tradingPair, string side, decimal amount, string owner)
        {
            List <TradeMatch> result = new List <TradeMatch>();

            //Find all matches
            List <TradeMatch> matches = FindAllMatchedOrders(tradingPair, side, amount);

            //Filter out matches based on owner's available balance
            if (side == TradeSides.Buy)
            {
                //Get owner balance
                var           token         = tradingPair.Split('/')[1];
                IndexServices indexServices = new IndexServices();
                var           balance       = indexServices.BalanceIndex.Get(owner, token);

                decimal balanceValue = balance.Balance;

                int  i = 0;
                bool lookForNextMatch = true;
                while (lookForNextMatch)
                {
                    //Exit if no more orders available
                    if (i >= matches.Count)
                    {
                        lookForNextMatch = false;
                        break;
                    }

                    var trade         = matches[i];
                    var factoredPrice = ToFactoredPrice(trade.Price, token);

                    if (balanceValue >= (trade.Amount * factoredPrice))
                    {
                        //Full
                        result.Add(trade);
                    }
                    else
                    {
                        //Partial
                        trade.Amount        = Math.Floor(balanceValue / factoredPrice);
                        trade.PartialOrFull = TradePartialFull.Partial;
                        result.Add(trade);
                        lookForNextMatch = false;
                    }

                    balanceValue = balanceValue - (trade.Amount * factoredPrice);
                    i++;
                }
            }
            else if (side == TradeSides.Sell)
            {
                result = matches;
            }
            else
            {
                throw new ArgumentException();
            }

            return(result);
        }
예제 #2
0
        public decimal?GetMarketPrice(string tradingPair, string side)
        {
            IndexServices indexServices = new IndexServices();
            var           orders        = indexServices.OrderIndex.GetByTradingPair(tradingPair);

            if (orders != null)
            {
                orders = orders.Where(x => x.Side == side).ToList();
                if (orders.Count == 0)
                {
                    return(null);
                }

                if (side == TradeSides.Buy)
                {
                    return(orders.Min(x => x.Price));
                }
                else if (side == TradeSides.Sell)
                {
                    return(orders.Max(x => x.Price));
                }
                else
                {
                    throw new ArgumentException("GetMarketPrice: Parameter side not recognized: " + side);
                }
            }
            else
            {
                return(null);
            }
        }
예제 #3
0
        public decimal?GetWalletBalance(string address, string tokenSymbol)
        {
            decimal?balance = null;

            IndexServices indexService = new IndexServices();
            var           index        = indexService.BalanceIndex.Get(address, tokenSymbol);

            if (index != null)
            {
                balance = FromFactoredPrice(index.Balance, indexService.TokenIndex.Get(tokenSymbol).Decimals);
            }

            return(balance);
        }
예제 #4
0
        public void CreateGenesisBlock(
            string ownerAddress,
            string nativeTokenName,
            string nativeTokenSymbol,
            long initialSupply,
            short decimals)
        {
            if (LastBlock != null || (ApplicationState.PendingRecords != null && ApplicationState.PendingRecords.Count > 0))
            {
                throw new Exception("Error CreateGenesisBlock: Chain already exist.");
            }

            List <Transaction> transactions = new List <Transaction>();

            //Deploy native token to genesis block
            TransactionToken nativeToken = new TransactionToken();

            nativeToken.Name          = nativeTokenName;
            nativeToken.Symbol        = nativeTokenSymbol;
            nativeToken.Decimals      = decimals;
            nativeToken.TransactionId = GenerateTransactionId(nativeToken);
            transactions.Add(nativeToken);

            //Transfer native token supply to owner address
            TransactionTransfer transfer = new TransactionTransfer();

            transfer.TokenSymbol   = nativeTokenSymbol;
            transfer.FromAddress   = null;
            transfer.ToAddress     = ownerAddress;
            transfer.Amount        = ToFactoredPrice(initialSupply, decimals);
            transfer.TransactionId = GenerateTransactionId(transfer);
            transactions.Add(transfer);

            ApplicationState.PendingRecordsAddRange(transactions);

            //Create genesis block
            var header = CreateBlockHeader(true);
            var block  = CreateBlockAndClearPendingTx(header);

            //Push genesis block to blockchain
            database.SaveBlock(block);

            //Update index
            IndexServices indexServices = new IndexServices();

            indexServices.UpdateIndex(block);
        }
예제 #5
0
        public string CancelOrder(
            long nonce,
            decimal fee,
            string orderTransactionId,
            string owner)
        {
            //Verify fee account balance
            if (!HasSufficientBalanceFee(owner, fee))
            {
                throw new ValidationException(ResponseCodes.InsufficientFundsForFee, ResponseMessages.InsufficientFundsForFee);
            }

            //Check if the cancelling transaction belongs to this owner
            IndexServices indexServices = new IndexServices();
            var           orderToCancel = indexServices.OrderIndex.Get(orderTransactionId);

            if (orderToCancel.Owner != owner)
            {
                throw new ValidationException(ResponseCodes.OrderDoesNotBelongToRequester, ResponseMessages.OrderDoesNotBelongToRequester);
            }

            TransactionOrderCancel order = new TransactionOrderCancel();

            order.Nonce = nonce;
            order.OrderTransactionId = orderTransactionId;
            order.Owner         = owner;
            order.TransactionId = GenerateTransactionId(order);

            //Check if same transaction has already been received
            if (IsTransactionInPending(order.TransactionId))
            {
                return(order.TransactionId);
            }

            //Delete from index so new orders does not match with this cancelled order
            indexServices.OrderIndex.DeleteSingleTransaction(orderTransactionId);

            //Note: Do not remove the cancelled order from pending records, otherwise the order would not go into next block
            //hence this transaction is unable to tie back to the originally cancelled order in the block


            ApplicationState.PendingRecordsAdd(order);

            return(order.TransactionId);
        }
예제 #6
0
        public void RegisterSelfOnNetwork(string walletAddress, string signature, bool broadcast)
        {
            ApplicationLog.Info("Registering self on the network.");

            //Broadcast to other nodes
            if (broadcast)
            {
                var nodes = ApplicationState.ConnectedNodesExceptSelf;
                Parallel.ForEach(nodes, new ParallelOptions {
                    MaxDegreeOfParallelism = ConstantConfig.BroadcastThreadCount
                }, networkNode =>
                {
                    ApiClient api = new ApiClient(networkNode.ServerAddress);
                    api.AnnounceRegisterNode(ApplicationState.ServerAddress, walletAddress, signature);
                });
            }

            //Register self in local nodes cache

            //Get wallet balance
            decimal       walletBalance = 0;
            IndexServices indexServices = new IndexServices();
            var           nativeToken   = indexServices.TokenIndex.GetNative();

            if (nativeToken != null) //Native token would be null if chain has not yet been sync / no local chain
            {
                var walletIndex = indexServices.BalanceIndex.Get(walletAddress, indexServices.TokenIndex.GetNative().Symbol);
                if (walletIndex != null)
                {
                    walletBalance = walletIndex.Balance;
                }
            }

            //Add node to local consensus ledger
            Node node = new Node()
            {
                ServerAddress = ApplicationState.ServerAddress,
                WalletAddress = walletAddress,
                Signature     = signature,
                Balance       = walletBalance
            };

            AddConsensusNode(node);
            ApplicationState.Node = node;
        }
예제 #7
0
        public void SaveBlockToChain(Block block)
        {
            //Validate block hash
            var previousBlock = database.GetBlock(block.Header.Index - 1);

            if (previousBlock.Header.Hash != block.Header.PreviousHash)
            {
                throw new ValidationException(ResponseCodes.InvalidHash, ResponseMessages.InvalidHash);
            }

            //Push the new block to blockchain
            database.SaveBlock(block);

            //Update index
            IndexServices indexService = new IndexServices();

            indexService.UpdateIndex(block);
        }
예제 #8
0
        /// <summary>
        /// Returns the network fee based on number of transactions in the latest block
        /// </summary>
        public long GetNetworkFee()
        {
            DataServices dataServices = new DataServices();
            var          lastBlock    = dataServices.LastBlock;

            if (lastBlock != null)
            {
                IndexServices indexServices = new IndexServices();
                var           nativeToken   = indexServices.TokenIndex.GetNative();

                var multiplier = Convert.ToInt64(Math.Floor(Convert.ToDecimal(lastBlock.Header.TransactionCount / ConstantConfig.NetworkFeeUnit)));
                var rate       = ToFactoredPrice(ConstantConfig.NetworkFeeRate, nativeToken.Decimals);
                return(rate * multiplier);
            }
            else
            {
                return(0);
            }
        }
예제 #9
0
        protected bool HasSufficientBalance(string address, string symbol, decimal amount)
        {
            if (amount <= 0)
            {
                return(true);
            }

            IndexServices indexServices = new IndexServices();
            var           factoredPrice = ToFactoredPrice(amount, indexServices.TokenIndex.Get(symbol).Decimals);

            //Verify account balance
            var senderWallet = indexServices.BalanceIndex.Get(address, symbol);

            if (senderWallet == null || senderWallet.Balance < factoredPrice)
            {
                return(false);
            }
            else
            {
                return(true);
            }
        }
예제 #10
0
        //TODO: for IoC orders
        //FindMatchedOrdersByGivenBalance

        /// <summary>
        /// Find all trades that match for this order and returns the list of matched orders
        /// </summary>
        private List <TradeMatch> FindAllMatchedOrders(string tradingPair, string side, decimal amount)
        {
            List <TradeMatch> result = new List <TradeMatch>();

            decimal balanceAmount = amount;

            IndexServices indexServices = new IndexServices();
            var           orders        = indexServices.OrderIndex.GetByTradingPair(tradingPair);

            if (orders == null || orders.Count == 0)
            {
                throw new ValidationException(ResponseCodes.NotEnoughOrdersAvailable, ResponseMessages.NotEnoughOrdersAvailable);
            }

            if (side == TradeSides.Buy)
            {
                //Get sell orders sorted by lowest price
                var sellOrders = orders
                                 .Where(x => x.Side == TradeSides.Sell)
                                 .OrderBy(x => x.Price)
                                 .ToList();

                int  i = 0;
                bool lookForNextMatch = true;
                while (lookForNextMatch)
                {
                    //Exit if no more orders available
                    if (i >= sellOrders.Count ||
                        balanceAmount == 0)
                    {
                        lookForNextMatch = false;
                        break;
                    }

                    var currentOrder = sellOrders[i];

                    //Check full/partial
                    if (balanceAmount >= currentOrder.Amount)
                    {
                        result.Add(new TradeMatch()
                        {
                            TransactionId = currentOrder.TransactionId,
                            Address       = currentOrder.Owner,
                            TradingPair   = tradingPair,
                            Side          = currentOrder.Side,
                            Amount        = currentOrder.Amount,
                            Price         = currentOrder.Price,
                            PartialOrFull = TradePartialFull.Full
                        });

                        balanceAmount = balanceAmount - currentOrder.Amount;
                        i++;
                    }
                    else
                    {
                        result.Add(new TradeMatch()
                        {
                            TransactionId = currentOrder.TransactionId,
                            Address       = currentOrder.Owner,
                            TradingPair   = tradingPair,
                            Side          = currentOrder.Side,
                            Amount        = balanceAmount,
                            Price         = currentOrder.Price,
                            PartialOrFull = TradePartialFull.Partial
                        });

                        lookForNextMatch = false;
                        balanceAmount    = 0;
                        i++;
                    }
                }
            }
            else if (side == TradeSides.Sell)
            {
                //Get buy orders sorted by highest price
                var buyOrders = orders
                                .Where(x => x.Side == TradeSides.Buy)
                                .OrderByDescending(x => x.Price)
                                .ToList();

                int  i = 0;
                bool lookForNextMatch = true;
                while (lookForNextMatch)
                {
                    var currentOrder = buyOrders[i];

                    //Check if there is anymore order
                    if (i >= buyOrders.Count)
                    {
                        lookForNextMatch = false;
                        break;
                    }

                    //Check full/partial
                    if (balanceAmount > currentOrder.Amount)
                    {
                        result.Add(new TradeMatch()
                        {
                            TransactionId = currentOrder.TransactionId,
                            Address       = currentOrder.Owner,
                            TradingPair   = tradingPair,
                            Side          = currentOrder.Side,
                            Amount        = currentOrder.Amount,
                            Price         = currentOrder.Price,
                            PartialOrFull = TradePartialFull.Full
                        });

                        balanceAmount = balanceAmount - currentOrder.Amount;
                        i++;
                    }
                    else
                    {
                        result.Add(new TradeMatch()
                        {
                            TransactionId = currentOrder.TransactionId,
                            Address       = currentOrder.Owner,
                            TradingPair   = tradingPair,
                            Side          = currentOrder.Side,
                            Amount        = balanceAmount,
                            Price         = currentOrder.Price,
                            PartialOrFull = TradePartialFull.Partial
                        });

                        lookForNextMatch = false;
                        balanceAmount    = 0;
                        i++;
                    }
                }
            }
            else
            {
                throw new ArgumentException();
            }

            return(result);
        }
예제 #11
0
 public TradeServices()
 {
     indexServices = new IndexServices();
 }
예제 #12
0
 public TransactionServices()
 {
     indexServices = new IndexServices();
 }
예제 #13
0
        protected bool HasSufficientBalanceFee(string address, decimal amount)
        {
            IndexServices indexServices = new IndexServices();

            return(HasSufficientBalance(address, indexServices.TokenIndex.GetNative().Symbol, amount));
        }
예제 #14
0
        protected long FromFactoredPrice(decimal amount, string symbol)
        {
            IndexServices indexServices = new IndexServices();

            return(FromFactoredPrice(amount, indexServices.TokenIndex.Get(symbol).Decimals));
        }
예제 #15
0
        protected long ToFactoredPriceFee(decimal amount)
        {
            IndexServices indexServices = new IndexServices();

            return(ToFactoredPrice(amount, indexServices.TokenIndex.GetNative().Decimals));
        }