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); }
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); } }
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); }
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); }
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); }
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; }
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); }
/// <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); } }
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); } }
//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); }
public TradeServices() { indexServices = new IndexServices(); }
public TransactionServices() { indexServices = new IndexServices(); }
protected bool HasSufficientBalanceFee(string address, decimal amount) { IndexServices indexServices = new IndexServices(); return(HasSufficientBalance(address, indexServices.TokenIndex.GetNative().Symbol, amount)); }
protected long FromFactoredPrice(decimal amount, string symbol) { IndexServices indexServices = new IndexServices(); return(FromFactoredPrice(amount, indexServices.TokenIndex.Get(symbol).Decimals)); }
protected long ToFactoredPriceFee(decimal amount) { IndexServices indexServices = new IndexServices(); return(ToFactoredPrice(amount, indexServices.TokenIndex.GetNative().Decimals)); }