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;
+ }
+ }
+}