Using Steem-API with Ruby Part 6 — Print Account Balances improved

in #utopian-io6 years ago (edited)

Steemit_Ruby.png

Repositories

SteemRubyTutorial

All examples from this tutorial can be found as fully functional scripts on GitHub:

steem-ruby

radiator

What Will I Learn?

This tutorial shows how to interact with the Steem blockchain and Steem database using Ruby. When using Ruby you have two APIs available to chose: steem-api and radiator which differentiates in how return values and errors are handled:

  • steem-api uses closures and exceptions and provides low level computer readable data.
  • radiator uses classic function return values and provides high level human readable data.

Since both APIs have advantages and disadvantages sample code for both APIs will be provided so the reader ca decide which is more suitable.

In this part of the tutorial the Steem-Dump-Balances.rb and Steem-Print-Balances.rb will be revised to convert all values into each other and calculate the account value.

Requirements

Basic knowledge of Ruby programming is needed. It is necessary to install at least Ruby 2.5 as well as the following ruby gems:

gem install bundler
gem install colorize
gem install contracts
gem install steem-ruby
gem install radiator

Note: Both steem-ruby and radiator provide a file called steem.rb. This means that:

  1. When both APIs are installed ruby must be told which one to use.
  2. Both APIs can't be used in the same script.

This part of the tutorial builds on several previous parts:

If there is anything not clear you can ask in the comments.

Difficulty

For reader with programming experience this tutorial is basic level.

Tutorial Contents

In Part 2 of the tutorial the account balances where printed out the following way:

Screenshot at Jan 27 17-44-59.png

However the values where not the way they are usually displayed on the website:

Screenshot at Feb 04 142910.png

The steem power is printed in VESTS which are incredibly large number and the estimated account was missing from the output.

In this part of the tutorial the Steem-Dump-Balances.rb and Steem-Print-Balances.rb will be improve the output to the user more informations. For this a greatly improved Amount class will be used. All the theoretical knowledge has been part of previous tutorials so this tutorial will delve right into the code.

Implementation using steem-ruby

The central part of the improved script is the improved Amount class which can be used in other projects as well.


class Amount < Steem::Type::Amount
   include Contracts::Core

   public

Defining constants for the three currencies / tokens on on the Steem blockchain. This reduces the risk of typing mistakes.

      VESTS = "VESTS"
      STEEM = "STEEM"
      SBD = "SBD"

Return the actual amount as float to be used by the various calculations.

      Contract nil => Float
      def to_f
         return @amount.to_f
      end

Convert VESTS to level, which is one of "Whale", "Orca", "Dolphin", "Minnow", "Plankton" or "N/A" when the value isn't a VEST value.

Logo Level Your Steem Power in VESTS Your Steem Power in Steem
Whale more than 10⁹ VESTS more than ≈500'000 Steem
Ocra [10⁸ … 10⁹) VESTS ≈50'000 … ≈500'000 Steem
Dolphin [10⁷ … 10⁸) VESTS ≈5'000 … ≈50'000 Steem
Minnow [10⁶ … 10⁷) VESTS ≈500 … ≈5'000 Steem
Plankton [0 … 10⁶) VESTS 0 … ≈500 Steem
      Contract nil => String
      def to_level
         _value = @amount.to_f

         return (
         if @asset != VESTS then
            "N/A"
         elsif _value > 1.0e9 then
            "Whale"
         elsif _value > 1.0e8 then
            "Ocra"
         elsif _value > 1.0e7 then
            "Dolphin"
         elsif _value > 1.0e6 then
            "Minnow"
         else
            "Plankton"
         end)
      end

Converting the amount to a desired asset type. If the amount is already in the needed asset type the value is cloned. Since we only have conversions for VESTS⇔STEEM and STEEM⇔SBD the conversion between VESTS⇔SBD are done in two steps with one recursive call.

      Contract nil => Amount
      def to_sbd
         return (
         case @asset
            when SBD
               self.clone
            when STEEM
               Amount.to_amount(@amount.to_f * Conversion_Rate_Steem, SBD)
            when VESTS
               self.to_steem.to_sbd
            else
               raise ArgumentError, 'unknown asset type types'
         end)
      end

      Contract nil => Amount
      def to_steem
         return (
         case @asset
            when SBD
               Amount.to_amount(@amount.to_f / Conversion_Rate_Steem, STEEM)
            when STEEM
               self.clone
            when VESTS
               Amount.to_amount(@amount.to_f * Conversion_Rate_Vests, STEEM)
            else
               raise ArgumentError, 'unknown asset type types'
         end)
      end

      Contract nil => Amount
      def to_vests
         return (
         case @asset
            when SBD
               self.to_steem.to_vests
            when STEEM
               Amount.to_amount(@amount.to_f / Conversion_Rate_Vests, VESTS)
            when VESTS
               self.clone
            else
               raise ArgumentError, 'unknown asset type types'
         end)
      end

Create a colorised string showing the amount in SDB, STEEM and VESTS. The value of the actual asset type is colorised in blue while the converted values are colorised in grey (aka dark white). Here an example:

to_ansi_s.png

The magic all happens in the % operator which calls sprintf to create the formatted string.

      Contract nil => String
      def to_ansi_s
         _sbd   = to_sbd
         _steem = to_steem
         _vests = to_vests

         return (
         "%1$15.3f %2$s".colorize(
            if @asset == SBD then
               :blue
            else
               :white
            end
         ) + " " + "%3$15.3f %4$s".colorize(
            if @asset == STEEM then
               :blue
            else
               :white
            end
         ) + " " + "%5$18.6f %6$s".colorize(
            if @asset == VESTS then
               :blue
            else
               :white
            end
         )) % [
            _sbd.to_f,
            _sbd.asset,
            _steem.to_f,
            _steem.asset,
            _vests.to_f,
            _vests.asset]
      end

The arithmetic operators have changed slightly since Part 2.

  1. There is now an operator method for all four base functions.
  2. Both amounts are checked that if they have the same asset type (which is mathematically correct).
  3. A new Amount is returned with is of the same asset type (which also is mathematically correct).

In addition to being mathematically correct this makes the methods more simple.

      Contract Amount => Amount
      def +(right)
         raise ArgumentError, 'asset types differ' if @asset != right.asset

         return Amount.to_amount(@amount.to_f + right.to_f, @asset)
      end

      Contract Amount => Amount
      def -(right)
         raise ArgumentError, 'asset types differ' if @asset != right.asset

         return Amount.to_amount(@amount.to_f - right.to_f, @asset)
      end

      Contract Amount => Amount
      def *(right)
         raise ArgumentError, 'asset types differ' if @asset != right.asset

         return Amount.to_amount(@amount.to_f * right.to_f, @asset)
      end

      Contract Amount => Amount
      def /(right)
         raise ArgumentError, 'asset types differ' if @asset != right.asset

         return Amount.to_amount(@amount.to_f / right.to_f, @asset)
      end

Helper factory method to create a new Amount from an value and asset type. Used by the arithmetic operators amd made private as it's not needed outside the class.

   private

      Contract Float, String => Amount
      def self.to_amount(value, asset)
         return Amount.new(value.to_s + " " + asset)
      end
end

Since the implementations for steem-api and radiator are almost identical The rest of the functionality is explained in the radiator part.

Hint: Follow this link to Github for the complete script with comments and syntax highlighting: Steem-Dump-Balances.rb.

The output of the command (for the steem account) looks like this:

Screenshot at Feb 13 145331.png

Implementation using radiator

First the two conversion for converting VESTS⇔STEEM and STEEM⇔SBD values need to be calculate. This is done using the global properties and the median history.

begin

Create instance to the steem condenser API which will give us access to to the global properties and the median history.

   _condenser_api = Radiator::CondenserApi.new

Read the global properties and median history values. Note the use of result at the end. It stripps some additional JSON boilerplate which isn't needed and makes the data more useable.

   _global_properties    = _condenser_api.get_dynamic_global_properties.result
   _median_history_price = _condenser_api.get_current_median_history_price.result

Calculate the conversion Rate for STEEM to SBD. Uses the Amount class to convert the string values into amounts.

   _base                 = Amount.new _median_history_price.base
   _quote                = Amount.new _median_history_price.quote
   Conversion_Rate_Steem = _base.to_f / _quote.to_f

Calculate the conversion Rate for VESTS to STEEM. Here too the Amount class is used to convert the string values into amounts.

   _total_vesting_fund_steem = Amount.new _global_properties.total_vesting_fund_steem
   _total_vesting_shares     = Amount.new _global_properties.total_vesting_shares
   Conversion_Rate_Vests     = _total_vesting_fund_steem.to_f / _total_vesting_shares.to_f
rescue => error

The Kernel::abort is used so the script aborts when an the important conversion values could not be calculated.

   Kernel::abort("Error reading global properties:\n".red + error.to_s)
end

Print account information for an array of accounts.

def print_account_balances(accounts)
   accounts.each do |account|

Create an amount instances for each balance to be used for further processing.

      _balance                  = Amount.new account.balance
      _savings_balance          = Amount.new account.savings_balance
      _sbd_balance              = Amount.new account.sbd_balance
      _savings_sbd_balance      = Amount.new account.savings_sbd_balance
      _vesting_shares           = Amount.new account.vesting_shares
      _delegated_vesting_shares = Amount.new account.delegated_vesting_shares
      _received_vesting_shares  = Amount.new account.received_vesting_shares

Calculate actual vesting by subtracting and adding delegation done by the user (_delegated_vesting_shares) and done to the user (_received_vesting_shares).

      _actual_vesting = _vesting_shares - _delegated_vesting_shares + _received_vesting_shares

Calculate the account value by adding all balances in SBD. Apart from the delegation. Delegation does not add or subtract from the account value as it can be reverted at any time.

      _account_value =
         _balance.to_sbd +
            _savings_balance.to_sbd +
            _sbd_balance.to_sbd +
            _savings_sbd_balance.to_sbd +
            _vesting_shares.to_sbd

Pretty print out the balances. Note that for a quick printout Radiator::Type::Amount provides a simple to_s method. But this method won't align the decimal point resulting in a hard to read output. The new to_ansi_s will format the values perfectly in columns.

      puts ("Account: %1$s".blue + +" " + "(%2$s)".green) % [account.name, _vesting_shares.to_level]
      puts ("  SBD             = " + _sbd_balance.to_ansi_s)
      puts ("  SBD Savings     = " + _savings_sbd_balance.to_ansi_s)
      puts ("  Steem           = " + _balance.to_ansi_s)
      puts ("  Steem Savings   = " + _savings_balance.to_ansi_s)
      puts ("  Steem Power     = " + _vesting_shares.to_ansi_s)
      puts ("  Delegated Power = " + _delegated_vesting_shares.to_ansi_s)
      puts ("  Received Power  = " + _received_vesting_shares.to_ansi_s)
      puts ("  Actual Power    = " + _actual_vesting.to_ansi_s)
      puts ("  Account Value   = " + "%1$15.3f %2$s".green) % [
         _account_value.to_f,
         _account_value.asset]
   end

   return
end

if ARGV.length == 0 then
   puts "
Steem-Print-Balances — Print account balances.

Usage:
   Steem-Print-Balances account_name …

"
else
   Account_Names = ARGV

   Database_Api = Radiator::DatabaseApi.new

Request account information from the Steem database and print out the accounts balances found using a new function or print out error information when an error occurred.

   Result = Database_Api.get_accounts(Account_Names)

   if Result.key?('error') then
      Kernel::abort("Error reading accounts:\n".red + Result.error.to_s)
   elsif Result.result.length == 0 then
      puts "No accounts found.".yellow
   else
      print_account_balances Result.result
   end
end

Hint: Follow this link to Github for the complete script with comments and syntax highlighting : Steem-Print-Balances.rb.

The output of the command (for the steem account) looks identical to the previous output:

Screenshot at Feb 13 145420.png

Curriculum

First tutorial

Previous tutorial

Next tutorial

Proof of Work

Image Source

Beneficiary

Beneficiary.png

comment votes posts level payout commented voted

Sort:  

Thank you for your contribution @krischik.
After analyzing your tutorial we suggest the following points below:

  • Code sections are better displayed using the code markup, instead of placing them as screenshots.

  • Using the first person in the tutorials makes it difficult to understand the tutorials. We suggest using the third person in your text.

  • Thanks for following some suggestions we put in your previous tutorials.

Thank you for your work in developing this tutorial.
Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Code sections are better displayed using the code markup, instead of placing them as screenshots.

All code section are in markup and also on GitHub as complete script. Only the script output is in screenshots. That was suggested to make article more visually appealing.

We suggest using the third person in your text.

Done.

Thank you for your review, @portugalcoin! Keep up the good work!

Hey, @krischik!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Hi, @krischik!

You just got a 0.98% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.