From 5b269ab982b271e9f902886ab95b81558e34857a Mon Sep 17 00:00:00 2001
From: Holger <holger@nahrstaedt.de>
Date: Thu, 25 Oct 2018 23:46:08 +0200
Subject: [PATCH] Several improvements and prepare next release

Account
* update_account_keys added for changing account keys
* comment fixed for chains without SBD
RC
* RC changes from 0.20.6 included
Witness
* Fixed for chains without SBD symbol
Operations
* Claim_reward_balance fixed for chains without SBD symbol

Chains
* VIT fixed
---
 .circleci/config.yml        | 33 ++++++++++++---
 CHANGELOG.rst               |  8 ++++
 beem/account.py             | 80 +++++++++++++++++++++++++++++++------
 beem/comment.py             | 22 ++++++----
 beem/constants.py           | 42 ++++++++++++++++++-
 beem/rc.py                  | 63 ++++++++++++++++++++++++-----
 beem/version.py             |  2 +-
 beem/witness.py             | 41 +++++++++++++------
 beemapi/version.py          |  2 +-
 beembase/operations.py      | 22 ++++++----
 beembase/version.py         |  2 +-
 beemgraphenebase/chains.py  |  1 -
 beemgraphenebase/version.py |  2 +-
 setup.py                    |  3 +-
 14 files changed, 260 insertions(+), 63 deletions(-)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index ae7ff195..2491b968 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -8,7 +8,7 @@ jobs:
     docker:
       # specify the version you desire here
       # use `-browsers` prefix for selenium tests, e.g. `3.6.1-browsers`
-      - image: circleci/python:3.6.4
+      - image: circleci/python:3.6.6
       
       # Specify service dependencies here if necessary
       # CircleCI maintains a library of pre-built images
@@ -57,7 +57,7 @@ jobs:
 
   build-python2.7:
     docker:
-      - image: circleci/python:2.7.13
+      - image: circleci/python:2.7
     working_directory: ~/repo
     steps:
       - checkout
@@ -79,7 +79,7 @@ jobs:
 
   build-python3.4:
     docker:
-      - image: circleci/python:3.4.8
+      - image: circleci/python:3.4
     working_directory: ~/repo
     steps:
       - checkout
@@ -101,7 +101,7 @@ jobs:
 
   build-python3.5:
     docker:
-      - image: circleci/python:3.5.5
+      - image: circleci/python:3.5
     working_directory: ~/repo
     steps:
       - checkout
@@ -120,6 +120,27 @@ jobs:
           command: |
             tox -e py35
 
+  build-python3.7:
+    docker:
+      - image: circleci/python:3.7
+    working_directory: ~/repo
+    steps:
+      - checkout
+      # Download and cache dependencies
+      - restore_cache:
+          keys:
+          - v1-dependencies-{{ checksum "requirements-test.txt" }}
+          # fallback to using the latest cache if no exact match is found
+          - v1-dependencies-
+      - run:
+          name: install dependencies
+          command: |
+            sudo python -m pip install --upgrade -r requirements-test.txt
+      - run:
+          name: run tests
+          command: |
+            tox -e py37
+            
 workflows:
   version: 2
   build:
@@ -134,4 +155,6 @@ workflows:
       - build-python3.5:
           requires:
             - build-python3.4
-
+      - build-python3.7:
+          requires:
+            - build-python3.5
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 6a7935b0..28743c79 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,5 +1,13 @@
 Changelog
 =========
+0.20.10
+-------
+* update_account_keys added for changing account keys
+* comment, witness, account classes fixed for chains without SBD
+* RC costs adapted on changes from 0.20.6
+* VIT chain fixed
+* update_account_keys function added to account
+
 0.20.9
 ------
 * add missing scrypt to the pyinstaller
diff --git a/beem/account.py b/beem/account.py
index ae92617d..b8d604d0 100644
--- a/beem/account.py
+++ b/beem/account.py
@@ -20,7 +20,7 @@ from .utils import formatTimeString, formatTimedelta, remove_from_dict, reputati
 from beem.amount import Amount
 from beembase import operations
 from beem.rc import RC
-from beemgraphenebase.account import PrivateKey, PublicKey
+from beemgraphenebase.account import PrivateKey, PublicKey, PasswordKey
 from beemgraphenebase.py23 import bytes_types, integer_types, string_types, text_type
 from beem.constants import STEEM_VOTE_REGENERATION_SECONDS, STEEM_1_PERCENT, STEEM_100_PERCENT, STEEM_VOTING_MANA_REGENERATION_SECONDS
 log = logging.getLogger(__name__)
@@ -2204,6 +2204,51 @@ class Account(BlockchainObject):
         })
         return self.steem.finalizeOp(op, account, "active", **kwargs)
 
+    def update_account_keys(self, new_password, account=None, **kwargs):
+        """ Updates all account keys
+
+            This method does **not** add any private keys to your
+            wallet but merely changes the memo public key.
+
+            :param str new_password: is used to derive the owner, active,
+                posting and memo key
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if account is None:
+            account = self
+        else:
+            account = Account(account, steem_instance=self.steem)
+
+        key_auths = {}
+        for role in ['owner', 'active', 'posting', 'memo']:
+            pk = PasswordKey(account['name'], new_password, role=role)
+            key_auths[role] = format(pk.get_public_key(), self.steem.prefix) 
+
+        op = operations.Account_update(**{
+            "account": account["name"],
+            'owner': {
+                 'account_auths': [],
+                 'key_auths': [[key_auths['owner'], 1]],
+                 "address_auths": [],
+                 'weight_threshold': 1},
+            'active': {
+                 'account_auths': [],
+                 'key_auths': [[key_auths['active'], 1]],
+                 "address_auths": [],
+                 'weight_threshold': 1},
+            'posting': {
+                 'account_auths': account['posting']['account_auths'],
+                 'key_auths': [[key_auths['posting'], 1]],
+                 "address_auths": [],
+                 'weight_threshold': 1},
+            'memo_key': key_auths['memo'],
+            "json_metadata": account['json_metadata'],
+            "prefix": self.steem.prefix,
+        })
+
+        return self.steem.finalizeOp(op, account, "owner", **kwargs)
+
     # -------------------------------------------------------------------------
     # Simple Transfer
     # -------------------------------------------------------------------------
@@ -2459,18 +2504,29 @@ class Account(BlockchainObject):
         reward_vests = self._check_amount(reward_vests, self.steem.vests_symbol)
 
         if reward_steem.amount == 0 and reward_sbd.amount == 0 and reward_vests.amount == 0:
-            reward_steem = account.balances["rewards"][0]
-            reward_sbd = account.balances["rewards"][1]
-            reward_vests = account.balances["rewards"][2]
+            if len(account.balances["rewards"]) == 3:
+                reward_steem = account.balances["rewards"][0]
+                reward_sbd = account.balances["rewards"][1]
+                reward_vests = account.balances["rewards"][2]
+                op = operations.Claim_reward_balance(
+                    **{
+                        "account": account["name"],
+                        "reward_steem": reward_steem,
+                        "reward_sbd": reward_sbd,
+                        "reward_vests": reward_vests,
+                        "prefix": self.steem.prefix,
+                    })                
+            else:
+                reward_steem = account.balances["rewards"][0]
+                reward_vests = account.balances["rewards"][1]
+                op = operations.Claim_reward_balance(
+                    **{
+                        "account": account["name"],
+                        "reward_steem": reward_steem,
+                        "reward_vests": reward_vests,
+                        "prefix": self.steem.prefix,
+                    })
 
-        op = operations.Claim_reward_balance(
-            **{
-                "account": account["name"],
-                "reward_steem": reward_steem,
-                "reward_sbd": reward_sbd,
-                "reward_vests": reward_vests,
-                "prefix": self.steem.prefix,
-            })
         return self.steem.finalizeOp(op, account, "posting", **kwargs)
 
     def delegate_vesting_shares(self, to_account, vesting_shares,
diff --git a/beem/comment.py b/beem/comment.py
index e2af12dd..31502a8e 100644
--- a/beem/comment.py
+++ b/beem/comment.py
@@ -431,21 +431,25 @@ class Comment(BlockchainObject):
                     "payout_SBD": Amount(0, self.steem.sbd_symbol, steem_instance=self.steem),
                     "total_payout_SBD": Amount(self["total_payout_value"], steem_instance=self.steem)}
 
-        median_price = Price(self.steem.get_current_median_history(), steem_instance=self.steem)
+        median_hist = self.steem.get_current_median_history()
+        if median_hist is not None:
+            median_price = Price(median_hist, steem_instance=self.steem)
         beneficiaries_pct = self.get_beneficiaries_pct()
         curation_tokens = self.reward * 0.25
         author_tokens = self.reward - curation_tokens
         curation_rewards = self.get_curation_rewards()
-        if self.steem.hardfork >= 20:
+        if self.steem.hardfork >= 20 and median_hist is not None:
             author_tokens += median_price * curation_rewards['unclaimed_rewards']
 
         benefactor_tokens = author_tokens * beneficiaries_pct / 100.
         author_tokens -= benefactor_tokens
 
-        sbd_steem = author_tokens * self["percent_steem_dollars"] / 20000.
-        vesting_steem = median_price.as_base(self.steem.steem_symbol) * (author_tokens - sbd_steem)
-
-        return {'pending_rewards': True, "payout_SP": vesting_steem, "payout_SBD": sbd_steem, "total_payout_SBD": author_tokens}
+        if median_hist is not None:
+            sbd_steem = author_tokens * self["percent_steem_dollars"] / 20000.
+            vesting_steem = median_price.as_base(self.steem.steem_symbol) * (author_tokens - sbd_steem)
+            return {'pending_rewards': True, "payout_SP": vesting_steem, "payout_SBD": sbd_steem, "total_payout_SBD": author_tokens}
+        else:
+            return {'pending_rewards': True, "total_payout": author_tokens}
 
     def get_curation_rewards(self, pending_payout_SBD=False, pending_payout_value=None):
         """ Returns the curation rewards.
@@ -475,7 +479,9 @@ class Comment(BlockchainObject):
                 }
 
         """
-        median_price = Price(self.steem.get_current_median_history(), steem_instance=self.steem)
+        median_hist = self.steem.get_current_median_history()
+        if median_hist is not None:
+            median_price = Price(median_hist, steem_instance=self.steem)
         pending_rewards = False
         total_vote_weight = self["total_vote_weight"]
         if not self["allow_curation_rewards"] or not self.is_pending():
@@ -488,7 +494,7 @@ class Comment(BlockchainObject):
                 pending_payout_value = Amount(pending_payout_value, self.steem.sbd_symbol, steem_instance=self.steem)
             elif isinstance(pending_payout_value, str):
                 pending_payout_value = Amount(pending_payout_value, steem_instance=self.steem)
-            if pending_payout_SBD:
+            if pending_payout_SBD or median_hist is None:
                 max_rewards = (pending_payout_value * 0.25)
             else:
                 max_rewards = median_price.as_base(self.steem.steem_symbol) * (pending_payout_value * 0.25)
diff --git a/beem/constants.py b/beem/constants.py
index 3b634d92..68cc841e 100644
--- a/beem/constants.py
+++ b/beem/constants.py
@@ -18,7 +18,9 @@ STATE_BYTES_SCALE = 10000
 STATE_TRANSACTION_BYTE_SIZE = 174
 STATE_TRANSFER_FROM_SAVINGS_BYTE_SIZE = 229
 STATE_LIMIT_ORDER_BYTE_SIZE = 1940
+EXEC_FOLLOW_CUSTOM_OP_SCALE = 20
 RC_DEFAULT_EXEC_COST = 100000
+STATE_COMMENT_VOTE_BYTE_SIZE = 525
 STEEM_RC_REGEN_TIME = 60 * 60 * 24 * 5
 
 state_object_size_info = {'authority_base_size': 4 * STATE_BYTES_SCALE,
@@ -31,7 +33,7 @@ state_object_size_info = {'authority_base_size': 4 * STATE_BYTES_SCALE,
                           'comment_object_permlink_char_size': 1 * STATE_BYTES_SCALE,
                           'comment_object_parent_permlink_char_size': 2 * STATE_BYTES_SCALE,
                           'comment_object_beneficiaries_member_size': 18 * STATE_BYTES_SCALE,
-                          'comment_vote_object_base_size': 47 * STATE_BYTES_SCALE,
+                          'comment_vote_object_base_size': 47 * STATE_COMMENT_VOTE_BYTE_SIZE,
                           'convert_request_object_base_size': 48 * STATE_BYTES_SCALE,
                           'decline_voting_rights_request_object_base_size': 28 * STATE_BYTES_SCALE,
                           'escrow_object_base_size': 119 * STATE_BYTES_SCALE,
@@ -46,4 +48,42 @@ state_object_size_info = {'authority_base_size': 4 * STATE_BYTES_SCALE,
                           'witness_object_url_char_size': 1 * STATE_BYTES_SCALE,
                           'witness_vote_object_base_size': 40 * STATE_BYTES_SCALE}
 
+resource_execution_time = {"account_create_operation_exec_time": 57700,
+                           "account_create_with_delegation_operation_exec_time": 57700,
+                           "account_update_operation_exec_time": 14000,
+                           "account_witness_proxy_operation_exec_time": 117000,
+                           "account_witness_vote_operation_exec_time": 23000,
+                           "cancel_transfer_from_savings_operation_exec_time": 11500,
+                           "change_recovery_account_operation_exec_time": 12000,
+                           "claim_account_operation_exec_time": 10000,
+                           "claim_reward_balance_operation_exec_time": 50300,
+                           "comment_operation_exec_time": 114100,
+                           "comment_options_operation_exec_time": 13200,
+                           "convert_operation_exec_time": 15700,
+                           "create_claimed_account_operation_exec_time": 57700,
+                           "custom_operation_exec_time": 11400,
+                           "custom_json_operation_exec_time": 11400,
+                           "custom_binary_operation_exec_time": 11400,
+                           "decline_voting_rights_operation_exec_time": 5300,
+                           "delegate_vesting_shares_operation_exec_time": 19900,
+                           "delete_comment_operation_exec_time": 51100,
+                           "escrow_approve_operation_exec_time": 9900,
+                           "escrow_dispute_operation_exec_time": 11500,
+                           "escrow_release_operation_exec_time": 17200,
+                           "escrow_transfer_operation_exec_time": 19100,
+                           "feed_publish_operation_exec_time": 6200,
+                           "limit_order_cancel_operation_exec_time": 9600,
+                           "limit_order_create_operation_exec_time": 31700,
+                           "limit_order_create2_operation_exec_time": 31700,
+                           "request_account_recovery_operation_exec_time": 54400,
+                           "set_withdraw_vesting_route_operation_exec_time": 17900,
+                           "transfer_from_savings_operation_exec_time": 17500,
+                           "transfer_operation_exec_time": 9600,
+                           "transfer_to_savings_operation_exec_time": 6400,
+                           "transfer_to_vesting_operation_exec_time": 44400,
+                           "vote_operation_exec_time": 26500,
+                           "withdraw_vesting_operation_exec_time": 10400,
+                           "witness_set_properties_operation_exec_time": 9500,
+                           "witness_update_operation_exec_time": 9500}
+
 operation_exec_info = {}
diff --git a/beem/rc.py b/beem/rc.py
index 746429fc..fa6df933 100644
--- a/beem/rc.py
+++ b/beem/rc.py
@@ -6,7 +6,7 @@ from __future__ import unicode_literals
 import logging
 import json
 from .instance import shared_steem_instance
-from beem.constants import state_object_size_info
+from beem.constants import state_object_size_info, resource_execution_time, EXEC_FOLLOW_CUSTOM_OP_SCALE
 import hashlib
 from binascii import hexlify, unhexlify
 import os
@@ -42,13 +42,14 @@ class RC(object):
         tx_size = len(txWire)
         return tx_size
 
-    def get_resource_count(self, tx_size, state_bytes_count=0, new_account_op_count=0, market_op_count=0):
+    def get_resource_count(self, tx_size, execution_time_count, state_bytes_count=0, new_account_op_count=0, market_op_count=0):
         """Creates the resource_count dictionary based on tx_size, state_bytes_count, new_account_op_count and market_op_count"""
         resource_count = {"resource_history_bytes": tx_size}
         resource_count["resource_state_bytes"] = state_object_size_info["transaction_object_base_size"]
         resource_count["resource_state_bytes"] += state_object_size_info["transaction_object_byte_size"] * tx_size
         resource_count["resource_state_bytes"] += state_bytes_count
         resource_count["resource_new_accounts"] = new_account_op_count
+        resource_count["resource_execution_time"] = execution_time_count
         if market_op_count > 0:
             resource_count["resource_market_bytes"] = tx_size
         return resource_count
@@ -83,7 +84,8 @@ class RC(object):
         state_bytes_count = state_object_size_info["comment_object_base_size"]
         state_bytes_count += state_object_size_info["comment_object_permlink_char_size"] * permlink_length
         state_bytes_count += state_object_size_info["comment_object_parent_permlink_char_size"] * parent_permlink_length
-        resource_count = self.get_resource_count(tx_size, state_bytes_count)
+        execution_time_count = resource_execution_time["comment_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, state_bytes_count)
         return self.steem.get_rc_cost(resource_count)
 
     def vote_dict(self, vote_dict):
@@ -110,7 +112,8 @@ class RC(object):
     def vote(self, tx_size=210):
         """Calc RC for a vote"""
         state_bytes_count = state_object_size_info["comment_vote_object_base_size"]
-        resource_count = self.get_resource_count(tx_size, state_bytes_count)
+        execution_time_count = resource_execution_time["vote_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, state_bytes_count)
         return self.steem.get_rc_cost(resource_count)
 
     def transfer_dict(self, transfer_dict):
@@ -139,7 +142,8 @@ class RC(object):
 
     def transfer(self, tx_size=290, market_op_count=1):
         """Calc RC of a transfer"""
-        resource_count = self.get_resource_count(tx_size, market_op_count=market_op_count)
+        execution_time_count = resource_execution_time["transfer_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, market_op_count=market_op_count)
         return self.steem.get_rc_cost(resource_count)
 
     def custom_json_dict(self, custom_json_dict):
@@ -168,20 +172,57 @@ class RC(object):
         """
         op = operations.Custom_json(**custom_json_dict)
         tx_size = self.get_tx_size(op)
-        return self.custom_json(tx_size=tx_size)
-
-    def custom_json(self, tx_size=444):
-        resource_count = self.get_resource_count(tx_size)
+        follow_id = custom_json_dict["id"] == "follow"
+        return self.custom_json(tx_size=tx_size, follow_id=follow_id)
+
+    def custom_json(self, tx_size=444, follow_id=False):
+        execution_time_count = resource_execution_time["custom_json_operation_exec_time"]
+        if follow_id:
+            execution_time_count *= EXEC_FOLLOW_CUSTOM_OP_SCALE
+        resource_count = self.get_resource_count(tx_size, execution_time_count)
         return self.steem.get_rc_cost(resource_count)
 
     def account_update_dict(self, account_update_dict):
         """Calc RC costs for account update"""
         op = operations.Account_update(**account_update_dict)
         tx_size = self.get_tx_size(op)
-        resource_count = self.get_resource_count(tx_size)
+        execution_time_count = resource_execution_time["account_update_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count)
         return self.steem.get_rc_cost(resource_count)
 
     def claim_account(self, tx_size=300):
         """Claim account"""
-        resource_count = self.get_resource_count(tx_size, new_account_op_count=1)
+        execution_time_count = resource_execution_time["claim_account_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, new_account_op_count=1)
+        return self.steem.get_rc_cost(resource_count)
+
+    def get_authority_byte_count(self, auth):
+        return (state_object_size_info["authority_base_size"]
+                + state_object_size_info["authority_account_member_size"] * len(auth["account_auths"])
+                + state_object_size_info["authority_key_member_size"] * len(auth["key_auths"]))
+
+    def account_create_dict(self, account_create_dict):
+        """Calc RC costs for account create"""
+        op = operations.Account_create(**account_create_dict)
+        state_bytes_count = state_object_size_info["account_object_base_size"]
+        state_bytes_count += state_object_size_info["account_authority_object_base_size"]
+        state_bytes_count += self.get_authority_byte_count(account_create_dict["owner"])
+        state_bytes_count += self.get_authority_byte_count(account_create_dict["active"])
+        state_bytes_count += self.get_authority_byte_count(account_create_dict["posting"])
+        tx_size = self.get_tx_size(op)
+        execution_time_count = resource_execution_time["account_update_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, state_bytes_count)
+        return self.steem.get_rc_cost(resource_count)
+
+    def create_claimed_account_dict(self, create_claimed_account_dict):
+        """Calc RC costs for claimed account create"""
+        op = operations.Create_claimed_account(**create_claimed_account_dict)
+        state_bytes_count = state_object_size_info["account_object_base_size"]
+        state_bytes_count += state_object_size_info["account_authority_object_base_size"]
+        state_bytes_count += self.get_authority_byte_count(create_claimed_account_dict["owner"])
+        state_bytes_count += self.get_authority_byte_count(create_claimed_account_dict["active"])
+        state_bytes_count += self.get_authority_byte_count(create_claimed_account_dict["posting"])
+        tx_size = self.get_tx_size(op)
+        execution_time_count = resource_execution_time["account_update_operation_exec_time"]
+        resource_count = self.get_resource_count(tx_size, execution_time_count, state_bytes_count)
         return self.steem.get_rc_cost(resource_count)
diff --git a/beem/version.py b/beem/version.py
index 95450f79..7e4d04f4 100644
--- a/beem/version.py
+++ b/beem/version.py
@@ -1,2 +1,2 @@
 """THIS FILE IS GENERATED FROM beem SETUP.PY."""
-version = '0.20.9'
+version = '0.20.10'
diff --git a/beem/witness.py b/beem/witness.py
index 3fd38fc9..810e89c0 100644
--- a/beem/witness.py
+++ b/beem/witness.py
@@ -188,7 +188,12 @@ class Witness(BlockchainObject):
 class WitnessesObject(list):
     def printAsTable(self, sort_key="votes", reverse=True, return_str=False, **kwargs):
         utc = pytz.timezone('UTC')
-        table_header = ["Name", "Votes [PV]", "Disabled", "Missed", "Feed base", "Feed quote", "Feed update", "Fee", "Size", "Interest", "Version"]
+        no_feed = False
+        if len(self) > 0 and "sbd_exchange_rate" not in self[0]:
+            table_header = ["Name", "Votes [PV]", "Disabled", "Missed", "Fee", "Size", "Version"]
+            no_feed = True
+        else:
+            table_header = ["Name", "Votes [PV]", "Disabled", "Missed", "Feed base", "Feed quote", "Feed update", "Fee", "Size", "Interest", "Version"]
         t = PrettyTable(table_header)
         t.align = "l"
         if sort_key == 'base':
@@ -208,21 +213,31 @@ class WitnessesObject(list):
         else:
             sortedList = sorted(self, key=lambda self: self[sort_key], reverse=reverse)
         for witness in sortedList:
-            td = utc.localize(datetime.utcnow()) - witness['last_sbd_exchange_update']
             disabled = ""
             if not witness.is_active:
                 disabled = "yes"
-            t.add_row([witness['owner'],
-                       str(round(int(witness['votes']) / 1e15, 2)),
-                       disabled,
-                       str(witness['total_missed']),
-                       str(Amount(witness['sbd_exchange_rate']['base'], steem_instance=self.steem)),
-                       str(Amount(witness['sbd_exchange_rate']['quote'], steem_instance=self.steem)),
-                       str(td.days) + " days " + str(td.seconds // 3600) + ":" + str((td.seconds // 60) % 60),
-                       str(witness['props']['account_creation_fee']),
-                       str(witness['props']['maximum_block_size']),
-                       str(witness['props']['sbd_interest_rate'] / 100) + " %",
-                       witness['running_version']])
+
+            if no_feed:
+                t.add_row([witness['owner'],
+                           str(round(int(witness['votes']) / 1e15, 2)),
+                           disabled,
+                           str(witness['total_missed']),
+                           str(witness['props']['account_creation_fee']),
+                           str(witness['props']['maximum_block_size']),
+                           witness['running_version']])
+            else:
+                td = utc.localize(datetime.utcnow()) - witness['last_sbd_exchange_update']
+                t.add_row([witness['owner'],
+                           str(round(int(witness['votes']) / 1e15, 2)),
+                           disabled,
+                           str(witness['total_missed']),
+                           str(Amount(witness['sbd_exchange_rate']['base'], steem_instance=self.steem)),
+                           str(Amount(witness['sbd_exchange_rate']['quote'], steem_instance=self.steem)),
+                           str(td.days) + " days " + str(td.seconds // 3600) + ":" + str((td.seconds // 60) % 60),
+                           str(witness['props']['account_creation_fee']),
+                           str(witness['props']['maximum_block_size']),
+                           str(witness['props']['sbd_interest_rate'] / 100) + " %",
+                           witness['running_version']])
         if return_str:
             return t.get_string(**kwargs)
         else:
diff --git a/beemapi/version.py b/beemapi/version.py
index 95450f79..7e4d04f4 100644
--- a/beemapi/version.py
+++ b/beemapi/version.py
@@ -1,2 +1,2 @@
 """THIS FILE IS GENERATED FROM beem SETUP.PY."""
-version = '0.20.9'
+version = '0.20.10'
diff --git a/beembase/operations.py b/beembase/operations.py
index 128a00d6..89dcb2ab 100644
--- a/beembase/operations.py
+++ b/beembase/operations.py
@@ -670,13 +670,21 @@ class Claim_reward_balance(GrapheneObject):
         if len(args) == 1 and len(kwargs) == 0:
             kwargs = args[0]
         prefix = kwargs.get("prefix", default_prefix)
-        super(Claim_reward_balance, self).__init__(
-            OrderedDict([
-                ('account', String(kwargs["account"])),
-                ('reward_steem', Amount(kwargs["reward_steem"], prefix=prefix)),
-                ('reward_sbd', Amount(kwargs["reward_sbd"], prefix=prefix)),
-                ('reward_vests', Amount(kwargs["reward_vests"], prefix=prefix)),
-            ]))
+        if "reward_sbd" in kwargs:
+            super(Claim_reward_balance, self).__init__(
+                OrderedDict([
+                    ('account', String(kwargs["account"])),
+                    ('reward_steem', Amount(kwargs["reward_steem"], prefix=prefix)),
+                    ('reward_sbd', Amount(kwargs["reward_sbd"], prefix=prefix)),
+                    ('reward_vests', Amount(kwargs["reward_vests"], prefix=prefix)),
+                ]))
+        else:
+            super(Claim_reward_balance, self).__init__(
+                OrderedDict([
+                    ('account', String(kwargs["account"])),
+                    ('reward_steem', Amount(kwargs["reward_steem"], prefix=prefix)),
+                    ('reward_vests', Amount(kwargs["reward_vests"], prefix=prefix)),
+                ]))
 
 
 class Transfer_to_savings(GrapheneObject):
diff --git a/beembase/version.py b/beembase/version.py
index 95450f79..7e4d04f4 100644
--- a/beembase/version.py
+++ b/beembase/version.py
@@ -1,2 +1,2 @@
 """THIS FILE IS GENERATED FROM beem SETUP.PY."""
-version = '0.20.9'
+version = '0.20.10'
diff --git a/beemgraphenebase/chains.py b/beemgraphenebase/chains.py
index 28db4246..372467f3 100644
--- a/beemgraphenebase/chains.py
+++ b/beemgraphenebase/chains.py
@@ -70,7 +70,6 @@ known_chains = {
         "min_version": "0.0.0",
         "prefix": "VIT",
         "chain_assets": [
-            {"asset": "SBD", "symbol": "VBD", "precision": 3, "id": 0},
             {"asset": "STEEM", "symbol": "VIT", "precision": 3, "id": 1},
             {"asset": "VESTS", "symbol": "VESTS", "precision": 6, "id": 2}
         ],
diff --git a/beemgraphenebase/version.py b/beemgraphenebase/version.py
index 95450f79..7e4d04f4 100644
--- a/beemgraphenebase/version.py
+++ b/beemgraphenebase/version.py
@@ -1,2 +1,2 @@
 """THIS FILE IS GENERATED FROM beem SETUP.PY."""
-version = '0.20.9'
+version = '0.20.10'
diff --git a/setup.py b/setup.py
index 837fb2ce..68228cbc 100755
--- a/setup.py
+++ b/setup.py
@@ -16,7 +16,7 @@ except LookupError:
     ascii = codecs.lookup('ascii')
     codecs.register(lambda name, enc=ascii: {True: enc}.get(name == 'mbcs'))
 
-VERSION = '0.20.9'
+VERSION = '0.20.10'
 
 tests_require = ['mock >= 2.0.0', 'pytest', 'pytest-mock', 'parameterized']
 
@@ -90,6 +90,7 @@ if __name__ == '__main__':
             'Programming Language :: Python :: 3.4',
             'Programming Language :: Python :: 3.5',
             'Programming Language :: Python :: 3.6',
+            'Programming Language :: Python :: 3.7',
             'Development Status :: 4 - Beta',
             'Intended Audience :: Developers',
             'Intended Audience :: Financial and Insurance Industry',
-- 
GitLab