Advanced BCH Monitoring with Tasker (Part 4: Retrieving your data from the block explorer API)

0 287
Avatar for elrikpiro
4 years ago

Previous part 3:
Recovering price quotations from an exchange data API

The IBlockExplorer API

This interface will allow us to query information about anything related to the addresses we manage.

It needs three variables to keep its state, the last JSON retrieved, the balance in satoshis and a list of funded addresses, the last two are meant to cache results so we don't need to recalculate them once we already have it.

Its functions are as follows:

  • load() : gets a list of addresses and an API key (if needed) as a parameter and will retrieve information of those addresses from the REST API and save it in the lastJson variable.

  • getTotalBalance() : after calling load this will return the total balance from the queried addresses.

  • getFundedAddresses() : a utility function that returns a list of the addresses that have UTXO, it will serve a purpose on later posts.

entry:
    Goto(Type="Action Label", Label="function_load", If(%par1 ~ "function_load");
    Goto(Type="Action Label", Label="function_getTotalBalance", If(%par1 ~ "function_getTotalBalance");
    Goto(Type="Action Label", Label="function_getFundedAddresses", If(%par1 ~ "function_getFundedAddresses");
    Return(Value="ERROR", Stop="On");
function_load:
    ArrayClear(VariableArray="%args");
    VariableSet(Name="%args", To="%par2");
    VariableSplit(Name="%args", Splitter=";");
    VariableSet(Name="%input_addresses", To="%args1");
    VariableSet(Name="%input_apikey", To="%args2");
    VariableSet(Name="%lastJson", To=0);
    VariableClear(Name="%balanceSat");
    VariableClear(Name="%fundedAddresses");
    Return(Value="SUCCESS", Stop="On");
function_getTotalBalance:
    Return(Value="%balanceSat", Stop="On");
function_getFundedAddresses:
    Return(Value="%fundedAddresses", Stop="On");

What the fuck is happening in function_load?

As the Perform Task action only gets up to two parameters we will separate different parameters with the semicolon character ';' and use the %args variable to store the %par2 content and split it in an array where each element will have one parameter. This pretty much sums up the first five actions, that get the %par2 parameter and turns it into two different local variables: %input_addresses and %input_apikey.

Implementing the interface, the BlockChair case

Blockchair is one of the most used block explorers and has a very professional and convenient REST API, that makes retrieving information from the Blockchain really easy.

The steps used to retrieve information using the Blockchair API may be different on other Block Explorer API, but the idea behind it remains the same: Querying the sum for each UTXO value for each address under monitoring.

The first step will be cloning the IBlockExplorerAPI task into a new BlockChairBlockExplorerAPI task.

Retrieving your address information

As many BCH users use to have more than 20 addresses on their wallets thanks to the BIP39 address derivation, retrieving the information could be a very cumbersome task even programmatically, because of that pre-loading the JSON data once and treating it later is the most efficient strategy to follow.

entry:
    ( . . . )
function_load:
    ArrayClear(VariableArray="%args");
    VariableSet(Name="%args", To="%par2");
    VariableSplit(Name="%args", Splitter=";");
    VariableSet(Name="%input_addresses", To="%args1");
    VariableSet(Name="%input_apikey", To="%args2");

    HttpRequest(Method="GET", URL="https://api.blockchair.com/bitcoin-cash/dashboards/addresses/%input_addresses?key=%input_apikey");
    Return(Value="ERROR : HTTP %http_response_code", Stop="On", If="%http_response_code > 299");
    VariableSet(Name="%BlockChair_lastJson", To="%http_data");

    VariableClear(Name="%BlockChair_balanceSat");
    VariableClear(Name="%BlockChair_fundedAddresses");
    Return(Value="SUCCESS", Stop="On");
function_getTotalBalance:
    ( . . . )
function_getFundedAddresses:
    ( . . . )

Step by step:

  • An HTTP Request to the https://api.blockchair.com/bitcoin-cash/dashboards/addresses/ endpoint, to query a list of comma-separated CashAddr addresses %input_addresses using an (optional if you are just doing your own experiments) API key ?key=%input_apikey

  • Like in the last chapter, a conditional return if the HTTP response is an error

  • The Request data assignment to the JSON variable

Getting your account's balance

As soon as we have the JSON loaded we can start getting information from it, in this case, we are going to query and cache the balance in satoshi from the JSON.

entry:
    ( . . . )
function_load:
    ( . . . )
function_getTotalBalance:
    If( %BlockChair_balanceSat !Set)
        JavaScriptlet(Code={
            var balance = JSON.parse(global('BlockChair_lastJson')).data.set.balance;
            setLocal('%BlockChair_balanceSat', balance);
        });
    EndIf
    Return(Value="%BlockChair_balanceSat", Stop="On");
function_getFundedAddresses:
    ( . . . )

Step by step:

  • If the %BlockChair_balanceSat isn't set...

    • We run a JavascriptLet action that will retrieve the total balance from all addresses and set it into the variable.

  • Finally, we return the variable value.

Note that every time we load a new JSON, we unset the variable, so after loading a JSON the variable will need to be recalculated calling this function again.

Listing all addresses with UTXO

The Blockchair API, and in general, any other block explorer APIs have a consumption rate that can lead your IP to be blacklisted, temporarily blocked or if you have a paid subscription, charged for requests.

Because of that and because some of the APIs need to call each address individually you might want to know which addresses are funded.

entry:
    ( . . . )
function_load:
    ( . . . )
function_getTotalBalance:
    ( . . . )
function_getFundedAddresses:
    If( %BlockChair_fundedAddresses !Set)
        JavaScriptlet(Code={
            var utxo = JSON.parse(global('BlockChair_lastJson')).data.utxo;
            var addressList = "";
            for(var i = 0 ; i < utxo.length; i++) {
                if(addressList.localeCompare("") == 0) {
                    addressList = addressList.concat(",");
                }
                addressList = addressList.concat(utxo[i].address);
            }
            setGlobal('%BlockChair_fundedAddresses', addressList);
        });
        VariableSplit(Name="%BlockChair_fundedAddresses", Splitter=",");
        ArrayProcess(VariableArray="%BlockChair_fundedAddresses", Type="Remove Duplicates");
        VariableJoin(Name="%BlockChair_fundedAddresses", Joiner=",");
    EndIf
    Return(Value="%BlockChair_fundedAddresses", Stop="On");

Step by step:

  • If %BlockChair_fundedAddresses isn't set

    • Run JavaScriptlet action that generates a list of addresses contained in every UTXO at the JSON and separate it by a comma.

    • As one address may contain more than one UTXO some duplicated may be found, so the variable is split, processed to eliminate duplicates and joined back again

  • Finally, we return the comma-separated list of addresses with UTXO

Testing our implementation

Time to write some tests to check how is this working.

BlockChair_functionLoad_success:
    VariableClear(Name="%BlockChair_lastJson");
    PerformTask(Name="BlockChairBlockExplorerAPI", Parameter1="function_load", Parameter2="%ALL_ADDRESSES;%APIKEY", ReturnVariable="%retval");
    Return(Value="ERROR : %retval", Stop="On", If="%retval !~ SUCCESS");
BlockChair_getBalance_success:
    PerformTask(Name="BlockChairBlockExplorerAPI", Parameter1="function_getTotalBalance", ReturnVariable="%retval");
    Return(Value="ERROR : %retval", Stop="On", If="%retval eq 0 OR %retval !Set");
    Notify(Title="Balance in satoshi", Text="%retval");
BlockChair_getFundedAddresses_success:
    PerformTask(Name="BlockChairBlockExplorerAPI", Parameter1="function_getFundedAddresses", ReturnVariable="%retval");
    Return(Value="ERROR : %retval", Stop="On", If="%retval eq 0 OR %retval !Set");
    Notify(Title="List of funded addresses", Text="%retval");

The first test will load a JSON and check if returns SUCCESS, the second one will raise a push notification with your balance in satoshi, the third one will raise another notification with the list of funded addresses.

In the next part, we'll be preparing a notification builder object.

Sponsors of elrikpiro
empty
empty
empty

Special thanks to Blockchair.com for providing an API key.

Don't forget to subscribe, feedback will be appreciated!

Part 5: Notifying price quotes on your phone

2
$ 1.30
$ 1.00 from @btcfork
$ 0.20 from @Fidel
$ 0.10 from @sploit
Avatar for elrikpiro
4 years ago

Comments