Using Steem-API with Ruby Part 6 — Print Account Balances improved
Repositories
SteemRubyTutorial
All examples from this tutorial can be found as fully functional scripts on GitHub:
- SteemRubyTutorial
- steem-api sample code: Steem-Dump-Balances.rb
- radiator sample code: Steem-Print.Balances.rb.
steem-ruby
- Project Name: Steem Ruby
- Repository: https://github.com/steemit/steem-ruby
- Official Documentation: https://github.com/steemit/steem-ruby
- Official Tutorial: N/A
radiator
- Project Name: Radiator
- Repository: https://github.com/inertia186/radiator
- Official Documentation: https://www.rubydoc.info/gems/radiator
- Official Tutorial: https://developers.steem.io/tutorials-ruby/getting_started
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:
- When both APIs are installed ruby must be told which one to use.
- Both APIs can't be used in the same script.
This part of the tutorial builds on several previous parts:
- Using Steem-API with Ruby Part 2 — Print Account Balances
- Using Steem-API with Ruby Part 3 — Print Dynamic Global Properties
- Using Steem-API with Ruby Part 4 — Convert VESTS ⇔ Steem
- Using Steem-API with Ruby Part 5 — Print Current Median History Price
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:
However the values where not the way they are usually displayed on the website:
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:
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.
- There is now an operator method for all four base functions.
- Both amounts are checked that if they have the same asset type (which is mathematically correct).
- 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:
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:
Curriculum
First tutorial
Previous tutorial
Next tutorial
Proof of Work
- GitHub: SteemRubyTutorial Issue #7
Image Source
- Ruby symbol: Wikimedia, CC BY-SA 2.5.
- Steemit logo Wikimedia, CC BY-SA 4.0.
- Steem Power logos: SteemitBoard, @captaink
- Screenshots: @krischik, CC BY-NC-SA 4.0
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]
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.
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.