From eaf14093b53fb703f92ff76f96f2bf1f46b1d494 Mon Sep 17 00:00:00 2001 From: Kalakoi Date: Fri, 15 Dec 2017 12:57:02 -0500 Subject: [PATCH] Add files via upload --- src/ShapeShift/CancelResult.cs | 93 ++++++++++++ src/ShapeShift/EmailReceipt.cs | 103 +++++++++++++ src/ShapeShift/QuoteRequest.cs | 139 ++++++++++++++++++ src/ShapeShift/RecentTx.cs | 116 +++++++++++++++ src/ShapeShift/SendAmountRequest.cs | 185 ++++++++++++++++++++++++ src/ShapeShift/ShiftResult.cs | 166 +++++++++++++++++++++ src/ShapeShift/SupportedCoin.cs | 127 ++++++++++++++++ src/ShapeShift/TimeRemaining.cs | 84 +++++++++++ src/ShapeShift/TradingLimit.cs | 103 +++++++++++++ src/ShapeShift/TradingMarketInfo.cs | 135 +++++++++++++++++ src/ShapeShift/TradingPair.cs | 65 +++++++++ src/ShapeShift/TradingRate.cs | 104 +++++++++++++ src/ShapeShift/Tx.cs | 217 ++++++++++++++++++++++++++++ src/ShapeShift/TxStatus.cs | 175 ++++++++++++++++++++++ src/ShapeShift/ValidateAddress.cs | 82 +++++++++++ 15 files changed, 1894 insertions(+) create mode 100644 src/ShapeShift/CancelResult.cs create mode 100644 src/ShapeShift/EmailReceipt.cs create mode 100644 src/ShapeShift/QuoteRequest.cs create mode 100644 src/ShapeShift/RecentTx.cs create mode 100644 src/ShapeShift/SendAmountRequest.cs create mode 100644 src/ShapeShift/ShiftResult.cs create mode 100644 src/ShapeShift/SupportedCoin.cs create mode 100644 src/ShapeShift/TimeRemaining.cs create mode 100644 src/ShapeShift/TradingLimit.cs create mode 100644 src/ShapeShift/TradingMarketInfo.cs create mode 100644 src/ShapeShift/TradingPair.cs create mode 100644 src/ShapeShift/TradingRate.cs create mode 100644 src/ShapeShift/Tx.cs create mode 100644 src/ShapeShift/TxStatus.cs create mode 100644 src/ShapeShift/ValidateAddress.cs diff --git a/src/ShapeShift/CancelResult.cs b/src/ShapeShift/CancelResult.cs new file mode 100644 index 0000000..abb569f --- /dev/null +++ b/src/ShapeShift/CancelResult.cs @@ -0,0 +1,93 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to cancel pending exchanges. + /// + public class CancelResult + { + /*url: shapeshift.io/cancelpending + method: POST + data type: JSON + data required: address = The deposit address associated with the pending transaction + + Example data : {address : "1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v"} + + Success Output: + + { success : " Pending Transaction cancelled " } + + Error Output: + + { error : {errorMessage} } + */ + + /// + /// True if cancel was successful. + /// + public bool Success { get; private set; } + /// + /// Message returned from cancel operation, if any. + /// + public string Message { get; private set; } + /// + /// Error message thrown during cancel, if any. + /// + public string Error { get; private set; } + + private CancelResult() { } + + /// + /// Attempts to cancel pending exchange + /// + /// The deposit address associated with the pending transaction. + /// Result of cancel operation. + internal static async Task CancelAsync(string Address) + { + //Get URI for POST request + Uri uri = GetUri(); + //Generate JSON data string + string data = CreateData(Address); + //Send POST request + string response = await RestServices.GetPostResponseAsync(uri, data).ConfigureAwait(false); + //Parse response for results + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri() => + new Uri(@"https://shapeshift.io/cancelpending"); + + private static string CreateData(string Address) => + "{" + string.Format("\"address\":\"{0}\"", Address) + "}"; + + private static async Task ParseResponseAsync(string response) + { + CancelResult result = new CancelResult(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "success") + { + result.Success = true; + await jtr.ReadAsync().ConfigureAwait(false); + result.Message = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "error") + { + result.Success = false; + await jtr.ReadAsync().ConfigureAwait(false); + result.Error = jtr.Value.ToString(); + } + else continue; + } + } + return result; + } + } +} diff --git a/src/ShapeShift/EmailReceipt.cs b/src/ShapeShift/EmailReceipt.cs new file mode 100644 index 0000000..5175940 --- /dev/null +++ b/src/ShapeShift/EmailReceipt.cs @@ -0,0 +1,103 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Status of Email request. + /// + public enum EmailStatuses { Success, Failure } + + /// + /// Provides ability to request a receipt for a transaction to be sent via email. + /// + public class EmailReceipt + { + /*url: shapeshift.io/mail + method: POST + data type: JSON + data required: + email = the address for receipt email to be sent to + txid = the transaction id of the transaction TO the user (ie the txid for the withdrawal NOT the deposit) + example data {"email":"mail@example.com", "txid":"123ABC"} + + Success Output: + {"email": + { + "status":"success", + "message":"Email receipt sent" + } + } + */ + + /// + /// Status of receipt request. + /// + public EmailStatuses Status { get; private set; } + /// + /// Message returned by request, if any. + /// + public string Message { get; private set; } + /// + /// Error thrown by request, if any. + /// + public string Error { get; private set; } + + private EmailReceipt() { } + + /// + /// Requests a receipt for transaction to be sent via email. + /// + /// Email address to send receipt to. + /// Transaction ID of the transaction sent to the user. + /// Result of receipt request. + internal static async Task RequestAsync(string Email, string TxID) + { + //Get URI for POST request + Uri uri = GetUri(); + //Generate JSON data as string to send + string data = CreateData(Email, TxID); + //Send POST request and awaits response + string response = await RestServices.GetPostResponseAsync(uri, data).ConfigureAwait(false); + //Parse response for results + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri() => + new Uri(@"https://shapeshift.io/mail"); + + private static string CreateData(string Email, string TxID) => + "{" + string.Format("\"email\":\"{0}\", \"txid\":\"{1}\"", Email, TxID) + "}"; + + private static async Task ParseResponseAsync(string response) + { + EmailReceipt receipt = new EmailReceipt(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "status") + { + await jtr.ReadAsync().ConfigureAwait(false); + receipt.Status = jtr.Value.ToString() == "success" ? EmailStatuses.Success : EmailStatuses.Failure; + } + else if (jtr.Value.ToString() == "message") + { + await jtr.ReadAsync().ConfigureAwait(false); + receipt.Message = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + receipt.Error = jtr.Value.ToString(); + } + else continue; + } + } + return receipt; + } + } +} diff --git a/src/ShapeShift/QuoteRequest.cs b/src/ShapeShift/QuoteRequest.cs new file mode 100644 index 0000000..87a0ce9 --- /dev/null +++ b/src/ShapeShift/QuoteRequest.cs @@ -0,0 +1,139 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to request quotes for specific pair exchanges. + /// + public class QuoteRequest + { + /*url: shapeshift.io/sendamount + method: POST + data type: JSON + + Data required: + + amount = the amount to be sent to the withdrawal address + pair = what coins are being exchanged in the form [input coin]_[output coin] ie ltc_btc + + example data {"amount":123, "pair":"ltc_btc"} + + Success Output: + + { + success: + { + pair: [pair], + withdrawalAmount: [Withdrawal Amount], // Amount of the output coin you will receive + depositAmount: [Deposit Amount], // Exact amount of input coin to send in + expiration: [timestamp when this will expire], + quotedRate: [the exchange rate to be honored] + minerFee: [miner fee for this transaction] + } + } + */ + + /// + /// Coin pair to exchange between. + /// + public string Pair { get; private set; } + /// + /// Amount of coin to be received by user. + /// + public double WithdrawalAmount { get; private set; } + /// + /// Amount of coin required to be deposited. + /// + public double DepositAmount { get; private set; } + /// + /// Expiration timestamp of quote. + /// + public double Expiration { get; private set; } + /// + /// Quoted rate of exchange. + /// + public double QuotedRate { get; private set; } + /// + /// Fee to be sent to miners to fascilitate exchange. + /// + public double MinerFee { get; private set; } + /// + /// Error thrown by request, if any. + /// + public string Error { get; private set; } + + private QuoteRequest() { } + + /// + /// Requests a quote for an exchange without exchanging. + /// + /// Coin pair to exchange between. + /// Amount of coin to be sent to withdrawal address. + /// Quote for exchange information. + internal static async Task RequestAsync(string Pair, double Amount) + { + Uri uri = GetUri(); + string data = CreateData(Pair, Amount); + string response = await RestServices.GetPostResponseAsync(uri, data).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri() => + new Uri(@"https://shapeshift.io/sendamount"); + + private static string CreateData(string Pair, double Amount) => + "{" + string.Format("\"amount\":\"{0}\", \"pair\":\"{1}\"", Amount.ToString(), Pair) + "}"; + + private static async Task ParseResponseAsync(string response) + { + QuoteRequest request = new QuoteRequest(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "pair") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Pair = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdrawalAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.WithdrawalAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "depositAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.DepositAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "expiration") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Expiration = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "quotedRate") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.QuotedRate = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "minerFee") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.MinerFee = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Error = jtr.Value.ToString(); + } + else continue; + } + } + return request; + } + } +} diff --git a/src/ShapeShift/RecentTx.cs b/src/ShapeShift/RecentTx.cs new file mode 100644 index 0000000..ed6a71f --- /dev/null +++ b/src/ShapeShift/RecentTx.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides access to information on recent exchanges completed by ShapeShift. + /// + public class RecentTx + { + /*url: shapeshift.io/recenttx/[max] + method: GET + + [max] is an optional maximum number of transactions to return. + If [max] is not specified this will return 5 transactions. + Also, [max] must be a number between 1 and 50 (inclusive). + + Success Output: + [ + { + curIn : [currency input], + curOut: [currency output], + amount: [amount], + timestamp: [time stamp] //in seconds + }, + ... + ] + */ + + /// + /// Currency user sent to exchange. + /// + public string CurrencyInput { get; private set; } + /// + /// Currency user requested from exchange. + /// + public string CurrencyOutput { get; private set; } + /// + /// Amount sent to user's withdrawal address. + /// + public double Amount { get; private set; } + /// + /// Timestamp of exchange. + /// + public double TimeStamp { get; private set; } + + /// + /// Gets information on recent transactions completed by ShapeShift. + /// + /// Maximum number of transactions to return. Must be betweeen 1 and 50, inclusive. + /// List of recent transactions. + internal static async Task> GetRecentTransactionsAsync(int Max) + { + if (Max < 1 || Max > 50) throw new InvalidOperationException(); + Uri uri = GetUri(Max); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + /// + /// Gets information on recent transactions completed by ShapeShift. + /// + /// List of last 5 transactions. + internal static async Task> GetRecentTransactionsAsync() => + await GetRecentTransactionsAsync(5).ConfigureAwait(false); + + private static Uri GetUri(int max = 5) => + new Uri(string.Format(@"https://shapeshift.io/recenttx/{0}", max.ToString())); + + private static async Task> ParseResponseAsync(string response) + { + List TxList = new List(); + RecentTx NewTx = new RecentTx(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.TokenType.ToString() == "StartObject") + { + if (!string.IsNullOrEmpty(NewTx.CurrencyInput)) + TxList.Add(NewTx); + NewTx = new RecentTx(); + } + else if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "curIn") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.CurrencyInput = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "curOut") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.CurrencyOutput = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "amount") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.Amount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "timestamp") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.TimeStamp = Convert.ToDouble(jtr.Value.ToString()); + } + else continue; + } + } + if (!string.IsNullOrEmpty(NewTx.CurrencyInput)) + TxList.Add(NewTx); + + return TxList; + } + } +} diff --git a/src/ShapeShift/SendAmountRequest.cs b/src/ShapeShift/SendAmountRequest.cs new file mode 100644 index 0000000..e2f2b5d --- /dev/null +++ b/src/ShapeShift/SendAmountRequest.cs @@ -0,0 +1,185 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to request amount needed to be sent to complete transaction. + /// + public class SendAmountRequest + { + /*url: shapeshift.io/sendamount + method: POST + data type: JSON + + Data required: + + amount = the amount to be sent to the withdrawal address + withdrawal = the address for coin to be sent to + pair = what coins are being exchanged in the form [input coin]_[output coin] ie ltc_btc + returnAddress = (Optional) address to return deposit to if anything goes wrong with exchange + destTag = (Optional) Destination tag that you want appended to a Ripple payment to you + rsAddress = (Optional) For new NXT accounts to be funded, supply this on NXT payment to you + apiKey = (Optional) Your affiliate PUBLIC KEY, for volume tracking, affiliate payments, split-shifts, etc... + + example data {"amount":123, "withdrawal":"123ABC", "pair":"ltc_btc", returnAddress:"BBBBBBB"} + + Success Output: + + { + success: + { + pair: [pair], + withdrawal: [Withdrawal Address], //-- will match address submitted in post + withdrawalAmount: [Withdrawal Amount], // Amount of the output coin you will receive + deposit: [Deposit Address (or memo field if input coin is BTS / BITUSD)], + depositAmount: [Deposit Amount], // Exact amount of input coin to send in + expiration: [timestamp when this will expire], + quotedRate: [the exchange rate to be honored] + apiPubKey: [public API attached to this shift, if one was given] + } + } + */ + + /// + /// Coin pair requested for exchange. + /// + public string Pair { get; private set; } + /// + /// Address to send exchanged coins to. + /// + public string WithdrawalAddress { get; private set; } + /// + /// Amount of coins to be received from exchange. + /// + public double WithdrawalAmount { get; private set; } + /// + /// Address to send coins to be exchanged. + /// + public string DepositAddress { get; private set; } + /// + /// Amount of coins required to complete exchange. + /// + public double DepositAmount { get; private set; } + /// + /// Exchange expiration timestamp. + /// + public double Expiration { get; private set; } + /// + /// Quoted rate of exchange. + /// + public double QuotedRate { get; private set; } + /// + /// Public API key attached to this exchange, if any. + /// + public string APIKey { get; private set; } + /// + /// Error thrown by request, if any. + /// + public string Error { get; private set; } + + private SendAmountRequest() { } + + /// + /// Gets information on pending exchange. + /// + /// Amount to be sent to withdrawal address. + /// The withdrawal address. + /// The coin pair. + /// Address to return coins to if exchange fails. + /// Destination tag that you want appended to a Ripple payment to you. + /// For new NXT accounts to be funded, supply this on NXT payment to you. + /// Your affiliate PUBLIC KEY, for volume tracking, affiliate payments, split-shifts, etc... + /// Information on pending exchange. + internal static async Task RequestAsync(double Amount, string Address, string Pair, string ReturnAddress = "", string RippleTag = "", string NXTRsAddress = "", string APIKey = "") + { + Uri uri = GetUri(); + string data = CreateData(Amount, Address, Pair, ReturnAddress, RippleTag, NXTRsAddress, APIKey); + string response = await RestServices.GetPostResponseAsync(uri, data).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri() => + new Uri(@"https://shapeshift.io/sendamount"); + + private static string CreateData(double Amount, string Address, string Pair, string ReturnAddress, string RippleTag, string NXTRsAddress, string APIKey) + { + string dataFormat = "\"{0}\":\"{1}\""; + string data = "{"; + data += string.Format(dataFormat, "amount", Amount.ToString()); + data += ", " + string.Format(dataFormat, "withdrawal", Address); + data += ", " + string.Format(dataFormat, "pair", Pair); + if (!string.IsNullOrEmpty(ReturnAddress)) + data += ", " + string.Format(dataFormat, "returnAddress", ReturnAddress); + if (!string.IsNullOrEmpty(RippleTag)) + data += ", " + string.Format(dataFormat, "destTag", RippleTag); + if (!string.IsNullOrEmpty(NXTRsAddress)) + data += ", " + string.Format(dataFormat, "rsAddress", NXTRsAddress); + if (!string.IsNullOrEmpty(APIKey)) + data += ", " + string.Format(dataFormat, "apiKey", APIKey); + data += "}"; + return data; + } + + private static async Task ParseResponseAsync(string response) + { + SendAmountRequest request = new SendAmountRequest(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "pair") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Pair = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdrawal") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.WithdrawalAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdrawalAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.WithdrawalAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "deposit") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.DepositAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "depositAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.DepositAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "expiration") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Expiration = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "quotedRate") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.QuotedRate = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "apiPubKey") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.APIKey = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + request.Error = jtr.Value.ToString(); + } + else continue; + } + } + return request; + } + } +} diff --git a/src/ShapeShift/ShiftResult.cs b/src/ShapeShift/ShiftResult.cs new file mode 100644 index 0000000..9fa2851 --- /dev/null +++ b/src/ShapeShift/ShiftResult.cs @@ -0,0 +1,166 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to send exchange requests to ShapeShift. + /// + public class ShiftResult + { + /*url: shapeshift.io/shift + method: POST + data type: JSON + data required: + withdrawal = the address for resulting coin to be sent to + pair = what coins are being exchanged in the form [input coin]_[output coin] ie btc_ltc + returnAddress = (Optional) address to return deposit to if anything goes wrong with exchange + destTag = (Optional) Destination tag that you want appended to a Ripple payment to you + rsAddress = (Optional) For new NXT accounts to be funded, you supply this on NXT payment to you + apiKey = (Optional) Your affiliate PUBLIC KEY, for volume tracking, affiliate payments, split-shifts, etc... + + example data: {"withdrawal":"AAAAAAAAAAAAA", "pair":"btc_ltc", returnAddress:"BBBBBBBBBBB"} + + Success Output: + { + deposit: [Deposit Address (or memo field if input coin is BTS / BITUSD)], + depositType: [Deposit Type (input coin symbol)], + withdrawal: [Withdrawal Address], //-- will match address submitted in post + withdrawalType: [Withdrawal Type (output coin symbol)], + public: [NXT RS-Address pubkey (if input coin is NXT)], + xrpDestTag : [xrpDestTag (if input coin is XRP)], + apiPubKey: [public API attached to this shift, if one was given] + } + */ + + /// + /// Address to send coins to exchange. + /// + public string DepositAddress { get; private set; } + /// + /// Currency to exchange from. + /// + public string DepositCoin { get; private set; } + /// + /// Address to send exchanged coins to. + /// + public string WithdrawalAddress { get; private set; } + /// + /// Currency to exchange to. + /// + public string WithdrawalCoin { get; private set; } + /// + /// Destination tag to be appended to Ripple payment. + /// + public string RippleTag { get; private set; } + /// + /// NXT RS-Address public key. + /// + public string NXTRsAddress { get; private set; } + /// + /// Public API key attached to this exchange, if any. + /// + public string APIKey { get; private set; } + /// + /// Error thrown by exchange request, if any. + /// + public string Error { get; private set; } + + private ShiftResult() { } + + /// + /// Sends Shift request. + /// + /// Address for resulting coins to be sent to. + /// Currency pair for exchange. + /// Address to return coins to if exchange fails. + /// Destination tag that you want appended to a Ripple payment to you. + /// For new NXT accounts to be funded, you supply this on NXT payment to you. + /// Your affiliate PUBLIC KEY, for volume tracking, affiliate payments, split-shifts, etc... + /// Result of Shift request. + internal static async Task ShiftAsync(string Withdrawal, string Pair, string Return = "", string RippleTag = "", string NXTRsAddress = "", string APIKey = "") + { + Uri uri = GetUri(); + string data = CreateData(Withdrawal, Pair, Return, RippleTag, NXTRsAddress, APIKey); + string response = await RestServices.GetPostResponseAsync(uri, data).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri() => + new Uri(@"https://shapeshift.io/shift"); + + private static string CreateData(string Withdrawal, string Pair, string Return, string RippleTag, string NXTRsAddress, string APIKey) + { + string dataFormat = "\"{0}\":\"{1}\""; + string data = "{"; + data += string.Format(dataFormat, "withdrawal", Withdrawal); + data += ", " + string.Format(dataFormat, "pair", Pair); + if (!string.IsNullOrEmpty(Return)) + data += ", " + string.Format(dataFormat, "returnAddress", Return); + if (!string.IsNullOrEmpty(RippleTag)) + data += ", " + string.Format(dataFormat, "destTag", RippleTag); + if (!string.IsNullOrEmpty(NXTRsAddress)) + data += ", " + string.Format(dataFormat, "rsAddress", NXTRsAddress); + if (!string.IsNullOrEmpty(APIKey)) + data += ", " + string.Format(dataFormat, "apiKey", APIKey); + data += "}"; + return data; + } + + private static async Task ParseResponseAsync(string response) + { + ShiftResult result = new ShiftResult(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "deposit") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.DepositAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "depositType") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.DepositCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdrawal") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.WithdrawalAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdrawalType") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.WithdrawalCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "public") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.NXTRsAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "xrpDestTag") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.RippleTag = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "apiPubKey") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.APIKey = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + result.Error = jtr.Value.ToString(); + } + else continue; + } + } + return result; + } + } +} diff --git a/src/ShapeShift/SupportedCoin.cs b/src/ShapeShift/SupportedCoin.cs new file mode 100644 index 0000000..e22f833 --- /dev/null +++ b/src/ShapeShift/SupportedCoin.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Exchange availability status of coin. + /// + public enum CoinStatuses { Available, Unavailable } + + /// + /// Provides information on specific currencies. + /// + public class SupportedCoin + { + /*url: shapeshift.io/getcoins + method: GET + + Success Output: + + { + "SYMBOL1" : + { + name: ["Currency Formal Name"], + symbol: <"SYMBOL1">, + image: ["https://shapeshift.io/images/coins/coinName.png"], + status: [available / unavailable] + } + (one listing per supported currency) + } + + The status can be either "available" or "unavailable". Sometimes coins become temporarily unavailable during updates or + unexpected service issues. + */ + + /// + /// Name of currency. + /// + public string Name { get; private set; } + /// + /// Ticker symbol for currency. + /// + public string Symbol { get; private set; } + /// + /// Link to currency icon. + /// + public string ImageLink { get; private set; } + /// + /// Currency exchange availability. + /// + public CoinStatuses Status { get; private set; } + + private SupportedCoin() { } + + /// + /// Provides information on all currencies supported by ShapeShift. + /// + /// List of all supported currencies. + internal static async Task> GetCoinsAsync() + { + Uri uri = GetUri(); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + /// + /// Provides information on a specific currency supported by ShapeShift. + /// + /// Ticker symbol of currency. + /// Information on specific supported currency. + internal static async Task GetCoinAsync(string Symbol) => + (await GetCoinsAsync().ConfigureAwait(false)).First(c => c.Symbol == Symbol); + + private static Uri GetUri() => new Uri(@"https://shapeshift.io/getcoins"); + + private static async Task> ParseResponseAsync(string response) + { + List CoinList = new List(); + SupportedCoin ToAdd = new SupportedCoin(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.TokenType.ToString() == "StartObject") + { + if (!string.IsNullOrEmpty(ToAdd.Name)) + CoinList.Add(ToAdd); + ToAdd = new SupportedCoin(); + continue; + } + if (jtr.Value != null) + { + if (jtr.Value.ToString() == "name") + { + await jtr.ReadAsync().ConfigureAwait(false); + ToAdd.Name = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "symbol") + { + await jtr.ReadAsync().ConfigureAwait(false); + ToAdd.Symbol = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "image") + { + await jtr.ReadAsync().ConfigureAwait(false); + ToAdd.ImageLink = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "status") + { + await jtr.ReadAsync().ConfigureAwait(false); + ToAdd.Status = jtr.Value.ToString() == "available" ? CoinStatuses.Available : CoinStatuses.Unavailable; + } + else continue; + } + } + } + if (!string.IsNullOrEmpty(ToAdd.Name)) + CoinList.Add(ToAdd); + + return CoinList; + } + } +} diff --git a/src/ShapeShift/TimeRemaining.cs b/src/ShapeShift/TimeRemaining.cs new file mode 100644 index 0000000..1ccfcfe --- /dev/null +++ b/src/ShapeShift/TimeRemaining.cs @@ -0,0 +1,84 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Statuses available for Time Remaining requests. + /// + public enum TimeStatuses { Pending, Expired } + + /// + /// Provides access to information on time remaining for pending deposits. + /// + public class TimeRemaining + { + /*url: shapeshift.io/timeremaining/[address] + method: GET + + [address] is the deposit address to look up. + + Success Output: + + { + status:"pending", + seconds_remaining: 600 + } + + The status can be either "pending" or "expired". + If the status is expired then seconds_remaining will show 0. + */ + + /// + /// Status of deposit. + /// + public TimeStatuses Status { get; private set; } + /// + /// Seconds remaining before transaction expires. + /// + public double SecondsRemaining { get; private set; } + + private TimeRemaining() { } + + /// + /// Gets status of deposit and time remaining to complete deposit before expiration. + /// + /// The deposit address to look up. + /// Time remaining for deposit. + internal static async Task GetTimeRemainingAsync(string Address) + { + Uri uri = GetUri(Address); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri(string Address) => + new Uri(string.Format(@"https://shapeshift.io/timeremaining/{0}", Address)); + + private static async Task ParseResponseAsync(string response) + { + TimeRemaining tr = new TimeRemaining(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "status") + { + await jtr.ReadAsync().ConfigureAwait(false); + tr.Status = jtr.Value.ToString() == "pending" ? TimeStatuses.Pending : TimeStatuses.Expired; + } + else if (jtr.Value.ToString() == "seconds_remaining") + { + await jtr.ReadAsync().ConfigureAwait(false); + tr.SecondsRemaining = Convert.ToDouble(jtr.Value.ToString()); + } + else continue; + } + } + return tr; + } + } +} diff --git a/src/ShapeShift/TradingLimit.cs b/src/ShapeShift/TradingLimit.cs new file mode 100644 index 0000000..3ca0e50 --- /dev/null +++ b/src/ShapeShift/TradingLimit.cs @@ -0,0 +1,103 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides access to trade limits for specific coin pairs. + /// + public class TradingLimit + { + /*url: shapeshift.io/limit/[pair] + method: GET + + [pair] is any valid coin pair such as btc_ltc or ltc_btc + + Success Output: + { + "pair" : "btc_ltc", + "limit" : "1.2345" + } + */ + + /// + /// Currency pair. + /// + public string Pair { get; private set; } + /// + /// Trade limit. + /// + public double Limit { get; private set; } + + private TradingLimit() { } + + /// + /// Gets trade limit for specified currency pair. + /// + /// Currency pair to exchange. + /// Trading limit information. + internal static async Task GetLimitAsync(string Pair) + { + Uri uri = GetUri(Pair); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + /// + /// Gets trade limit for specified currency pair. + /// + /// Ticker for currency to exchange from. + /// Ticker for currency to exchange to. + /// Trading limit information. + internal static async Task GetLimitAsync(string Ticker1, string Ticker2) => + await GetLimitAsync(string.Format("{0}_{1}", Ticker1, Ticker2)).ConfigureAwait(false); + + /// + /// Gets list of all trade limits. + /// + /// List of all trade limits. + internal static async Task> GetAllLimitsAsync() + { + List LimitList = new List(); + List PairList = await TradingPair.GetAllPairsAsync().ConfigureAwait(false); + foreach (TradingPair tp in PairList) + { + TradingLimit NewLimit = await GetLimitAsync(tp.Pair).ConfigureAwait(false); + LimitList.Add(NewLimit); + } + return LimitList; + } + + private static Uri GetUri(string Pair) => + new Uri(string.Format(@"https://shapeshift.io/limit/{0}", Pair)); + + private static async Task ParseResponseAsync(string response) + { + TradingLimit limit = new TradingLimit(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value != null) + { + if (jtr.Value.ToString() == "pair") + { + await jtr.ReadAsync().ConfigureAwait(false); + limit.Pair = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "limit") + { + await jtr.ReadAsync().ConfigureAwait(false); + limit.Limit = Convert.ToDouble(jtr.Value.ToString()); + } + else continue; + } + } + } + return limit; + } + } +} diff --git a/src/ShapeShift/TradingMarketInfo.cs b/src/ShapeShift/TradingMarketInfo.cs new file mode 100644 index 0000000..097899c --- /dev/null +++ b/src/ShapeShift/TradingMarketInfo.cs @@ -0,0 +1,135 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides access to exchange market info for currency pairs. + /// + public class TradingMarketInfo + { + /*url: shapeshift.io/marketinfo/[pair] + method: GET + + [pair] (OPTIONAL) is any valid coin pair such as btc_ltc or ltc_btc. + The pair is not required and if not specified will return an array of all market infos. + + Success Output: + { + "pair" : "btc_ltc", + "rate" : 130.12345678, + "limit" : 1.2345, + "min" : 0.02621232, + "minerFee" : 0.0001 + } + */ + + /// + /// Currency pair. + /// + public string Pair { get; private set; } + /// + /// Exchange rate. + /// + public double Rate { get; private set; } + /// + /// Exchange limit. + /// + public double Limit { get; private set; } + /// + /// Minimum exchange amount. + /// + public double Min { get; private set; } + /// + /// Fee to be sent to miners for exchange. + /// + public double MinerFee { get; private set; } + + private TradingMarketInfo() { } + + /// + /// Gets market info for specific currency pair. + /// + /// Pair to get information for. + /// Market Information. + internal static async Task GetMarketInfoAsync(string Pair) + { + Uri uri = GetUri(Pair); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + /// + /// Gets market info for specific currency pair. + /// + /// Ticker for currency to exchange from. + /// Ticker for currency to exchange to. + /// Market Information. + internal static async Task GetMarketInfoAsync(string Ticker1, string Ticker2) => + await GetMarketInfoAsync(string.Format("{0}_{1}", Ticker1, Ticker2)).ConfigureAwait(false); + + /// + /// Gets market info for all currency pairs. + /// + /// List of Market Information. + internal static async Task> GetAllMarketsAsync() + { + List MarketList = new List(); + List PairList = await TradingPair.GetAllPairsAsync().ConfigureAwait(false); + foreach (TradingPair tp in PairList) + { + TradingMarketInfo NewMarket = await GetMarketInfoAsync(tp.Pair).ConfigureAwait(false); + MarketList.Add(NewMarket); + } + return MarketList; + } + + private static Uri GetUri(string Pair) => + new Uri(string.Format(@"https://shapeshift.io/marketinfo/{0}", Pair)); + + private static async Task ParseResponseAsync(string response) + { + TradingMarketInfo MarketInfo = new TradingMarketInfo(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value != null) + { + if (jtr.Value.ToString() == "pair") + { + await jtr.ReadAsync().ConfigureAwait(false); + MarketInfo.Pair = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "rate") + { + await jtr.ReadAsync().ConfigureAwait(false); + MarketInfo.Rate = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "limit") + { + await jtr.ReadAsync().ConfigureAwait(false); + MarketInfo.Limit = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "min") + { + await jtr.ReadAsync().ConfigureAwait(false); + MarketInfo.Min = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "minerFee") + { + await jtr.ReadAsync().ConfigureAwait(false); + MarketInfo.MinerFee = Convert.ToDouble(jtr.Value.ToString()); + } + else continue; + } + else continue; + } + } + return MarketInfo; + } + } +} diff --git a/src/ShapeShift/TradingPair.cs b/src/ShapeShift/TradingPair.cs new file mode 100644 index 0000000..61a48ea --- /dev/null +++ b/src/ShapeShift/TradingPair.cs @@ -0,0 +1,65 @@ +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides access to verified currency pairs available for exchange. + /// + public class TradingPair + { + /// + /// The Pair string used by ShapeShift. + /// + public string Pair { get; private set; } + /// + /// The Ticker of the currency to be converted. + /// + public string Ticker1 { get; private set; } + /// + /// The Ticker of the currency to convert to. + /// + public string Ticker2 { get; private set; } + + private TradingPair() { } + + /// + /// Generates a list of all TradingPairs supported by ShapeShift. + /// + /// A List of TradingPairs. + internal static async Task> GetAllPairsAsync() + { + //Initialize an empty List of TradingPairs + List PairList = new List(); + //Generate a List of all SupportedCoins + List CoinList = await SupportedCoin.GetCoinsAsync().ConfigureAwait(false); + //Loop through all SupportedCoins + foreach (SupportedCoin Coin1 in CoinList) + { + //Check if coin is available for Shifting + if (Coin1.Status == CoinStatuses.Available) + { + //Loop through all SupportedCoins again for generating all TradingPairs + foreach (SupportedCoin Coin2 in CoinList) + { + //Check if both coins are not the same and both coins are available to Shift + if (Coin1 != Coin2 && Coin2.Status == CoinStatuses.Available) + { + //Create new TradingPair object and assign values + TradingPair NewPair = new TradingPair(); + NewPair.Ticker1 = Coin1.Symbol; + NewPair.Ticker2 = Coin2.Symbol; + NewPair.Pair = string.Format("{0}_{1}", Coin1.Symbol, Coin2.Symbol); + //Add new TradingPair object to List of TradingPairs + PairList.Add(NewPair); + } + else continue; + } + } + else continue; + } + //Return complete TradingPair List + return PairList; + } + } +} diff --git a/src/ShapeShift/TradingRate.cs b/src/ShapeShift/TradingRate.cs new file mode 100644 index 0000000..60f7001 --- /dev/null +++ b/src/ShapeShift/TradingRate.cs @@ -0,0 +1,104 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to find exchange rates for coin pairs. + /// + public class TradingRate + { + /*url: shapeshift.io/rate/[pair] + method: GET + + [pair] is any valid coin pair such as btc_ltc or ltc_btc + + Success Output: + + { + "pair" : "btc_ltc", + "rate" : "70.1234" + } + */ + + /// + /// Coin pair. + /// + public string Pair { get; private set; } + /// + /// Exchange rate. + /// + public double Rate { get; private set; } + + private TradingRate() { } + + /// + /// Finds exchange rate for specified coin pair. + /// + /// Coin pair to find rate for. + /// Exchange rate. + internal static async Task GetRateAsync(string Pair) + { + Uri uri = GetUri(Pair); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + /// + /// Finds exchange rate for specified coin pair. + /// + /// Ticker symbol for coin to exchange. + /// Ticker symbol for resulting coin. + /// Exchange rate. + internal static async Task GetRateAsync(string Ticker1, string Ticker2) => + await GetRateAsync(string.Format("{0}_{1}", Ticker1, Ticker2)).ConfigureAwait(false); + + /// + /// Finds exchange rates for all valid coin pairs. + /// + /// List of exchange rates. + internal static async Task> GetAllRatesAsync() + { + List RateList = new List(); + List PairList = await TradingPair.GetAllPairsAsync().ConfigureAwait(false); + foreach (TradingPair tp in PairList) + { + TradingRate NewRate = await GetRateAsync(tp.Pair).ConfigureAwait(false); + RateList.Add(NewRate); + } + return RateList; + } + + private static Uri GetUri(string Pair) => + new Uri(string.Format(@"https://shapeshift.io/rate/{0}", Pair)); + + private static async Task ParseResponseAsync(string response) + { + TradingRate rate = new TradingRate(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value != null) + { + if (jtr.Value.ToString() == "pair") + { + await jtr.ReadAsync().ConfigureAwait(false); + rate.Pair = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "rate") + { + await jtr.ReadAsync().ConfigureAwait(false); + rate.Rate = Convert.ToDouble(jtr.Value.ToString()); + } + else continue; + } + } + } + return rate; + } + } +} diff --git a/src/ShapeShift/Tx.cs b/src/ShapeShift/Tx.cs new file mode 100644 index 0000000..5d69e33 --- /dev/null +++ b/src/ShapeShift/Tx.cs @@ -0,0 +1,217 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to view previous transactions by address or API key. + /// + public class Tx + { + /// + /// Transaction ID of the input coin going into shapeshift. + /// + public string InputTxID { get; private set; } + /// + /// Address that the input coin was paid to for this shift. + /// + public string InputAddress { get; private set; } + /// + /// Currency type of the input coin. + /// + public string InputCoin { get; private set; } + /// + /// Amount of input coin that was paid in on this shift. + /// + public double InputAmount { get; private set; } + /// + /// Transaction ID of the output coin going out to user. + /// + public string OutputTxID { get; private set; } + /// + /// Address that the output coin was sent to for this shift. + /// + public string OutputAddress { get; private set; } + /// + /// Currency type of the output coin. + /// + public string OutputCoin { get; private set; } + /// + /// Amount of output coin that was paid out on this shift. + /// + public double OutputAmount { get; private set; } + /// + /// The effective rate the user got on this shift. + /// + public double ShiftRate { get; private set; } + /// + /// Status of the shift. + /// + public TxStatuses Status { get; private set; } + + private Tx() { } + + /*url: shapeshift.io/txbyapikey/[apiKey] + method: GET + + [apiKey] is the affiliate's PRIVATE api key. + + [ + { + inputTXID: [Transaction ID of the input coin going into shapeshift], + inputAddress: [Address that the input coin was paid to for this shift], + inputCurrency: [Currency type of the input coin], + inputAmount: [Amount of input coin that was paid in on this shift], + outputTXID: [Transaction ID of the output coin going out to user], + outputAddress: [Address that the output coin was sent to for this shift], + outputCurrency: [Currency type of the output coin], + outputAmount: [Amount of output coin that was paid out on this shift], + shiftRate: [The effective rate the user got on this shift.], + status: [status of the shift] + } + (one listing per transaction returned) + ] + + The status can be "received", "complete", "returned", "failed". + */ + + /// + /// Finds all transactions sent using the specified API key. + /// + /// The affiliate's PRIVATE api key. + /// List of transactions. + internal static async Task> GetTransactionsByAPIKeyAsync(string APIKey) + { + Uri uri = GetKeyUri(APIKey); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetKeyUri(string APIKey) => + new Uri(string.Format(@"https://shapeshift.io/txbyapikey/{0}", APIKey)); + + /*url: shapeshift.io/txbyaddress/[address]/[apiKey] + method: GET + + [address] the address that output coin was sent to for the shift + [apiKey] is the affiliate's PRIVATE api key. + + Success Output: + + [ + { + inputTXID: [Transaction ID of the input coin going into shapeshift], + inputAddress: [Address that the input coin was paid to for this shift], + inputCurrency: [Currency type of the input coin], + inputAmount: [Amount of input coin that was paid in on this shift], + outputTXID: [Transaction ID of the output coin going out to user], + outputAddress: [Address that the output coin was sent to for this shift], + outputCurrency: [Currency type of the output coin], + outputAmount: [Amount of output coin that was paid out on this shift], + shiftRate: [The effective rate the user got on this shift.], + status: [status of the shift] + } + (one listing per transaction returned) + ] + + The status can be "received", "complete", "returned", "failed". + */ + + /// + /// Finds all transactions sent to specified address. + /// + /// The address that output coin was sent to for the shift. + /// The affiliate's PRIVATE api key. + /// List of transactions. + internal static async Task> GetTransactionsByAddressAsync(string Address, string APIKey) + { + Uri uri = GetAddressUri(Address, APIKey); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetAddressUri(string Address, string APIKey) => + new Uri(string.Format(@"https://shapeshift.io/txbyaddress/{0}/{1}", Address, APIKey)); + + private static async Task> ParseResponseAsync(string response) + { + List TxList = new List(); + Tx NewTx = new Tx(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.TokenType.ToString() == "StartObject") + { + if (!string.IsNullOrEmpty(NewTx.InputTxID)) + TxList.Add(NewTx); + NewTx = new Tx(); + } + else if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "inputTXID") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.InputTxID = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "inputAddress") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.InputAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "inputCurrency") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.InputCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "inputAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.InputAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "outputTXID") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.OutputTxID = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "outputAddress") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.OutputAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "outputCurrency") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.OutputCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "outputAmount") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.OutputAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "shiftRate") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.ShiftRate = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "status") + { + await jtr.ReadAsync().ConfigureAwait(false); + NewTx.Status = + jtr.Value.ToString() == "received" ? TxStatuses.Received : + jtr.Value.ToString() == "complete" ? TxStatuses.Complete : + jtr.Value.ToString() == "returned" ? TxStatuses.Returned : + jtr.Value.ToString() == "failed" ? TxStatuses.Failed : TxStatuses.NoDeposits; + } + else continue; + } + } + if (!string.IsNullOrEmpty(NewTx.InputTxID)) + TxList.Add(NewTx); + + return TxList; + } + } +} diff --git a/src/ShapeShift/TxStatus.cs b/src/ShapeShift/TxStatus.cs new file mode 100644 index 0000000..03d35fd --- /dev/null +++ b/src/ShapeShift/TxStatus.cs @@ -0,0 +1,175 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Transaction Statuses. + /// + public enum TxStatuses { NoDeposits, Received, Returned, Complete, Failed } + + /// + /// Provides access to transaction status. + /// + public class TxStatus + { + /*url: shapeshift.io/txStat/[address] + method: GET + + [address] is the deposit address to look up. + + Success Output: (various depending on status) + + Status: No Deposits Received + { + status:"no_deposits", + address:[address] //matches address submitted + } + + Status: Received (we see a new deposit but have not finished processing it) + { + status:"received", + address:[address] //matches address submitted + } + + Status: Complete + { + status : "complete", + address: [address], + withdraw: [withdrawal address], + incomingCoin: [amount deposited], + incomingType: [coin type of deposit], + outgoingCoin: [amount sent to withdrawal address], + outgoingType: [coin type of withdrawal], + transaction: [transaction id of coin sent to withdrawal address] + } + + Status: Failed + { + status : "failed", + error: [Text describing failure] + } + + //Note: this can still get the normal style error returned. For example if request is made without an address. + */ + + /// + /// Status of exchange. + /// + public TxStatuses Status { get; private set; } + /// + /// Deposit address coins (need to be) sent to. + /// + public string Address { get; private set; } + /// + /// Address to send coins after exchange. + /// + public string WithdrawalAddress { get; private set; } + /// + /// Amount to send to deposit address. + /// + public double IncomingAmount { get; private set; } + /// + /// Currency type to send. + /// + public string IncomingCoin { get; private set; } + /// + /// Amount (to be) received from exchange. + /// + public double OutgoingAmount { get; private set; } + /// + /// Currency type to receive. + /// + public string OutgoingCoin { get; private set; } + /// + /// Transaction ID of coin sent to withdrawal address. + /// + public string TxID { get; private set; } + /// + /// Error thrown by status check, if any. + /// + public string Error { get; private set; } + + private TxStatus() { } + + /// + /// Gets status of transaction (to be) deposited to supplied address. + /// + /// Deposit address. + /// Transaction status. + internal static async Task GetStatusAsync(string Address) + { + Uri uri = GetUri(Address); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri(string address) => + new Uri(string.Format(@"https://shapeshift.io/txStat/{0}", address)); + + private static async Task ParseResponseAsync(string response) + { + TxStatus status = new TxStatus(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "status") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.Status = + jtr.Value.ToString() == "no_deposits" ? TxStatuses.NoDeposits : + jtr.Value.ToString() == "received" ? TxStatuses.Received : + jtr.Value.ToString() == "complete" ? TxStatuses.Complete : + jtr.Value.ToString() == "failed" ? TxStatuses.Failed : TxStatuses.Returned; + } + else if (jtr.Value.ToString() == "address") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.Address = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "withdraw") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.WithdrawalAddress = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "incomingCoin") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.IncomingAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "incomingType") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.IncomingCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "outgoingCoin") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.OutgoingAmount = Convert.ToDouble(jtr.Value.ToString()); + } + else if (jtr.Value.ToString() == "outgoingType") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.OutgoingCoin = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "transaction") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.TxID = jtr.Value.ToString(); + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + status.Error = jtr.Value.ToString(); + } + else continue; + } + } + return status; + } + } +} diff --git a/src/ShapeShift/ValidateAddress.cs b/src/ShapeShift/ValidateAddress.cs new file mode 100644 index 0000000..4a3f5f3 --- /dev/null +++ b/src/ShapeShift/ValidateAddress.cs @@ -0,0 +1,82 @@ +using System; +using System.IO; +using System.Threading.Tasks; +using Newtonsoft.Json; + +namespace Kalakoi.Crypto.ShapeShift +{ + /// + /// Provides ability to validate addresses for supported coins. + /// + public class ValidateAddress + { + /*url: shapeshift.io/validateAddress/[address]/[coinSymbol] + method: GET + + [address] the address that the user wishes to validate + [coinSymbol] the currency symbol of the coin + + Success Output: + + + { + isValid: [true / false], + error: [(if isvalid is false, there will be an error message)] + } + + + isValid will either be true or false. If isvalid returns false, an error parameter will be present and will contain a descriptive error message. + */ + + /// + /// True if address is valid. + /// + public bool IsValid { get; private set; } + /// + /// Error thrown by validation check. + /// + public string Error { get; private set; } + + private ValidateAddress() { } + + /// + /// Validates address belongs to specified currency. + /// + /// Address to check. + /// Ticker symbol for currency to check. + /// Validation results. + internal static async Task ValidateAsync(string Address, string Symbol) + { + Uri uri = GetUri(Address, Symbol); + string response = await RestServices.GetResponseAsync(uri).ConfigureAwait(false); + return await ParseResponseAsync(response).ConfigureAwait(false); + } + + private static Uri GetUri(string Address, string Symbol) => + new Uri(string.Format(@"https://shapeshift.io/validateaddress/{0}/{1}", Address, Symbol)); + + private static async Task ParseResponseAsync(string response) + { + ValidateAddress va = new ValidateAddress(); + using (JsonTextReader jtr = new JsonTextReader(new StringReader(response))) + { + while (await jtr.ReadAsync().ConfigureAwait(false)) + { + if (jtr.Value == null) continue; + else if (jtr.Value.ToString() == "isValid") + { + await jtr.ReadAsync().ConfigureAwait(false); + va.IsValid = jtr.Value.ToString() == "true"; + } + else if (jtr.Value.ToString() == "error") + { + await jtr.ReadAsync().ConfigureAwait(false); + va.Error = jtr.Value.ToString(); + } + else continue; + } + } + return va; + } + } +}