I have been playing around with some crypto API to get the contents of any wallet for multiple coins. Thought I would share.
First up I use the following simple REST API to get wallet contents for various coins.
Chain.so for Bitcoin, Dash, Dogecoin and Litecoin. The request is:
https://chain.so/api/v2/get_address_balance/{Symbol}/{WalletAddress}
And the response:
{
"status":"success",
"data":
{
"network":"DASH",
"address":"some address",
"confirmed_balance":"0.0345600000",
"unconfirmed_balance":"0.00000000"
}
}
TokenBalance.com for many ERC20 tokens. The request is:
https://api.tokenbalance.com/token/{ContractAddress}/{WalletAddress}
And the response:
{
"name":"Basic Attention Token",
"wallet":"some address",
"symbol":"BAT",
"balance":"0.000000000000031747",
"eth_balance":"0.82619688",
"decimals":18,
"block":4518351
}
Eitherchain.org for Etherium. The request is:
https://etherchain.org/api/account/{WalletAddress}
And the response:
{
"status":1,
"data":
[{
"address":"some address",
"balance":826196880000000000,
"nonce":null,
"code":"0x",
"name":null,
"storage":null,
"firstSeen":"2017-08-05T10:55:28.000Z"
}]
}
Going through the code I have just realised a bit bigger that first thought. So I will start with some core parts and write up more later if people are interested.
So first up the code to get the contents of a wallet
public static class WalletRequester
{
public static async Task<WalletContents> CoinsInWallet(Wallet definition)
{
try
{
using (var client = new HttpClient())
{
var response = await client
.GetStringAsync(definition.RequestUrl)
.ConfigureAwait(false);
return definition.ResponseParser(response);
}
}
catch (Exception ex)
{
return definition.BuildExceptionWalletError(ex);
}
}
}
This function sends an asynchronous request to a REST API identified in the wallet definition RequestUrl. It then parses the response and returns the wallet contents as a task. Task, async and await is the C# system to handle aysnc results. If something goes wrong it catches the exception. then wraps it and then returns the WalletContents with the error.
Nice simple generic code so far. So here is all I need to define to add a new API to the system. Here is the Chain.so definition:
public class SoChainWallet : Wallet
{
public SoChainWallet(string symbol, string walletAddress)
: base(symbol, walletAddress) { }
public override string RequestUrl
=> $"https://chain.so/api/v2/get_address_balance/{Symbol}/{WalletAddress}";
public override WalletContents ResponseParser(string walletResponse)
{
var parser = JsonObject.Parse(walletResponse);
var status = parser["status"].GetString();
if (status != "success")
{
return BuildStandardWalletError();
}
return BuildWallet(parser["data"].GetObject()["confirmed_balance"].GetString());
}
}
A constructor, which is a simple pass through to the base. Something to build the request and finally a parser for the response. I make use of a free JSON parser library to make things simple.
The actual Wallet base object is just a few properties and a few constructors.
public abstract class Wallet
{
protected Wallet(string symbol, string walletAddress)
{
Symbol = symbol;
WalletAddress = walletAddress;
}
public string Symbol { get; }
public string WalletAddress { get; }
public abstract string RequestUrl { get; }
public abstract WalletContents ResponseParser(string walletResponse);
public WalletContents BuildWallet(string balanceString, decimal divider = 1)
=> decimal.TryParse(balanceString, out decimal coins)
? BuildWalletResult(coins / divider)
: BuildWalletError($"Invalid balance string ({balanceString})");
public WalletContents BuildWalletResult(decimal coins)
=> new WalletContents(Symbol, coins.ToRight<decimal, string>());
public WalletContents BuildWalletError(string error)
=> new WalletContents(Symbol, error.ToLeft<decimal, string>());
public WalletContents BuildStandardWalletError()
=> BuildWalletError($"Failed to read {Symbol} wallet {WalletAddress}");
public WalletContents BuildExceptionWalletError(Exception ex)
=> BuildWalletError($"Failed to read {Symbol} wallet {WalletAddress}: {ex.Message}");
}
The WalletContents object is a simple immutable object.
public class WalletContents
{
public WalletContents(string symbol, Either<decimal, string> coins)
{
Symbol = symbol;
Coins = coins;
}
public string Symbol { get; }
public Either<decimal, string> Coins { get; }
}
The only thing worth a note is the Either object. This will either hold the amount of coins or an error message. The reason for this is I should be able to fire a block of these at the same time as they are all async requests, so when I get an error I want to know what failed but I also want my other requests to finish, best way is bind it to the symbol and keep the context.
So to use:
var result = WalletRequester
.CoinsInWallet(Create(Symbols.Etherium, "SomeAddr"))
The means I used to ease wallet creation is for a further details piece if people are interested
Happy xmas
Woz