DEV Community

Cover image for Crypto CLI
Danny Rivera
Danny Rivera

Posted on • Updated on

Crypto CLI

I've always been amazed about cryptocurrencies, so I decided to build a CLI app that gets prices and more detailed information about any cryptocurrency out there.

To make my app I used Open URI, Net/http and JSON to gather and parse data from an API, I used PRY for testing and I also used TTY-Prompt, Launchy and Colorize to add style.

Note: Before even sketching your app you should first research the right API for your app, play around and see what data can you get from it. In my case I used CoinGecko.

I created an API class with methods to handle gathering and parsing full information from the top 20 cryptocurrencies and from an individual cryptocurrency. Another method handles gathering and parsing partial information (Names and IDs) from ALL coins out there.

class FINDCRYPTO::API

    def initialize
        @url_top_20 = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=bitcoin%2C%20ethereum%2C%20binancecoin%2C%20tether%2C%20polkadot%2C%20cardano%2C%20ripple%2C%20litecoin%2C%20chainlink%2C%20bitcoin-cash%2C%20stellar%2C%20usd-coin%2C%20uniswap%2C%20dogecoin%2C%20wrapped-bitcoin%2C%20okb%2C%20aave%2C%20cosmos%2C%20nem%2C%20solana%20%20&order=market_cap_desc&per_page=100&page=1&sparkline=false&price_change_percentage=1h"
    end

    def get_top_20_cryptocurrencies
        request = URI.parse(@url_top_20)
        response = Net::HTTP.get_response(request)
        crypto_hash = JSON.parse(response.body)
        #binding.pry
        self.create_cryptocurrency_objects(crypto_hash)
    end 

    def create_cryptocurrency_objects(crypto_hash)
        crypto_hash.each do |crypto_object|
            FINDCRYPTO::Cryptocurrency.new(crypto_object)
        end
    end

    def get_data_from_new_crypto(crypto_id)
        url = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=#{crypto_id}&order=market_cap_desc&per_page=100&page=1&sparkline=false&price_change_percentage=1h"
        request = URI.parse(url)
        response = Net::HTTP.get_response(request)
        crypto_hash = JSON.parse(response.body)
        self.create_cryptocurrency_objects(crypto_hash)
    end

    def get_all_cryptocurrencies_names_and_ids
        url = "https://api.coingecko.com/api/v3/coins/list?include_platform=false"
        request = URI.parse(url)
        response = Net::HTTP.get_response(request)
        crypto_hash = JSON.parse(response.body)
        self.create_cryptocurrency_names_and_ids_objects(crypto_hash)
    end

    def create_cryptocurrency_names_and_ids_objects(crypto_hash)
        crypto_hash.each do |crypto_object|
            FINDCRYPTO::Cryptocurrency_lookup.new(crypto_object)
        end
    end
end
Enter fullscreen mode Exit fullscreen mode

The Cryptocurrency class takes a hash from the API class and handles the creation of individual cryptocurrency objects with full information, it also keeps on track of objects created.

class FINDCRYPTO::Cryptocurrency
    @@all = []

    def initialize(crypto_hash)
        crypto_hash.each do |key, value|
            self.class.attr_accessor(key)
            self.send("#{key}=", value)
        end
        @@all << self
    end

    def self.all
        @@all
    end
end
Enter fullscreen mode Exit fullscreen mode

The Cryptocurrency_lookup class takes a hash from the API class and handles the creation of individual cryptocurrency objects with partial information (Names and IDs only), it also keeps on track of objects created. What's the point of this class? Well, first of all my app counts all available crypto coins, then we'll need a full list of coins along with its id in order to request full information of a specific coin from the API later.

class FINDCRYPTO::Cryptocurrency_lookup
    @@all = []

    def initialize(crypto_hash)
        crypto_hash.each do |key, value|
            self.class.attr_accessor(key)
            self.send("#{key}=", value)
        end
        @@all << self
    end

    def self.all
        @@all
    end
end
Enter fullscreen mode Exit fullscreen mode

At this point you may be wondering what's that FINDCRYPTO:: right before a class name. Well, it's called module FINDCRYPTO and it's just a wrapper for our code.

module FINDCRYPTO
end
Enter fullscreen mode Exit fullscreen mode

The CLI class keeps track of the user's menu choices. It initialize by displaying a welcome message along with the top 20 cryptocurrencies of the day and also gets a list of all crypto coins. The run method greets the user and displays the menu. In menu the user have the ability to select one of the 20 coins displayed, user can search for any cryptocurrency, user can also get updated data, see its logo in a browser or just display a list of 6000+ cryptocurrencies. The following code is easy to follow.

class FINDCRYPTO::CLI
    def initialize
        puts "\n"
        puts "--------------------------------$--------------------------------"
        crypt_is_everywhere = "Crypto Is Everywhere, But Should You Invest?"
        puts "           #{crypt_is_everywhere.colorize(:green)}          "
        puts "--------------------------------$--------------------------------"
        puts "\n"
        FINDCRYPTO::API.new.get_top_20_cryptocurrencies 
        FINDCRYPTO::API.new.get_all_cryptocurrencies_names_and_ids
        @prompt = TTY::Prompt.new(help_color: :cyan)
    end

    def run
        greeting
        menu
    end

    def greeting
        puts "There are exactly #{FINDCRYPTO::Cryptocurrency_lookup.all.count} cryptocurrencies at the time of this writing."
        puts "Some experts have suggested that Bitcoin could be worth as much as $100,000 one day!"
        puts "But, not everyone thinks investing in cryptocurrency is a good idea."
        puts "Check the following valuable data and make your own conclusions:"
        puts "\n"
    end

    def menu
        puts "--------------------------------$--------------------------------"
        top_20_crypt_prices = "Top 20 Cryptocurrency Prices by Market Cap:"
        puts "            #{top_20_crypt_prices.colorize(:green)}          "
        puts "--------------------------------$--------------------------------"
        display_top_20_cryptocurrencies
        puts "--------------------------------$--------------------------------"
        input = @prompt.select("Would you like more information?") do |menu|
            menu.choice "Yes, let me choose a cryptocurrency from the list above"
            menu.choice "Yes, but let me search for a different cryptocurrency"
            menu.choice "Nope! Please exit now."
        end

        if input == 'Yes, let me choose a cryptocurrency from the list above'
            input_number = @prompt.ask("Type its number please:", default: "e.g. 1 for Bitcoin")
        # if input.to_i > 0 && input.to_i <= FINDCRYPTO::Cryptocurrency.all.count
            number = input_number.to_i
            if number > FINDCRYPTO::Cryptocurrency.all.count || number == 0
                invalid_input_try_again
            else
                display_crypto(number)
                display_cryto_footer_menu
            end

        elsif input == 'Yes, but let me search for a different cryptocurrency'
            puts "-----------------------$------------------------"
            puts "Can't find your favorite crypto?"
            display_search_input = @prompt.select("Choose an option:") do |menu|
                menu.choice "Search a cryptocurrency"
                menu.choice "Show full list of cryptocurrencies"
                menu.choice "Go back"
                menu.choice "Exit"
            end
            if display_search_input == 'Show full list of cryptocurrencies'
                display_all_cryptocurrencies_names_and_ids
                display_cryto_footer_menu
            elsif display_search_input == 'Go back'
                menu
            elsif display_search_input == 'Search a cryptocurrency'
                display_new_crypto
            elsif display_search_input == 'Exit'
                exit_cli
            end

        elsif input == 'Nope! Please exit now.'
            exit_cli
        end 
    end



    def invalid_input_try_again
        puts "Invalid input. Please try again"
        sleep(1.5)
        menu
    end

    def exit_cli
        puts "If you don’t believe it or don’t get it, I don’t have the time to try to convince you, sorry."
        puts "– Satoshi Nakamoto"
        exit
    end

    def display_cryto_footer_menu
        puts "\n"
        puts "Very interesting, right?"
        back_to_menu_input = @prompt.select("Would you like to see data of another cryptocurrency?", %w(Yes Exit))
        if back_to_menu_input == "Yes"
            menu
        elsif back_to_menu_input == "Exit"
            exit_cli
        end
    end

    def display_new_crypto    
        input = @prompt.ask("Please type a Cryptocurrency name:", default: "e.g. Terra")

        FINDCRYPTO::Cryptocurrency.all.each.with_index(1) do |cryptocurrency, index| 
            if cryptocurrency.name.downcase == input.downcase
                crypto_id = cryptocurrency.id
                puts "Oops! #{cryptocurrency.name}'s price is already displayed in main menu."
                upadated_data_menu_input = @prompt.select("Do you want to see updated data?") do |menu|
                    menu.choice "Yes"
                    menu.choice "No. I love old news! Show me old data!"
                end
                #upadated_data_menu_input = gets.strip.downcase
                if upadated_data_menu_input == "Yes"
                    FINDCRYPTO::API.new.get_data_from_new_crypto(crypto_id)
                    number = FINDCRYPTO::Cryptocurrency.all.count
                    display_crypto(number)
                    display_cryto_footer_menu
                else upadated_data_menu_input == "No. I love old news! Show me old data!"
                    display_crypto(index)
                    display_cryto_footer_menu
                end
            end
        end
        ### Iterate over all 6000+ cryptos to confirm name
        FINDCRYPTO::Cryptocurrency_lookup.all.each do |cryptocurrency|
            if cryptocurrency.name.downcase == input.downcase
                crypto_id = cryptocurrency.id
                FINDCRYPTO::API.new.get_data_from_new_crypto(crypto_id)
                number = FINDCRYPTO::Cryptocurrency.all.count
                display_crypto(number)
                display_cryto_footer_menu
           end
        end

        puts "Oops!"
        puts "Sorry! I just iterated over #{FINDCRYPTO::Cryptocurrency_lookup.all.count} cryptocurrencies and I couldn't find '#{input}'"
        puts "Double check its name and try again!"
        sleep(1)
        display_new_crypto
    end

    def display_crypto(number)
        dollar_sign = '$'
        index_number = number -1
        crypto = FINDCRYPTO::Cryptocurrency.all[index_number]
        bar_left = "--------------------$"
        bar_right = "$--------------------"
        puts "#{bar_left.ljust(24)} #{crypto.name.upcase.ljust(10).colorize(:green)} #{bar_right}"
        puts "#{crypto.name}(#{crypto.symbol.upcase}) can be purchased on peer-to-peer networks"
        puts "and cryptocurrency exchanges, just like buying stocks."
        puts "-----------------------$ DATA $-----------------------"
        puts "*** This data is valid as #{convert_to_local_time(crypto.last_updated)} ***"
        coin_symbol_and_price = crypto.symbol.upcase + " price:"                  
        puts "#{coin_symbol_and_price.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.current_price.to_s.colorize(:green)}"
        pc24h = "Price Change 24 Hours:"
        puts "#{pc24h.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.price_change_24h.to_s.colorize(:green)} or #{sprintf('%+.2f', crypto.price_change_percentage_24h)}%"
        pc1h = "Price Change  1 Hour:"
        puts "#{pc1h.ljust(25)} #{dollar_sign.colorize(:green)}#{sprintf('%.2f', ((crypto.price_change_percentage_1h_in_currency * crypto.current_price)/100)).to_s.colorize(:green)} or #{sprintf('%+.2f', crypto.price_change_percentage_1h_in_currency)}%"
        hh24 = "24 Hour High:"
        puts "#{hh24.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.high_24h.to_s.colorize(:green)}"
        hl24 = "24 Hour Low:"
        puts "#{hl24.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.low_24h.to_s.colorize(:green)}"
        tv24 = "Trading Volume 24 Hours:"
        puts "#{tv24.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.total_volume.to_s.colorize(:green)}"
        mc = "Market Cap:"
        puts "#{mc.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.market_cap.to_s.colorize(:green)}"       
        mcr = "Market Cap Rank:"
        hash_sign = '#'
        puts "#{mcr.ljust(25)} #{hash_sign.colorize(:green)}#{crypto.market_cap_rank.to_s.colorize(:green)}"
        cs = "Circulating Supply:"
        puts "#{cs.ljust(25)} #{dollar_sign.colorize(:green)}#{crypto.circulating_supply.to_s.colorize(:green)}"
        #puts "Logo link: #{crypto.image}"
        puts "--------------------------$---------------------------"
        puts "Showing you #{crypto.name}'s logo in your browser!"
        sleep(1)
        Launchy.open(crypto.image)
    end

    def display_top_20_cryptocurrencies
        dollar_sign = '$'
        dot = '.'
        price_string = 'PRICE:'
        puts "#   COIN:                #{price_string.colorize(:green)}              DATE:"
        FINDCRYPTO::Cryptocurrency.all.each.with_index(1) do |cryptocurrency, index|
            puts "#{index.to_s.ljust(3)} #{cryptocurrency.name.ljust(20)} #{dollar_sign.colorize(:green)}#{cryptocurrency.current_price.to_s.ljust(18).colorize(:green)} #{convert_to_local_time(cryptocurrency.last_updated)}"
        end
    end

    def display_all_cryptocurrencies_names_and_ids
        puts "***This is a LOOOOOOOOOONG list! I'm warning you!***"
        sleep(1)
        puts "#  COIN:"
        FINDCRYPTO::Cryptocurrency_lookup.all.each.with_index(1) do |cryptocurrency, index|
            puts "#{index}. #{cryptocurrency.name}(#{cryptocurrency.symbol.upcase})"
        end
    end

    private
    def convert_to_local_time(utc_time) 
        utc_parsed = Time.parse(utc_time)
        utc_parsed.localtime
    end
end
Enter fullscreen mode Exit fullscreen mode

In order to RUN this app you'll need to set up your environment:

require 'pry'
require 'net/http'
require 'open-uri'
require 'json'
require 'tty-prompt'
require 'launchy'
require 'colorize'

require_relative '../lib/modules/findcrypto.rb'
require_relative '../lib/api'
require_relative '../lib/cli'
require_relative '../lib/cryptocurrency'
require_relative '../lib/cryptocurrency_lookup'
Enter fullscreen mode Exit fullscreen mode

And make sure to create a run file:

#!/usr/bin/env ruby

require_relative '../config/environment'

FINDCRYPTO::CLI.new.run
Enter fullscreen mode Exit fullscreen mode

Final product:
Alt Text
Check out a quick YouTube video of my project.
Check out the project here on GitHub!

Top comments (6)

Collapse
 
jesko profile image
Jeremy Stalko • Edited

Quite an interesting program, I think it's a really cool thing, the only thing I think is that not so many cryptocurrencies support especially niche ones, but for the main ones - just super. But it would be great to do something like this for parsing crypto wallet for business. Very curious thing would be, but it's probably not welcomed)

Collapse
 
f3lix profile image
Felix

I like to analyze and predict stock market events. Many factors suggest that this is a good time to buy bitcoin now. I think buying bitcoin anonymously is a great option. After reading the reviews in general, it was clear that there is a lot of fraud around and so you need to protect yourself.

Collapse
 
ajcryptoknights profile image
AJCryptoknights

This tutorial is really helpful to me, in addition of this I discovered and learn a lot from other website regarding cryptocurrency.
You can visit this website to know more about Cryptocurrency [](cryptoknights.online/)

Collapse
 
avoton00 profile image
Avonaco Chayton

I urge you to contact a professional hacker if you are a victim of a scam. I lost a huge sum of money to a crypto scammer who lured me to invest in what I later found out to be a scam, but the good news is that HACKWEST AT WRITEME DOT COM recovered all the money back to my blockchain wallet. The fraudster stole about $468k in USDT, BTC, Ethereum and Sol. I strongly recommend West and his team if you’re a victim of crypto scam.

Collapse
 
pimcliefff profile image
PimCliefff

I'm always on the lookout for cool and user friendly crypto trading apps so I'll check this one out.

Collapse
 
tonygreyn profile image
TonyGreyn

Do you like being a trader? If you are an experienced trader, give a couple of good tips.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.