Creating my own personal claiming and staking software and run it on a Raspberry Pi

in #palnet5 years ago (edited)


Source

Since I have by now more than 15 accounts for different Steem Engine tokens, manually claiming, distributing and staking them has become a bit time consuming. Of course, @steemchiller has added a Claim All button on his popular site http://steemworld.org, but this works on a per account basis. I would still have to navigate to each individual account to claim the tokens I use that account for. And claiming is only part of the story. Some tokens I want to keep (stake), and some I want to sell. Some of the tokens are delegated from a main account (I have two accounts I consider as main account; @partitura is of course one, I'll leave it to you to guess the other) and some are staked in the auxiliary account. And recently some accounts stake part of the rewards right away, and others don't. There is scotauto to automate claiming over different accounts, but I have not yet come across a way to automatically distribute and stake tokens over multiple accounts. And probablty I am the only one who does it in the way I do it.

I am therefore in need of my own personal claiming and staking software.

Luckily, I have a spare Raspberry Pi lying around and a few days left before my holidays are over. And since I am basically a nerd, let's start tinkering.

I drew inspiration for my own software solution from a post by @contrabourdon about his quick and dirty way to claim and stake tokens across multiple accounts, and a post by @drakos about his way to claim all tokens with multiple accounts.

The scripts in these posts are written in Javascript. A nice way to run Javascript on a Raspberry Pi (and on other computers as well) is by means of NodeJS. If you don't know what NodeJS is: look it up. There are plenty of tutorials on the internet about how to install NodeJS on a Raspberry Pi.
Here's one: https://thisdavej.com/beginners-guide-to-installing-node-js-on-a-raspberry-pi/.
And here: https://www.instructables.com/id/Install-Nodejs-and-Npm-on-Raspberry-Pi/.
And here: https://www.w3schools.com/nodejs/nodejs_raspberrypi.asp

I use an old Raspberry, model B, revision 2.0 (some 6 years old) . It's processor is based on the ARMv6 architecture. That means that where most guides on the internet (including the three mentioned above) wil tell you to type:

sudo apt-get install -y nodejs

to install NodeJS, it won't actually work for a Raspberry B version 2. Recent builds of Raspbian expect at least a Raspberry with ARMv7 acrchitecture. For a Raspberry B version 2.0 (and earlier) you have to download the version of NodeJS from this link. Install instructions can befound on the github page of NodeJS.

After you have succefully installed NodeJS, as @contrabourdon writes in his post, you need to more libraries to write javascript code that can interact with Steem Engine. They are the steem-js library and the sscjs library. You can install them on your Raspberry by typing:

npm install steem --save
npm install sscjs

Once those libraries are installed you are good to go and create your own claiming and staking software.

For claiming tokens across all my accounts, I use the multiple accounts configuration (without granted posting authority) of the script @drakos published. It does it's job nicely and claims all rewards of my accounts. Now I need a way to list all claimed tokens over multiple accounts. Once I have that list I can transfer them (or part of them) to a mainaccount, or stake them, or delegate them. This is where the sscjs library comes into play. It provides methods to interact with the Steem Engine (or as they write on their github-page: with the JSON RPC server of a Steem Smart Contracts node). To have it available in a NodeJS program, you can use the code:

const SSC = require( 'sscjs' );   
const ssc = new SSC( 'https://api.steem-engine.com/rpc/' );

The library has a find-method to retreive records from the table of a contract. It is declared like this:

/**
   * retrieve records from the table of a contract
   * @param {String} contract: contract name
   * @param {String} table: table name
   * @param {JSON} query: query to perform on the table
   * @param {Integer} limit: limit the number of records to retrieve
   * @param {Integer} offset: offset applied to the records set
   * @param {Array<Object>}: indexes array of index definitions { index: string, descending: boolean }
   * @param {Function} callback: callback called if passed
   * @returns {Promise<JSON>} returns a promise if no callback passed
*/
find(contract, table, query, limit = 1000, offset = 0, indexes = [], callback = null) 

It takes some delving in the source code of Steem Smart Contracts to learn what contracts and tables are available. For what I want to do the contract "tokens" is needed and the table "balances". The "query"-parameter can be used to filter or query the table for data you're interested in. It seems enormously powerful but I need only one tiny ability: the ability to filter on account name. The way to get a list of tokens belonging to an account is therefore:

ssc.find('tokens', 'balances', {account:  "NameOfAccountYoureInterestedIn"}, 1000, 0, function(){ do something with the results}

At the beginning of my javascript-code I declare a variable with a list of the accounts I am interested in, like so:

var accounts = [
  ["partitura.pal"],
  ["partitura.sonic"],
  //etc
];

Now I can loop over the elements of this list and perform the ssc.find-method on each element. That method returns a list of balances in that account. On each element in that list I can perform a tranfer action or a stake action. The code to perform on each element of the balances list is wrapped in the function-call to ssc.find, in the "function(){ do something with the results}" in the above example.

    for( var num=0; num < accounts.length ; num++)
    {
        var account = accounts[num][0];
        ssc.find('tokens', 'balances', 
            { 
                account:  account 
            },  
            1000, 0, [], function(err, result) {
                if( err ) { 
                    console.log( err, result );
                }
                else if (result)    {
                    for (var y = 0; y < result.length; y++) {
                        bal = result[y].balance
                        accountName=result[y].account;
                        if (bal > 0) {
                            token = result[y].symbol;
                            sendTokens(accountName, token, bal, "partitura");
                        }
                    }
                }
            }
        );
    };

After a call to ssc.find the variable result contains the table 'balances' for the account given as an argument in to call to ssc.find. On each row of this table is a different token and in each column is a different attribute of this token. In the example above I take the value of

result[y].balance;

i.e. the number of claimed but not yet staked tokens of a particular tokens. If I am interested in the number of staked tokens, I could write:

result[y].stake;

For each token with a balance greater than zero, I send the tokens to my account "partitura". The functions that does that (sendTokens), builds a JSON-object, like you have probably seen when transfering, staking or delegating manually. It then broadcasts this JSON-object to Steem by means of the steem library. To have that library available in NodeJS program, you can use the code:

var steem = require("steem");

Transfering, staking and delegating token requieres the active key of the account doing the transfering, staking or delegating. At the beginning of my javascript-code I declare a variable with a list of the keys I need, like so:

var keys = { 
   "partitura.pal" : "actieprivatekey1",
   "partitura.sonic" : "actieprivatekey2"
}

A function to transfer a given amount of a given token, can then be done like this:

function sendTokens( accountname, token, amount, receiver )
{
  var json = JSON.stringify(
    { "contractName"    : "tokens",
      "contractAction"  : "transfer",
      "contractPayload" : { "symbol": `${token}`, "to": `${receiver}`, "quantity" : `${amount}` } }
  );
  steem.broadcast.customJson(keys[accountname], [accountname], [], 'ssc-mainnet1', json,
    function  (err, result )  {
      if( err )
      {
        console.log( err, result );
      }
      else console.log(result);
  } );
}

For staking and delegating only the "contractAction" has to be altered:

var json = JSON.stringify(
    { "contractName"    : "tokens",
      "contractAction"  : "stake",
      "contractPayload" : { "symbol": `${token}`, "to": `${receiver}`, "quantity" : `${amount}` } }
  );

and

var json = JSON.stringify(
    { "contractName"    : "tokens",
      "contractAction"  : "delegate",
      "contractPayload" : { "symbol": `${token}`, "to": `${receiver}`, "quantity" : `${amount}` } }
  );

Taking all these elements together, gives me the following code for transferring claimed tokens from a list of accounts to my main account.

var accounts = [
     ["partitura.pal"],
     ["partitura.sonic"]
     //etc
  ];

var keys = { 
    "partitura.pal" : "actieprivatekey1",
    "partitura.sonic" : "actieprivatekey2"
    //etc
 }

var steem = require("steem");
const SSC = require( 'sscjs' );
const ssc = new SSC( 'https://api.steem-engine.com/rpc/' );

function getBalances()
{
    for( var num=0; num < accounts.length ; num++) 
    {
        var account = accounts[num][0];
        ssc.find('tokens', 'balances', 
            { 
                account:  account 
            },  
            1000, 0, [], function(err, result) {
                if( err ) {
                    console.log( err, result );
                }
                else if (result) {
                    for (var y = 0; y < result.length; y++) {
                        bal = result[y].balance
                        accountName=result[y].account;
                        if (bal > 0) {
                            token = result[y].symbol;
                            sendTokens(accountName, token, bal, "partitura");
                        }
                    }
                }
            }
        );
    };
}

function sendTokens( accountname, token, amount, receiver )
{
  var json = JSON.stringify(
    { "contractName"    : "tokens",
      "contractAction"  : "transfer",
      "contractPayload" : { "symbol": `${token}`, "to": `${receiver}`, "quantity" : `${amount}` } }
  );
  steem.broadcast.customJson(keys[accountname], [accountname], [], 'ssc-mainnet1', json,
    function  (err, result )  {
      if( err )
      {
        console.log( err, result );
      }
      else console.log(result);
  } );
}

(async function () {
    getBalances();
})
().catch(console.error);

This is just the basic setup. I have some variations on this basic setup because not all tokens go to the same main account, or because I want (for example) half of the tokens to go to a main account, or because I want to stake them in another account. All these variations can be easily build with the elements described above.

I finished writing and testing my own personal claiming and staking software this afternoon. I added some more niceties, like logging to a file instead of to the console, but the main elements are as described above. The software is now scheduled to run twice a day. And I look forward to look to the logfiles of the first round of claiming, transferring and staking tomorrow morning.

If you have questions don't hesitate to ask them in the comments.


You can support me using Steem Basic Income


Sort:  

I'm happy for you! It's good to be a nerd at something...



// You can support giphy by using one of your witness votes on untersatz! //


This post was shared in the Curation Collective Discord community for curators, and upvoted and resteemed by the @c-squared community account after manual review.
@c-squared runs a community witness. Please consider using one of your witness votes on us here

Wow! Chuffed that I inspired you to do this on your Raspberry Pi. I'm not exactly sure what my model is, but it's one of the early ones. It's quite slow and since that post I have moved most things to a private VPS with a bit more grunt. I've also created a master account which holds active/posting authorities so I don't have to keep adding keys to my script. You can either schedule your script via cron or use pm2 (with setInterval()). I choose the latter because it has way more features and can auto log console.log() commands to a file. There's also a pm2 service called logrotate which can manage the log file sizes automatically (as Raspberry Pi storage is limited).

What are the odds of two organists in separate parts of the world, both using a Raspberry Pi and NodeJS and being on Steem? Haha!

I have some cronjobs for the scheduling. Log file rotation is one of the niceties I programmed into the NodeJS-scripts. It does the job for now. The Raspberry has a 8 GB SD-card as memory. It'll probably be a while before it is filled with logfiles.

The VPS is where you have setup your witness as well? That'll probably be a tiny bit too much for a Raspberry... :-)

Nice! The witness is on its own VPS. It needs all the grunt it can get.

It will be great to get hold of it also. Trying to claim a lot of tokens can be time consuming

I use @drako's script to claim all rewards. The script I use to transfer it to my main account is in the post above.
With some variations the same script can be used for staking and delegating by just changing the contractaction (transfer, stake, delegate) and the contractpayload "to:" if you want to differentiate in the account receiving the actions.

Another tip is to see the json data that keychain prompts you with when you are using Steem Engine.

Yes, that's probably the practical source of information. But being a nerd and all means you have to look in the source code... 😀
Not unlike what I do with music: look into the source code, i.e. the original manuscript.

Posted using Partiko Android

!giphy-auto

Posted using Partiko iOS



// You can support giphy by using one of your witness votes on untersatz! //
PS. I couldn't find you a suitable GIF (sorry!)

You got a 48.14% upvote from @ocdb courtesy of @partitura! :)

@ocdb is a non-profit bidbot for whitelisted Steemians, current min bid is 1.5 SBD and max bid is 10 SBD and the equivalent amount in STEEM.
Check our website https://thegoodwhales.io/ for the whitelist, queue and delegation info. Join our Discord channel for more information.

If you like what @ocd does, consider voting for ocd-witness through SteemConnect or on the Steemit Witnesses page. :)

This post earned a total payout of 11.205$ and 8.425$ worth of author reward which was liquified using @likwid. To learn more.

Coin Marketplace

STEEM 0.26
TRX 0.11
JST 0.033
BTC 64266.94
ETH 3077.24
USDT 1.00
SBD 3.87