From acbb397571ca2ead503a4adabab5259cbd2c1223 Mon Sep 17 00:00:00 2001
From: Holger Nahrstaedt <holger@nahrstaedt.de>
Date: Sun, 25 Feb 2018 10:23:32 +0100
Subject: [PATCH] Account: print_info added voting_power, steem_power,
 get_voting_value_SBD, get_recharge_time, get_followers, get_following,
 get_bandwidth, interest added The following operations were added: unfollow,
 follow, update_account_profile, approvewitness, disapprovewitness,
 update_memo_key, transfer, transfer_to_vesting, convert, transfer_to_savings,
 transfer_from_savings, transfer_from_savings_cancel, claim_reward_balance,
 delegate_vesting_shares, withdraw_vesting Comment: The following operations
 were added: upvote, downvote, vote, edit, reply, comment_options, post,
 resteem Discussion all possible discussion_by_... functions were added.
 Market volume24h, orderbook, trades, market_history, accountopenorders fixed
 or added buy, sell, cancel fixed steem register_api refresh_data added
 get_dynamic_global_properties, get_feed_history, get_reward_fund,
 get_current_median_history_price, get_next_scheduled_hardfork,
 get_hardfork_version, get_network, get_state, get_config  moved from
 blockchain steem_per_mvest, vests_to_sp, sp_to_vests, sp_to_sbd,
 sp_to_rshares, get_median_price, get_payout_from_rshares custom_json added
 utils make_patch added witness feed_publish and update added

---
 .flake8                    |   1 +
 .travis.yml                |   4 +
 beem/account.py            | 600 ++++++++++++++++++++++++++++++++++++-
 beem/amount.py             |   2 +-
 beem/asset.py              |   3 -
 beem/blockchain.py         |  63 +---
 beem/comment.py            | 403 ++++++++++++++++++++++++-
 beem/discussions.py        | 227 ++++++++++++++
 beem/exceptions.py         |   6 +
 beem/market.py             | 360 ++++++++++------------
 beem/steem.py              | 360 +++++++++++++++-------
 beem/transactionbuilder.py |   2 +-
 beem/utils.py              |  46 +++
 beem/wallet.py             |  23 +-
 beem/witness.py            |  85 ++++++
 beembase/operations.py     |   2 +-
 docs/index.rst             |  16 +-
 docs/tutorials.rst         |  10 +-
 setup.py                   |   2 +-
 tests/test_steem.py        |  19 +-
 tests/test_utils.py        |  12 +-
 tox.ini                    |   3 +-
 22 files changed, 1830 insertions(+), 419 deletions(-)

diff --git a/.flake8 b/.flake8
index 5a532da5..393dc2fc 100644
--- a/.flake8
+++ b/.flake8
@@ -6,6 +6,7 @@ ignore =
     E129,E501,F401,E722, E122
 exclude =
     .git,
+	.eggs,
     __pycache__,
     docs/conf.py,
     old,
diff --git a/.travis.yml b/.travis.yml
index 5f3756b9..a57c0e23 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,6 +15,10 @@ matrix:
         - pip install flake8
       script:
         - flake8
+    - os: linux
+      python: 3.4
+      env:
+        - TOXENV=py34
     - os: linux
       python: 3.5
       env:
diff --git a/beem/account.py b/beem/account.py
index 63f79b4e..57616ffc 100644
--- a/beem/account.py
+++ b/beem/account.py
@@ -2,9 +2,13 @@ from beem.instance import shared_steem_instance
 from .exceptions import AccountDoesNotExistsException
 from .blockchainobject import BlockchainObject
 from .utils import formatTimeString
-from datetime import datetime
+from beem.amount import Amount
+from datetime import datetime, timedelta
+from beembase import operations
+from beembase.account import PrivateKey, PublicKey
 import json
 import math
+import random
 
 
 class Account(BlockchainObject):
@@ -95,6 +99,23 @@ class Account(BlockchainObject):
     def rep(self):
         return self.reputation()
 
+    def print_info(self, force_refresh=False, return_str=False):
+        if force_refresh:
+            self.refresh()
+            self.steem.refresh_data(True)
+        ret = self.name + " (" + str(round(self.rep, 3)) + ") "
+        ret += str(self.voting_power()) + "%, full in " + self.get_recharge_time_str()
+        ret += " VP = " + str(self.get_voting_value_SBD()) + "$\n"
+        ret += str(round(self.steem_power(), 2)) + " SP, "
+        ret += str(self.balances["available"][0]) + ", " + str(self.balances["available"][1])
+        bandwidth = self.get_bandwidth()
+        ret += "\n"
+        ret += "Remaining Bandwidth: " + str(round(100 - bandwidth["used"] / bandwidth["allocated"] * 100, 2)) + " %"
+        ret += " (" + str(round(bandwidth["used"] / 1024)) + " kb of " + str(round(bandwidth["allocated"] / 1024 / 1024)) + " mb)"
+        if return_str:
+            return ret
+        print(ret)
+
     def reputation(self, precision=2):
         rep = int(self['reputation'])
         if rep == 0:
@@ -107,6 +128,78 @@ class Account(BlockchainObject):
         else:
             return score
 
+    def voting_power(self, precision=2, with_regeneration=True):
+        if with_regeneration:
+            diff_in_seconds = (datetime.utcnow() - formatTimeString(self["last_vote_time"])).total_seconds()
+            regenerated_vp = diff_in_seconds * 10000 / 86400 / 5 / 100
+        else:
+            regenerated_vp = 0
+        total_vp = (self["voting_power"] / 100 + regenerated_vp)
+        if total_vp > 100:
+            return 100
+        if total_vp < 0:
+            return 0
+        if precision is not None:
+            return round(total_vp, precision)
+        else:
+            return total_vp
+
+    def steem_power(self, onlyOwnSP=False):
+        if onlyOwnSP:
+            vests = Amount(self["vesting_shares"])
+        else:
+            vests = Amount(self["vesting_shares"]) - Amount(self["delegated_vesting_shares"]) + Amount(self["received_vesting_shares"])
+        return self.steem.vests_to_sp(vests)
+
+    def get_voting_value_SBD(self, voting_weight=100, voting_power=None, steem_power=None, precision=2):
+
+        if voting_power is None:
+            voting_power = self.voting_power()
+        if steem_power is None:
+            sp = self.steem_power()
+        else:
+            sp = steem_power
+
+        VoteValue = self.steem.sp_to_sbd(sp, voting_power=voting_power * 100, vote_pct=voting_weight * 100)
+        return round(VoteValue, precision)
+
+    def get_recharge_time_str(self, voting_power_goal=100):
+        hours = math.floor(self.get_recharge_hours(voting_power_goal=voting_power_goal, precision=3))
+        minutes = math.floor(self.get_recharge_reminder_minutes(voting_power_goal=voting_power_goal, precision=0))
+        return str(hours) + ":" + str(minutes).zfill(2)
+
+    def get_recharge_hours(self, voting_power_goal=100, precision=2):
+        missing_vp = voting_power_goal - self.voting_power(precision=10)
+        if missing_vp < 0:
+            return 0
+        recharge_seconds = missing_vp * 100 * 5 * 86400 / 10000
+        return round(missing_vp * recharge_seconds / 60 / 60, precision)
+
+    def get_recharge_reminder_minutes(self, voting_power_goal=100, precision=0):
+        hours = self.get_recharge_hours(voting_power_goal=voting_power_goal, precision=5)
+        reminder_in_minuts = (hours - math.floor(hours)) * 60
+        return round(reminder_in_minuts, precision)
+
+    def get_followers(self):
+        return [
+            x['follower'] for x in self._get_followers(direction="follower")
+        ]
+
+    def get_following(self):
+        return [
+            x['following'] for x in self._get_followers(direction="following")
+        ]
+
+    def _get_followers(self, direction="follower", last_user=""):
+        if direction == "follower":
+            followers = self.steem.rpc.get_followers(self.name, last_user, "blog", 100, api='follow')
+        elif direction == "following":
+            followers = self.steem.rpc.get_following(self.name, last_user, "blog", 100, api='follow')
+        if len(followers) >= 100:
+            followers += self._get_followers(
+                direction=direction, last_user=followers[-1][direction])[1:]
+        return followers
+
     @property
     def available_balances(self):
         """ List balances of an account. This call returns instances of
@@ -181,6 +274,25 @@ class Account(BlockchainObject):
                 return b
         return Amount(0, symbol)
 
+    def interest(self):
+        """ Caluclate interest for an account
+            :param str account: Account name to get interest for
+        """
+        last_payment = formatTimeString(self["sbd_last_interest_payment"])
+        next_payment = last_payment + timedelta(days=30)
+        interest_rate = self.steem.get_dynamic_global_properties()[
+            "sbd_interest_rate"] / 100  # percent
+        interest_amount = (interest_rate / 100) * int(
+            int(self["sbd_seconds"]) / (60 * 60 * 24 * 356)) * 10**-3
+
+        return {
+            "interest": interest_amount,
+            "last_payment": last_payment,
+            "next_payment": next_payment,
+            "next_payment_duration": next_payment - datetime.now(),
+            "interest_rate": interest_rate,
+        }
+
     @property
     def is_fully_loaded(self):
         """ Is this instance fully loaded / e.g. all data available?
@@ -192,6 +304,60 @@ class Account(BlockchainObject):
             self.full = True
             self.refresh()
 
+    def get_bandwidth(self, bandwidth_type=1, account=None, raw_data=False):
+        """ get_account_bandwidth """
+        if account is None:
+            account = self["name"]
+        if raw_data:
+            return self.steem.rpc.get_account_bandwidth(account, bandwidth_type)
+        else:
+            global_properties = self.steem.get_dynamic_global_properties()
+            received_vesting_shares = Amount(self["received_vesting_shares"]).amount
+            vesting_shares = Amount(self["vesting_shares"]).amount
+            max_virtual_bandwidth = float(global_properties["max_virtual_bandwidth"])
+            total_vesting_shares = Amount(global_properties["total_vesting_shares"]).amount
+            allocated_bandwidth = (max_virtual_bandwidth * (vesting_shares + received_vesting_shares) / total_vesting_shares)
+            allocated_bandwidth = round(allocated_bandwidth / 1000000)
+
+            total_seconds = 604800
+            date_bandwidth = formatTimeString(self["last_bandwidth_update"])
+            seconds_since_last_update = datetime.utcnow() - date_bandwidth
+            seconds_since_last_update = seconds_since_last_update.total_seconds()
+            average_bandwidth = float(self["average_bandwidth"])
+            used_bandwidth = 0
+            if seconds_since_last_update < total_seconds:
+                used_bandwidth = (((total_seconds - seconds_since_last_update) * average_bandwidth) / total_seconds)
+            used_bandwidth = round(used_bandwidth / 1000000)
+
+            return {"used": used_bandwidth,
+                    "allocated": allocated_bandwidth}
+            # print("bandwidth percent used: " + str(100 * used_bandwidth / allocated_bandwidth))
+            # print("bandwidth percent remaining: " + str(100 - (100 * used_bandwidth / allocated_bandwidth)))
+
+    def get_owner_history(self, account=None):
+        """ get_owner_history """
+        if account is None:
+            account = self["name"]
+        return self.steem.rpc.get_owner_history(account)
+
+    def get_recovery_request(self, account=None):
+        """ get_recovery_request """
+        if account is None:
+            account = self["name"]
+        return self.steem.rpc.get_recovery_request(account)
+
+    def get_follow_count(self, account=None):
+        """ get_follow_count """
+        if account is None:
+            account = self["name"]
+        return self.steem.rpc.get_follow_count(account, api="follow")
+
+    def verify_account_authority(self, keys, account=None):
+        """ verify_account_authority """
+        if account is None:
+            account = self["name"]
+        return self.steem.rpc.verify_account_authority(account, keys)
+
     def history(
         self, limit=100,
         only_ops=[], exclude_ops=[]
@@ -253,3 +419,435 @@ class Account(BlockchainObject):
                 break
             if first < _limit:
                 _limit = first - 1
+
+    def unfollow(self, unfollow, what=["blog"], account=None):
+        """ Unfollow another account's blog
+            :param str unfollow: Follow this account
+            :param list what: List of states to follow
+                (defaults to ``['blog']``)
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        # FIXME: removing 'blog' from the array requires to first read
+        # the follow.what from the blockchain
+        return self.follow(unfollow, what=[], account=account)
+
+    def follow(self, follow, what=["blog"], account=None):
+        """ Follow another account's blog
+            :param str follow: Follow this account
+            :param list what: List of states to follow
+                (defaults to ``['blog']``)
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if not account:
+            account = self["name"]
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        json_body = [
+            'follow', {
+                'follower': account,
+                'following': follow,
+                'what': what
+            }
+        ]
+        return self.steem.custom_json(
+            id="follow", json=json_body, required_posting_auths=[account])
+
+    def update_account_profile(self, profile, account=None):
+        """ Update an account's meta data (json_meta)
+            :param dict json: The meta data to use (i.e. use Profile() from
+                account.py)
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if not account:
+            account = self["name"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        op = operations.Account_update(
+            **{
+                "account": account["name"],
+                "memo_key": account["memo_key"],
+                "json_metadata": profile
+            })
+        return self.steem.finalizeOp(op, account["name"], "active")
+
+    # -------------------------------------------------------------------------
+    #  Approval and Disapproval of witnesses
+    # -------------------------------------------------------------------------
+    def approvewitness(self, witness, account=None, approve=True, **kwargs):
+        """ Approve a witness
+
+            :param list witnesses: list of Witness name or id
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if not account:
+            account = self["name"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+
+        # if not isinstance(witnesses, (list, set, tuple)):
+        #     witnesses = {witnesses}
+
+        # for witness in witnesses:
+        #     witness = Witness(witness, steem_instance=self)
+
+        op = operations.Account_witness_vote(**{
+            "account": account["name"],
+            "witness": witness,
+            "approve": approve
+        })
+        return self.steem.finalizeOp(op, account["name"], "active", **kwargs)
+
+    def disapprovewitness(self, witness, account=None, **kwargs):
+        """ Disapprove a witness
+
+            :param list witnesses: list of Witness name or id
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        return self.approvewitness(
+            witness=witness, account=account, approve=False)
+
+    def update_memo_key(self, key, account=None, **kwargs):
+        """ Update an account's memo public key
+
+            This method does **not** add any private keys to your
+            wallet but merely changes the memo public key.
+
+            :param str key: New memo public key
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        PublicKey(key, prefix=self.steem.prefix)
+
+        account = Account(account, steem_instance=self.steem)
+        account["memo_key"] = key
+        op = operations.Account_update(**{
+            "account": account["name"],
+            "memo_key": account["memo_key"],
+            "json_metadata": account["json_metadata"]
+        })
+        return self.steem.finalizeOp(op, account["name"], "active", **kwargs)
+
+    # -------------------------------------------------------------------------
+    # Simple Transfer
+    # -------------------------------------------------------------------------
+    def transfer(self, to, amount, asset, memo="", account=None, **kwargs):
+        """ Transfer an asset to another account.
+
+            :param str to: Recipient
+            :param float amount: Amount to transfer
+            :param str asset: Asset to transfer
+            :param str memo: (optional) Memo, may begin with `#` for encrypted
+                messaging
+            :param str account: (optional) the source account for the transfer
+                if not ``default_account``
+        """
+        from .memo import Memo
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        account = Account(account, steem_instance=self.steem)
+        amount = Amount(amount, asset, steem_instance=self.steem)
+        to = Account(to, steem_instance=self.steem)
+
+        memoObj = Memo(
+            from_account=account,
+            to_account=to,
+            steem_instance=self.steem
+        )
+        op = operations.Transfer(**{
+            "amount": amount,
+            "to": to["name"],
+            "memo": memoObj.encrypt(memo),
+            "from": account["name"],
+        })
+        return self.steem.finalizeOp(op, account, "active", **kwargs)
+
+    def transfer_to_vesting(self, amount, to=None, account=None, **kwargs):
+        """ Vest STEEM
+
+            :param float amount: Amount to transfer
+            :param str to: Recipient (optional) if not set equal to account
+            :param str account: (optional) the source account for the transfer
+                if not ``default_account``
+        """
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+        if not to:
+            to = account  # powerup on the same account
+        account = Account(account, steem_instance=self.steem)
+        if isinstance(amount, str):
+            amount = Amount(amount, steem_instance=self.steem)
+        elif isinstance(amount, Amount):
+            amount = Amount(amount, steem_instance=self.steem)
+        else:
+            amount = Amount(amount, "STEEM", steem_instance=self.steem)
+        assert amount["symbol"] == "STEEM"
+        to = Account(to, steem_instance=self.steem)
+
+        op = operations.Transfer_to_vesting(**{
+            "from": account["name"],
+            "to": to["name"],
+            "amount": amount,
+        })
+        return self.steem.finalizeOp(op, account, "active", **kwargs)
+
+    def convert(self, amount, account=None, request_id=None):
+        """ Convert SteemDollars to Steem (takes one week to settle)
+            :param float amount: number of VESTS to withdraw
+            :param str account: (optional) the source account for the transfer
+            if not ``default_account``
+            :param str request_id: (optional) identifier for tracking the
+            conversion`
+        """
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if isinstance(amount, str):
+            amount = Amount(amount, steem_instance=self.steem)
+        elif isinstance(amount, Amount):
+            amount = Amount(amount, steem_instance=self.steem)
+        else:
+            amount = Amount(amount, "SBD", steem_instance=self.steem)
+        assert amount["symbol"] == "SBD"
+        if request_id:
+            request_id = int(request_id)
+        else:
+            request_id = random.getrandbits(32)
+        op = operations.Convert(
+            **{
+                "owner": account["name"],
+                "requestid": request_id,
+                "amount": amount
+            })
+
+        return self.steem.finalizeOp(op, account, "active")
+
+    def transfer_to_savings(self, amount, asset, memo, to=None, account=None):
+        """ Transfer SBD or STEEM into a 'savings' account.
+            :param float amount: STEEM or SBD amount
+            :param float asset: 'STEEM' or 'SBD'
+            :param str memo: (optional) Memo
+            :param str to: (optional) the source account for the transfer if
+            not ``default_account``
+            :param str account: (optional) the source account for the transfer
+            if not ``default_account``
+        """
+        assert asset in ['STEEM', 'SBD']
+
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        amount = Amount(amount, asset, steem_instance=self.steem)
+        if not to:
+            to = account  # move to savings on same account
+
+        op = operations.Transfer_to_savings(
+            **{
+                "from": account["name"],
+                "to": to["name"],
+                "amount": amount,
+                "memo": memo,
+            })
+        return self.steem.finalizeOp(op, account, "active")
+
+    def transfer_from_savings(self,
+                              amount,
+                              asset,
+                              memo,
+                              request_id=None,
+                              to=None,
+                              account=None):
+        """ Withdraw SBD or STEEM from 'savings' account.
+            :param float amount: STEEM or SBD amount
+            :param float asset: 'STEEM' or 'SBD'
+            :param str memo: (optional) Memo
+            :param str request_id: (optional) identifier for tracking or
+            cancelling the withdrawal
+            :param str to: (optional) the source account for the transfer if
+            not ``default_account``
+            :param str account: (optional) the source account for the transfer
+            if not ``default_account``
+        """
+        assert asset in ['STEEM', 'SBD']
+
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if not to:
+            to = account  # move to savings on same account
+        amount = Amount(amount, asset, steem_instance=self.steem)
+        if request_id:
+            request_id = int(request_id)
+        else:
+            request_id = random.getrandbits(32)
+
+        op = operations.Transfer_from_savings(
+            **{
+                "from": account["name"],
+                "request_id": request_id,
+                "to": to["name"],
+                "amount": amount,
+                "memo": memo,
+            })
+        return self.steem.finalizeOp(op, account, "active")
+
+    def transfer_from_savings_cancel(self, request_id, account=None):
+        """ Cancel a withdrawal from 'savings' account.
+            :param str request_id: Identifier for tracking or cancelling
+            the withdrawal
+            :param str account: (optional) the source account for the transfer
+            if not ``default_account``
+        """
+        if not account:
+            account = self
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        op = operations.Cancel_transfer_from_savings(**{
+            "from": account["name"],
+            "request_id": request_id,
+        })
+        return self.steem.finalizeOp(op, account, "active")
+
+    def claim_reward_balance(self,
+                             reward_steem='0 STEEM',
+                             reward_sbd='0 SBD',
+                             reward_vests='0 VESTS',
+                             account=None):
+        """ Claim reward balances.
+        By default, this will claim ``all`` outstanding balances. To bypass
+        this behaviour, set desired claim amount by setting any of
+        `reward_steem`, `reward_sbd` or `reward_vests`.
+        Args:
+            reward_steem (string): Amount of STEEM you would like to claim.
+            reward_sbd (string): Amount of SBD you would like to claim.
+            reward_vests (string): Amount of VESTS you would like to claim.
+            account (string): The source account for the claim if not
+            ``default_account`` is used.
+        """
+        if not account:
+            account = self
+        else:
+            account = Account(account, steem_instance=self.steem)
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        # if no values were set by user, claim all outstanding balances on
+        # account
+        if isinstance(reward_steem, str):
+            reward_steem = Amount(reward_steem, steem_instance=self.steem)
+        elif isinstance(reward_steem, Amount):
+            reward_steem = Amount(reward_steem, steem_instance=self.steem)
+        else:
+            reward_steem = Amount(reward_steem, "STEEM", steem_instance=self.steem)
+        assert reward_steem["symbol"] == "STEEM"
+
+        if isinstance(reward_sbd, str):
+            reward_sbd = Amount(reward_sbd, steem_instance=self.steem)
+        elif isinstance(reward_sbd, Amount):
+            reward_sbd = Amount(reward_sbd, steem_instance=self.steem)
+        else:
+            reward_sbd = Amount(reward_sbd, "SBD", steem_instance=self.steem)
+        assert reward_sbd["symbol"] == "SBD"
+
+        if isinstance(reward_vests, str):
+            reward_vests = Amount(reward_vests, steem_instance=self.steem)
+        elif isinstance(reward_vests, Amount):
+            reward_vests = Amount(reward_vests, steem_instance=self.steem)
+        else:
+            reward_vests = Amount(reward_vests, "VESTS", steem_instance=self.steem)
+        assert reward_vests["symbol"] == "VESTS"
+        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]
+
+        op = operations.Claim_reward_balance(
+            **{
+                "account": account["name"],
+                "reward_steem": reward_steem,
+                "reward_sbd": reward_sbd,
+                "reward_vests": reward_vests,
+            })
+        return self.steem.finalizeOp(op, account, "posting")
+
+    def delegate_vesting_shares(self, to_account, vesting_shares,
+                                account=None):
+        """ Delegate SP to another account.
+        Args:
+            to_account (string): Account we are delegating shares to
+            (delegatee).
+            vesting_shares (string): Amount of VESTS to delegate eg. `10000
+            VESTS`.
+            account (string): The source account (delegator). If not specified,
+            ``default_account`` is used.
+        """
+        if not account:
+            account = self["name"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if isinstance(vesting_shares, str):
+            vesting_shares = Amount(vesting_shares, steem_instance=self.steem)
+        elif isinstance(vesting_shares, Amount):
+            vesting_shares = Amount(vesting_shares, steem_instance=self.steem)
+        else:
+            vesting_shares = Amount(vesting_shares, "VESTS", steem_instance=self.steem)
+        assert vesting_shares["symbol"] == "VESTS"
+        op = operations.Delegate_vesting_shares(
+            **{
+                "delegator": account,
+                "delegatee": to_account,
+                "vesting_shares": vesting_shares,
+            })
+        return self.steem.finalizeOp(op, account, "active")
+
+    def withdraw_vesting(self, amount, account=None):
+        """ Withdraw VESTS from the vesting account.
+            :param float amount: number of VESTS to withdraw over a period of
+            104 weeks
+            :param str account: (optional) the source account for the transfer
+            if not ``default_account``
+    """
+        if not account:
+            account = self["name"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if isinstance(amount, str):
+            amount = Amount(amount, steem_instance=self.steem)
+        elif isinstance(amount, Amount):
+            amount = Amount(amount, steem_instance=self.steem)
+        else:
+            amount = Amount(amount, "VESTS", steem_instance=self.steem)
+        assert amount["symbol"] == "VESTS"
+        op = operations.WithdrawVesting(
+            **{
+                "account": account["name"],
+                "vesting_shares": amount,
+            })
+
+        return self.steem.finalizeOp(op, account, "active")
diff --git a/beem/amount.py b/beem/amount.py
index 8bc950e3..054a0348 100644
--- a/beem/amount.py
+++ b/beem/amount.py
@@ -1,5 +1,5 @@
 from beem.instance import shared_steem_instance
-from .asset import Asset
+from beem.asset import Asset
 
 
 class Amount(dict):
diff --git a/beem/asset.py b/beem/asset.py
index b1f37ec5..09ab2d56 100644
--- a/beem/asset.py
+++ b/beem/asset.py
@@ -1,7 +1,4 @@
 import json
-from beem.account import Account
-from beembase import operations
-
 from .exceptions import AssetDoesNotExistsException
 from .blockchainobject import BlockchainObject
 
diff --git a/beem/blockchain.py b/beem/blockchain.py
index 8dc0b382..897b8a6c 100644
--- a/beem/blockchain.py
+++ b/beem/blockchain.py
@@ -2,6 +2,8 @@ import time
 from .block import Block
 from beem.instance import shared_steem_instance
 from beembase.operationids import getOperationNameForId
+from .amount import Amount
+from datetime import datetime
 
 
 class Blockchain(object):
@@ -39,7 +41,8 @@ class Blockchain(object):
     def __init__(
         self,
         steem_instance=None,
-        mode="irreversible"
+        mode="irreversible",
+        data_refresh_time_seconds=900,
     ):
         self.steem = steem_instance or shared_steem_instance()
 
@@ -50,67 +53,13 @@ class Blockchain(object):
         else:
             raise ValueError("invalid value for 'mode'!")
 
-    def get_dynamic_global_properties(self):
-        """ This call returns the *dynamic global properties*
-        """
-        return self.steem.rpc.get_dynamic_global_properties()
-
-    def get_feed_history(self):
-        """ Returns the feed_history
-        """
-        return self.steem.rpc.get_feed_history()
-
-    def get_current_median_history_price(self):
-        """ Returns the current median price
-        """
-        return self.steem.rpc.get_current_median_history_price()
-
-    def get_next_scheduled_hardfork(self):
-        """ Returns Hardfork and live_time of the hardfork
-        """
-        return self.steem.rpc.get_next_scheduled_hardfork()
-
-    def get_hardfork_version(self):
-        """ Current Hardfork Version as String
-        """
-        return self.steem.rpc.get_hardfork_version()
-
-    def get_network(self):
-        """ Identify the network
-
-            :returns: Network parameters
-            :rtype: dict
-        """
-        return self.steem.rpc.get_network()
-
-    def get_chain_properties(self):
-        """ Return witness elected chain properties
-
-            ::
-                {'account_creation_fee': '30.000 STEEM',
-                 'maximum_block_size': 65536,
-                 'sbd_interest_rate': 250}
-
-        """
-        return self.steem.rpc.get_chain_properties()
-
-    def get_state(self, path="value"):
-        """ get_state
-        """
-        return self.steem.rpc.get_state(path)
-
-    def get_config(self):
-        """ Returns internal chain configuration.
-        """
-        return self.steem.rpc.get_config()
-
     def get_current_block_num(self):
         """ This call returns the current block
 
             .. note:: The block number returned depends on the ``mode`` used
                       when instanciating from this class.
         """
-        return self.get_dynamic_global_properties().get(self.mode)
+        return self.steem.get_dynamic_global_properties(False).get(self.mode)
 
     def get_current_block(self):
         """ This call returns the current block
@@ -155,7 +104,7 @@ class Blockchain(object):
              confirmed by 2/3 of all block producers and is thus irreversible)
         """
         # Let's find out how often blocks are generated!
-        block_interval = self.get_config().get("STEEMIT_BLOCK_INTERVAL")
+        block_interval = self.steem.get_config().get("STEEMIT_BLOCK_INTERVAL")
 
         if not start:
             start = self.get_current_block_num()
diff --git a/beem/comment.py b/beem/comment.py
index 419e2a51..8f8db858 100644
--- a/beem/comment.py
+++ b/beem/comment.py
@@ -1,10 +1,13 @@
 from .instance import shared_steem_instance
 from .account import Account
-from .utils import resolve_authorperm, construct_authorperm
+from .utils import resolve_authorperm, construct_authorperm, derive_permlink, keep_in_dict, make_patch
 from .blockchainobject import BlockchainObject
-from .exceptions import ContentDoesNotExistsException
+from .exceptions import ContentDoesNotExistsException, VotingInvalidOnArchivedPost
+from beembase import operations
 import json
+import re
 import logging
+import difflib
 log = logging.getLogger(__name__)
 
 
@@ -25,7 +28,7 @@ class Comment(BlockchainObject):
         steem_instance=None
     ):
         self.full = full
-        if isinstance(authorperm, str):
+        if isinstance(authorperm, str) and authorperm != "":
             [author, permlink] = resolve_authorperm(authorperm)
             self["author"] = author
             self["permlink"] = permlink
@@ -33,7 +36,7 @@ class Comment(BlockchainObject):
         elif isinstance(authorperm, dict) and "author" in authorperm and "permlink" in authorperm:
             self["author"] = authorperm["author"]
             self["permlink"] = authorperm["permlink"]
-            self["authorperm"] = construct_authorperm(authorperm)
+            self["authorperm"] = construct_authorperm(authorperm["author"], authorperm["permlink"])
         super().__init__(
             authorperm,
             id_item="authorperm",
@@ -41,19 +44,20 @@ class Comment(BlockchainObject):
             full=full,
             steem_instance=steem_instance
         )
+        self.identifier = self["authorperm"]
 
     def refresh(self):
-        [author, permlink] = resolve_authorperm(self.identifier)
-        content = self.steem.rpc.get_content(author, permlink)
+        content = self.steem.rpc.get_content(self["author"], self["permlink"])
         if not content:
             raise ContentDoesNotExistsException
         super(Comment, self).__init__(content, id_item="authorperm", steem_instance=self.steem)
-
-        self.identifier = self.authorperm
+        self["authorperm"] = construct_authorperm(self["author"], self["permlink"])
+        self.identifier = self["authorperm"]
 
     def json(self):
         output = self
-        output.pop("authorperm")
+        if "authorperm" in output:
+            output.pop("authorperm")
         return json.loads(str(json.dumps(output)))
 
     @property
@@ -106,6 +110,387 @@ class Comment(BlockchainObject):
         """
         return self['depth'] > 0
 
+    def upvote(self, weight=+100, voter=None):
+        """ Upvote the post
+            :param float weight: (optional) Weight for posting (-100.0 -
+            +100.0) defaults to +100.0
+            :param str voter: (optional) Voting account
+        """
+        if self.get('net_rshares', None) is None:
+            raise VotingInvalidOnArchivedPost
+        return self.vote(weight, voter=voter)
+
+    def downvote(self, weight=-100, voter=None):
+        """ Downvote the post
+            :param float weight: (optional) Weight for posting (-100.0 -
+            +100.0) defaults to -100.0
+            :param str voter: (optional) Voting account
+        """
+        if self.get('net_rshares', None) is None:
+            raise VotingInvalidOnArchivedPost
+        return self.vote(weight, voter=voter)
+
+    def vote(self, weight, account=None, identifier=None, **kwargs):
+        """ Vote for a post
+            :param str identifier: Identifier for the post to upvote Takes
+                                   the form ``@author/permlink``
+            :param float weight: Voting weight. Range: -100.0 - +100.0. May
+                                 not be 0.0
+            :param str account: Voter to use for voting. (Optional)
+            If ``voter`` is not defines, the ``default_account`` will be taken
+            or a ValueError will be raised
+        """
+        if not account:
+            if "default_account" in self.steem.config:
+                account = self.steem.config["default_account"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self)
+        if not identifier:
+            post_author = self["author"]
+            post_permlink = self["permlink"]
+        else:
+            [post_author, post_permlink] = resolve_authorperm(identifier)
+
+        STEEMIT_100_PERCENT = 10000
+        STEEMIT_1_PERCENT = (STEEMIT_100_PERCENT / 100)
+        vote_weight = int(weight * STEEMIT_1_PERCENT)
+        if vote_weight > STEEMIT_100_PERCENT:
+            vote_weight = STEEMIT_100_PERCENT
+        if vote_weight < STEEMIT_100_PERCENT:
+            vote_weight = -STEEMIT_100_PERCENT
+
+        op = operations.Vote(
+            **{
+                "voter": account["name"],
+                "author": post_author,
+                "permlink": post_permlink,
+                "weight": int(weight * STEEMIT_1_PERCENT)
+            })
+
+        return self.steem.finalizeOp(op, account, "posting", **kwargs)
+
+    def edit(self, body, meta=None, replace=False):
+        """ Edit an existing post
+            :param str body: Body of the reply
+            :param json meta: JSON meta object that can be attached to the
+                              post. (optional)
+            :param bool replace: Instead of calculating a *diff*, replace
+                                 the post entirely (defaults to ``False``)
+        """
+        if not meta:
+            meta = {}
+        original_post = self
+
+        if replace:
+            newbody = body
+        else:
+            newbody = make_patch(original_post["body"], body)
+            if not newbody:
+                log.info("No changes made! Skipping ...")
+                return
+
+        reply_identifier = construct_authorperm(
+            original_post["parent_author"], original_post["parent_permlink"])
+
+        new_meta = {}
+        if meta:
+            if original_post["json_metadata"]:
+                import json
+                new_meta = original_post["json_metadata"].update(meta)
+            else:
+                new_meta = meta
+
+        return self.post(
+            original_post["title"],
+            newbody,
+            reply_identifier=reply_identifier,
+            author=original_post["author"],
+            permlink=original_post["permlink"],
+            json_metadata=new_meta,
+        )
+
+    def reply(self, body, title="", author="", meta=None):
+        """ Reply to an existing post
+            :param str body: Body of the reply
+            :param str title: Title of the reply post
+            :param str author: Author of reply (optional) if not provided
+                               ``default_user`` will be used, if present, else
+                               a ``ValueError`` will be raised.
+            :param json meta: JSON meta object that can be attached to the
+                              post. (optional)
+        """
+        return self.post(
+            title,
+            body,
+            json_metadata=meta,
+            author=author,
+            reply_identifier=self.identifier)
+
+    def post(self,
+             title=None,
+             body=None,
+             author=None,
+             permlink=None,
+             reply_identifier=None,
+             json_metadata=None,
+             comment_options=None,
+             community=None,
+             tags=None,
+             beneficiaries=None,
+             self_vote=False):
+        """ Create a new post.
+        If this post is intended as a reply/comment, `reply_identifier` needs
+        to be set with the identifier of the parent post/comment (eg.
+        `@author/permlink`).
+        Optionally you can also set json_metadata, comment_options and upvote
+        the newly created post as an author.
+        Setting category, tags or community will override the values provided
+        in json_metadata and/or comment_options where appropriate.
+        Args:
+        title (str): Title of the post
+        body (str): Body of the post/comment
+        author (str): Account are you posting from
+        permlink (str): Manually set the permlink (defaults to None).
+            If left empty, it will be derived from title automatically.
+        reply_identifier (str): Identifier of the parent post/comment (only
+            if this post is a reply/comment).
+        json_metadata (str, dict): JSON meta object that can be attached to
+            the post.
+        comment_options (str, dict): JSON options object that can be
+            attached to the post.
+        Example::
+            comment_options = {
+                'max_accepted_payout': '1000000.000 SBD',
+                'percent_steem_dollars': 10000,
+                'allow_votes': True,
+                'allow_curation_rewards': True,
+                'extensions': [[0, {
+                    'beneficiaries': [
+                        {'account': 'account1', 'weight': 5000},
+                        {'account': 'account2', 'weight': 5000},
+                    ]}
+                ]]
+            }
+        community (str): (Optional) Name of the community we are posting
+            into. This will also override the community specified in
+            `json_metadata`.
+        tags (str, list): (Optional) A list of tags (5 max) to go with the
+            post. This will also override the tags specified in
+            `json_metadata`. The first tag will be used as a 'category'. If
+            provided as a string, it should be space separated.
+        beneficiaries (list of dicts): (Optional) A list of beneficiaries
+            for posting reward distribution. This argument overrides
+            beneficiaries as specified in `comment_options`.
+        For example, if we would like to split rewards between account1 and
+        account2::
+            beneficiaries = [
+                {'account': 'account1', 'weight': 5000},
+                {'account': 'account2', 'weight': 5000}
+            ]
+        self_vote (bool): (Optional) Upvote the post as author, right after
+            posting.
+        """
+
+        # prepare json_metadata
+        json_metadata = json_metadata or {}
+        if isinstance(json_metadata, str):
+            json_metadata = json.loads(json_metadata)
+
+        # override the community
+        if community:
+            json_metadata.update({'community': community})
+
+        if title is None:
+            title = self["title"]
+        if body is None:
+            body = self["body"]
+        if author is None and permlink is None:
+            [author, permlink] = resolve_authorperm(self.identifier)
+        else:
+            if author is None:
+                author = self["author"]
+            if permlink is None:
+                permlink = self["permlink"]
+        account = Account(author, steem_instance=self.steem)
+        # deal with the category and tags
+        if isinstance(tags, str):
+            tags = list(set(filter(None, (re.split("[\W_]", tags)))))
+
+        category = None
+        tags = tags or json_metadata.get('tags', [])
+        if tags:
+            if len(tags) > 5:
+                raise ValueError('Can only specify up to 5 tags per post.')
+
+            # first tag should be a category
+            category = tags[0]
+            json_metadata.update({"tags": tags})
+
+        # can't provide a category while replying to a post
+        if reply_identifier and category:
+            category = None
+
+        # deal with replies/categories
+        if reply_identifier:
+            parent_author, parent_permlink = resolve_authorperm(
+                reply_identifier)
+            if not permlink:
+                permlink = derive_permlink(title, parent_permlink)
+        elif category:
+            parent_permlink = derive_permlink(category)
+            parent_author = ""
+            if not permlink:
+                permlink = derive_permlink(title)
+        else:
+            parent_author = ""
+            parent_permlink = ""
+            if not permlink:
+                permlink = derive_permlink(title)
+
+        post_op = operations.Comment(
+            **{
+                "parent_author": parent_author,
+                "parent_permlink": parent_permlink,
+                "author": author,
+                "permlink": permlink,
+                "title": title,
+                "body": body,
+                "json_metadata": json_metadata
+            })
+        ops = [post_op]
+
+        # if comment_options are used, add a new op to the transaction
+        if comment_options or beneficiaries:
+            options = keep_in_dict(comment_options or {}, [
+                'max_accepted_payout', 'percent_steem_dollars', 'allow_votes',
+                'allow_curation_rewards', 'extensions'
+            ])
+            # override beneficiaries extension
+            if beneficiaries:
+                # validate schema
+                # or just simply vo.Schema([{'account': str, 'weight': int}])
+
+                for b in beneficiaries:
+                    if 'account' not in b:
+                        raise ValueError(
+                            "beneficiaries need an account field!"
+                        )
+                    if 'weight' not in b:
+                        b['weight'] = 10000
+                    if len(b['account']) > 16:
+                        raise ValueError(
+                            "beneficiaries error, account name length >16!"
+                        )
+                    if b['weight'] < 1 or b['weight'] > 10000:
+                        raise ValueError(
+                            "beneficiaries error, 1<=weight<=10000!"
+                        )
+
+                options['beneficiaries'] = beneficiaries
+
+            default_max_payout = "1000000.000 SBD"
+            comment_op = operations.Comment_options(
+                **{
+                    "author":
+                    author,
+                    "permlink":
+                    permlink,
+                    "max_accepted_payout":
+                    options.get("max_accepted_payout", default_max_payout),
+                    "percent_steem_dollars":
+                    int(options.get("percent_steem_dollars", 10000)),
+                    "allow_votes":
+                    options.get("allow_votes", True),
+                    "allow_curation_rewards":
+                    options.get("allow_curation_rewards", True),
+                    "extensions":
+                    options.get("extensions", []),
+                    "beneficiaries":
+                    options.get("beneficiaries"),
+                })
+            ops.append(comment_op)
+
+        if self_vote:
+            vote_op = operations.Vote(
+                **{
+                    'voter': author,
+                    'author': author,
+                    'permlink': permlink,
+                    'weight': 10000,
+                })
+            ops.append(vote_op)
+
+        return self.steem.finalizeOp(ops, account, "posting")
+
+    def resteem(self, identifier=None, account=None):
+        """ Resteem a post
+            :param str identifier: post identifier (@<account>/<permlink>)
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        if not account:
+            account = self.steem.configStorage.get("default_account")
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if identifier is None:
+            identifier = self.identifier
+        author, permlink = resolve_authorperm(identifier)
+        json_body = [
+            "reblog", {
+                "account": account["name"],
+                "author": author,
+                "permlink": permlink
+            }
+        ]
+        return self.steem.custom_json(
+            id="follow", json=json_body, required_posting_auths=[account["name"]])
+
+    def comment_options(self, options, identifier=None, account=None):
+        """ Set the comment options
+            :param str identifier: Post identifier
+            :param dict options: The options to define.
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+            For the options, you have these defaults:::
+                    {
+                        "author": "",
+                        "permlink": "",
+                        "max_accepted_payout": "1000000.000 SBD",
+                        "percent_steem_dollars": 10000,
+                        "allow_votes": True,
+                        "allow_curation_rewards": True,
+                    }
+        """
+        if not account:
+            account = self.steem.configStorage.get("default_account")
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, steem_instance=self.steem)
+        if identifier is None:
+            identifier = self.identifier
+        author, permlink = resolve_authorperm(identifier)
+        default_max_payout = "1000000.000 SBD"
+        STEEMIT_100_PERCENT = 10000
+        STEEMIT_1_PERCENT = (STEEMIT_100_PERCENT / 100)
+        op = operations.Comment_options(
+            **{
+                "author":
+                author,
+                "permlink":
+                permlink,
+                "max_accepted_payout":
+                options.get("max_accepted_payout", default_max_payout),
+                "percent_steem_dollars":
+                options.get("percent_steem_dollars", 100) * STEEMIT_1_PERCENT,
+                "allow_votes":
+                options.get("allow_votes", True),
+                "allow_curation_rewards":
+                options.get("allow_curation_rewards", True),
+            })
+        return self.commit.finalizeOp(op, account, "posting")
+
 
 class RecentReplies(list):
     """ Obtain a list of recent replies
diff --git a/beem/discussions.py b/beem/discussions.py
index 37c2788f..47b4fd9c 100644
--- a/beem/discussions.py
+++ b/beem/discussions.py
@@ -6,6 +6,12 @@ import logging
 log = logging.getLogger(__name__)
 
 
+class Query(dict):
+    def __init__(self, limit=0, truncate_body=0):
+        self["limit"] = limit
+        self["truncate_body"] = truncate_body
+
+
 class Discussions_by_trending(list):
     """ get_discussions_by_trending
 
@@ -21,3 +27,224 @@ class Discussions_by_trending(list):
                 for x in posts
             ]
         )
+
+
+class Comment_discussions_by_payout(list):
+    """ get_comment_discussions_by_payout
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_comment_discussions_by_payout(discussion_query)
+        super(Comment_discussions_by_payout, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Post_discussions_by_payout(list):
+    """ get_post_discussions_by_payout
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_post_discussions_by_payout(discussion_query)
+        super(Post_discussions_by_payout, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_created(list):
+    """ get_discussions_by_created
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_created(discussion_query)
+        super(Discussions_by_created, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_active(list):
+    """ get_discussions_by_active
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_active(discussion_query)
+        super(Discussions_by_active, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_cashout(list):
+    """ get_discussions_by_cashout
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_cashout(discussion_query)
+        super(Discussions_by_cashout, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_payout(list):
+    """ get_discussions_by_payout
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_payout(discussion_query)
+        super(Discussions_by_payout, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_votes(list):
+    """ get_discussions_by_votes
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_votes(discussion_query)
+        super(Discussions_by_votes, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_children(list):
+    """ get_discussions_by_children
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_children(discussion_query)
+        super(Discussions_by_children, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_hot(list):
+    """ get_discussions_by_hot
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_hot(discussion_query)
+        super(Discussions_by_hot, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_feed(list):
+    """ get_discussions_by_feed
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_feed(discussion_query)
+        super(Discussions_by_feed, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_blog(list):
+    """ get_discussions_by_blog
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_blog(discussion_query)
+        super(Discussions_by_blog, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_comments(list):
+    """ get_discussions_by_comments
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_comments(discussion_query)
+        super(Discussions_by_comments, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
+
+
+class Discussions_by_promoted(list):
+    """ get_discussions_by_promoted
+
+        :param str discussion_query
+        :param steem steem_instance: Steem() instance to use when accesing a RPC
+    """
+    def __init__(self, discussion_query, steem_instance=None):
+        self.steem = steem_instance or shared_steem_instance()
+        posts = self.steem.rpc.get_discussions_by_promoted(discussion_query)
+        super(Discussions_by_promoted, self).__init__(
+            [
+                Comment(x)
+                for x in posts
+            ]
+        )
diff --git a/beem/exceptions.py b/beem/exceptions.py
index 55ee9caf..52849224 100644
--- a/beem/exceptions.py
+++ b/beem/exceptions.py
@@ -47,6 +47,12 @@ class InsufficientAuthorityError(Exception):
     pass
 
 
+class VotingInvalidOnArchivedPost(Exception):
+    """ The transaction requires signature of a higher authority
+    """
+    pass
+
+
 class MissingKeyError(Exception):
     """ A required key couldn't be found in the wallet
     """
diff --git a/beem/market.py b/beem/market.py
index 7b2b82ce..7eb7546b 100644
--- a/beem/market.py
+++ b/beem/market.py
@@ -7,6 +7,7 @@ from .amount import Amount
 from .price import Price, Order, FilledOrder
 from .account import Account
 from beembase import operations
+import random
 
 
 class Market(dict):
@@ -43,24 +44,14 @@ class Market(dict):
     def __init__(
         self,
         *args,
-        base=None,
-        quote=None,
         steem_instance=None,
         **kwargs
     ):
         self.steem = steem_instance or shared_steem_instance()
 
-        if len(args) == 1 and isinstance(args[0], str):
-            quote_symbol, base_symbol = assets_from_string(args[0])
-            quote = Asset(quote_symbol, steem_instance=self.steem)
-            base = Asset(base_symbol, steem_instance=self.steem)
-            super(Market, self).__init__({"base": base, "quote": quote})
-        elif len(args) == 0 and base and quote:
-            super(Market, self).__init__({"base": base, "quote": quote})
-        elif len(args) == 2 and not base and not quote:
-            super(Market, self).__init__({"base": args[1], "quote": args[0]})
-        else:
-            raise ValueError("Unknown Market Format: %s" % str(args))
+        quote = Asset("STEEM", steem_instance=self.steem)
+        base = Asset("SBD", steem_instance=self.steem)
+        super(Market, self).__init__({"base": base, "quote": quote})
 
     def get_string(self, separator=":"):
         """ Return a formated string that identifies the market, e.g. ``USD:BTS``
@@ -69,23 +60,7 @@ class Market(dict):
         """
         return "%s%s%s" % (self["quote"]["symbol"], separator, self["base"]["symbol"])
 
-    def __eq__(self, other):
-        if isinstance(other, str):
-            quote_symbol, base_symbol = assets_from_string(other)
-            return (
-                self["quote"]["symbol"] == quote_symbol and
-                self["base"]["symbol"] == base_symbol
-            ) or (
-                self["quote"]["symbol"] == base_symbol and
-                self["base"]["symbol"] == quote_symbol
-            )
-        elif isinstance(other, Market):
-            return (
-                self["quote"]["symbol"] == other["quote"]["symbol"] and
-                self["base"]["symbol"] == other["base"]["symbol"]
-            )
-
-    def ticker(self):
+    def ticker(self, raw_data=False):
         """ Returns the ticker for all markets.
 
             Output Parameters:
@@ -119,20 +94,13 @@ class Market(dict):
         """
         data = {}
         # Core Exchange rate
-        cer = self["quote"]["options"]["core_exchange_rate"]
-        data["core_exchange_rate"] = Price(
-            cer,
-            steem_instance=self.steem
-        )
-        if cer["base"]["asset_id"] == self["quote"]["id"]:
-            data["core_exchange_rate"] = data["core_exchange_rate"].invert()
+        self.steem.register_apis(["market_history"])
+        ticker = self.steem.rpc.get_ticker(api="market_history")
+        if raw_data:
+            return ticker
 
-        ticker = self.steem.rpc.get_ticker(
-            self["base"]["id"],
-            self["quote"]["id"],
-        )
-        data["baseVolume"] = Amount(ticker["base_volume"], self["base"], steem_instance=self.steem)
-        data["quoteVolume"] = Amount(ticker["quote_volume"], self["quote"], steem_instance=self.steem)
+        data["sbdVolume"] = Amount(ticker["sbd_volume"], steem_instance=self.steem)
+        data["steemVolume"] = Amount(ticker["steem_volume"], steem_instance=self.steem)
         data["lowestAsk"] = Price(
             ticker["lowest_ask"],
             base=self["base"],
@@ -155,7 +123,7 @@ class Market(dict):
 
         return data
 
-    def volume24h(self):
+    def volume24h(self, raw_data=False):
         """ Returns the 24-hour volume for all markets, plus totals for primary currencies.
 
             Sample output:
@@ -168,16 +136,53 @@ class Market(dict):
                 }
 
         """
-        volume = self.steem.rpc.get_24_volume(
-            self["base"]["id"],
-            self["quote"]["id"],
-        )
+        self.steem.register_apis(["market_history"])
+        volume = self.steem.rpc.get_volume(api="market_history")
+        if raw_data:
+            return volume
         return {
-            self["base"]["symbol"]: Amount(volume["base_volume"], self["base"], steem_instance=self.steem),
-            self["quote"]["symbol"]: Amount(volume["quote_volume"], self["quote"], steem_instance=self.steem)
+            self["base"]["symbol"]: Amount(volume["sbd_volume"], steem_instance=self.steem),
+            self["quote"]["symbol"]: Amount(volume["steem_volume"], steem_instance=self.steem)
         }
 
-    def orderbook(self, limit=25):
+    def orderbook(self, limit=25, raw_data=False):
+        """ Returns the order book for a given market. You may also
+            specify "all" to get the orderbooks of all markets.
+            :param int limit: Limit the amount of orders (default: 25)
+            Sample output:
+            .. code-block:: js
+                {'bids': [0.003679 USD/BTS (1.9103 USD|519.29602 BTS),
+                0.003676 USD/BTS (299.9997 USD|81606.16394 BTS),
+                0.003665 USD/BTS (288.4618 USD|78706.21881 BTS),
+                0.003665 USD/BTS (3.5285 USD|962.74409 BTS),
+                0.003665 USD/BTS (72.5474 USD|19794.41299 BTS)],
+                'asks': [0.003738 USD/BTS (36.4715 USD|9756.17339 BTS),
+                0.003738 USD/BTS (18.6915 USD|5000.00000 BTS),
+                0.003742 USD/BTS (182.6881 USD|48820.22081 BTS),
+                0.003772 USD/BTS (4.5200 USD|1198.14798 BTS),
+                0.003799 USD/BTS (148.4975 USD|39086.59741 BTS)]}
+            .. note:: Each bid is an instance of
+                class:`steem.price.Order` and thus carries the keys
+                ``base``, ``quote`` and ``price``. From those you can
+                obtain the actual amounts for sale
+        """
+        orders = self.steem.rpc.get_order_book(limit)
+        if raw_data:
+            return orders
+        asks = list(map(lambda x: Order(
+            Amount(x["order_price"]["quote"], steem_instance=self.steem),
+            Amount(x["order_price"]["base"], steem_instance=self.steem),
+            steem_instance=self.steem), orders["asks"]))
+        bids = list(map(lambda x: Order(
+            Amount(x["order_price"]["quote"], steem_instance=self.steem),
+            Amount(x["order_price"]["base"], steem_instance=self.steem),
+            steem_instance=self.steem), orders["bids"]))
+        asks_date = list(map(lambda x: formatTimeString(x["created"]), orders["asks"]))
+        bids_date = list(map(lambda x: formatTimeString(x["created"]), orders["bids"]))
+        data = {"asks": asks, "bids": bids, "asks_date": asks_date, "bids_date": bids_date}
+        return data
+
+    def recent_trades(self, limit=25, raw_data=False):
         """ Returns the order book for a given market. You may also
             specify "all" to get the orderbooks of all markets.
 
@@ -205,25 +210,20 @@ class Market(dict):
                 obtain the actual amounts for sale
 
         """
-        orders = self.steem.rpc.get_order_book(
-            self["base"]["id"],
-            self["quote"]["id"],
-            limit
-        )
-        asks = list(map(lambda x: Order(
-            Amount(x["quote"], self["quote"], steem_instance=self.steem),
-            Amount(x["base"], self["base"], steem_instance=self.steem),
-            steem_instance=self.steem
-        ), orders["asks"]))
-        bids = list(map(lambda x: Order(
-            Amount(x["quote"], self["quote"], steem_instance=self.steem),
-            Amount(x["base"], self["base"], steem_instance=self.steem),
-            steem_instance=self.steem
-        ), orders["bids"]))
-        data = {"asks": asks, "bids": bids}
-        return data
+        self.steem.register_apis(["market_history"])
+        orders = self.steem.rpc.get_recent_trades(limit, api="market_history")
+        if raw_data:
+            return orders
+        data_order = list(map(lambda x: Order(
+            Amount(x["open_pays"], steem_instance=self.steem),
+            Amount(x["current_pays"], steem_instance=self.steem),
+            steem_instance=self.steem), orders))
+
+        data_date = list(map(lambda x: formatTimeString(x["date"]), orders))
 
-    def trades(self, limit=25, start=None, stop=None):
+        return {'date': data_date, 'order': data_order}
+
+    def trades(self, limit=25, start=None, stop=None, raw_data=False):
         """ Returns your trade history for a given market.
 
             :param int limit: Limit the amount of orders (default: 25)
@@ -237,68 +237,42 @@ class Market(dict):
             stop = datetime.now()
         if not start:
             start = stop - timedelta(hours=24)
+        self.steem.register_apis(["market_history"])
         orders = self.steem.rpc.get_trade_history(
-            self["base"]["symbol"],
-            self["quote"]["symbol"],
-            formatTime(stop),
-            formatTime(start),
-            limit)
-        return list(map(
-            lambda x: FilledOrder(
-                x,
-                quote=Amount(x["amount"], self["quote"], steem_instance=self.steem),
-                base=Amount(float(x["amount"]) * float(x["price"]), self["base"], steem_instance=self.steem),
-                steem_instance=self.steem
-            ), orders
-        ))
-
-    def accounttrades(self, account=None, limit=25):
-        """ Returns your trade history for a given market, specified by
-            the "currencyPair" parameter. You may also specify "all" to
-            get the orderbooks of all markets.
-
-            :param str currencyPair: Return results for a particular market only (default: "all")
-            :param int limit: Limit the amount of orders (default: 25)
-
-            Output Parameters:
-
-                - `type`: sell or buy
-                - `rate`: price for `quote` denoted in `base` per `quote`
-                - `amount`: amount of quote
-                - `total`: amount of base at asked price (amount/price)
-
-            .. note:: This call goes through the trade history and
-                      searches for your account, if there are no orders
-                      within ``limit`` trades, this call will return an
-                      empty array.
-
-        """
-        if not account:
-            if "default_account" in self.steem.config:
-                account = self.steem.config["default_account"]
-        if not account:
-            raise ValueError("You need to provide an account")
-        account = Account(account, steem_instance=self.steem)
-
-        filled = self.steem.rpc.get_fill_order_history(
-            self["base"]["id"],
-            self["quote"]["id"],
-            2 * limit,
-            api="history"
-        )
-        trades = []
-        for f in filled:
-            if f["op"]["account_id"] == account["id"]:
-                trades.append(
-                    FilledOrder(
-                        f,
-                        base=self["base"],
-                        quote=self["quote"],
-                        steem_instance=self.steem
-                    ))
-        return trades
-
-    def accountopenorders(self, account=None):
+            formatTimeFromNow(start),
+            formatTimeFromNow(stop),
+            limit, api="market_history")
+        if raw_data:
+            return orders
+        data_order = list(map(lambda x: Order(
+            Amount(x["open_pays"], steem_instance=self.steem),
+            Amount(x["current_pays"], steem_instance=self.steem),
+            steem_instance=self.steem), orders))
+
+        data_date = list(map(lambda x: formatTimeString(x["date"]), orders))
+
+        return {'date': data_date, 'order': data_order}
+
+    def market_history_buckets(self):
+        self.steem.register_apis(["market_history"])
+        return self.steem.rpc.get_market_history_buckets(api="market_history")
+
+    def market_history(self, bucket_seconds=300, start_age=3600, end_age=0):
+        buckets = self.market_history_buckets()
+        # self.steem.register_apis(["market_history"])
+        if bucket_seconds < 5 and bucket_seconds >= 0:
+            bucket_seconds = buckets[bucket_seconds]
+        else:
+            if bucket_seconds not in buckets:
+                raise ValueError("You need select the bucket_seconds from " + str(buckets))
+        history = self.steem.rpc.get_market_history(
+            bucket_seconds,
+            formatTimeFromNow(-start_age - end_age),
+            formatTimeFromNow(-end_age),
+            api="market_history")
+        return history
+
+    def accountopenorders(self, account=None, raw_data=False):
         """ Returns open Orders
 
             :param steem.account.Account account: Account name or instance of Account to show orders for in this market
@@ -311,19 +285,20 @@ class Market(dict):
         account = Account(account, full=True, steem_instance=self.steem)
 
         r = []
-        orders = account["limit_orders"]
+        # orders = account["limit_orders"]
+        orders = self.steem.rpc.get_open_orders(account["name"])
+        if raw_data:
+            return orders
         for o in orders:
-            if ((
-                o["sell_price"]["base"]["asset_id"] == self["base"]["id"] and
-                o["sell_price"]["quote"]["asset_id"] == self["quote"]["id"]
-            ) or (
-                o["sell_price"]["base"]["asset_id"] == self["quote"]["id"] and
-                o["sell_price"]["quote"]["asset_id"] == self["base"]["id"]
-            )):
-                r.append(Order(
-                    o,
-                    steem_instance=self.steem
-                ))
+            order = {}
+            order["order"] = Order(
+                Amount(o["sell_price"]["base"]),
+                Amount(o["sell_price"]["quote"]),
+                steem_instance=self.steem
+            )
+            order["orderid"] = o["orderid"]
+            order["created"] = formatTimeString(o["created"])
+            r.append(order)
         return r
 
     def buy(
@@ -333,6 +308,7 @@ class Market(dict):
         expiration=None,
         killfill=False,
         account=None,
+        orderid=None,
         returnOrderId=False
     ):
         """ Places a buy order in a given market
@@ -386,20 +362,22 @@ class Market(dict):
             assert(amount["asset"]["symbol"] == self["quote"]["symbol"]), \
                 "Price: {} does not match amount: {}".format(
                     str(price), str(amount))
+        elif isinstance(amount, str):
+            amount = Amount(amount, steem_instance=self.steem)
         else:
             amount = Amount(amount, self["quote"]["symbol"], steem_instance=self.steem)
 
         order = operations.Limit_order_create(**{
-            "fee": {"amount": 0, "asset_id": "1.3.0"},
-            "seller": account["id"],
-            "amount_to_sell": {
-                "amount": int(float(amount) * float(price) * 10 ** self["base"]["precision"]),
-                "asset_id": self["base"]["id"]
-            },
-            "min_to_receive": {
-                "amount": int(float(amount) * 10 ** self["quote"]["precision"]),
-                "asset_id": self["quote"]["id"]
-            },
+            "owner": account["name"],
+            "orderid": orderid or random.getrandbits(32),
+            "amount_to_sell": Amount(
+                float(amount) * float(price),
+                self["base"]["symbol"]
+            ),
+            "min_to_receive": Amount(
+                float(amount),
+                self["quote"]["symbol"]
+            ),
             "expiration": formatTimeFromNow(expiration),
             "fill_or_kill": killfill,
         })
@@ -424,6 +402,7 @@ class Market(dict):
         expiration=None,
         killfill=False,
         account=None,
+        orderid=None,
         returnOrderId=False
     ):
         """ Places a sell order in a given market
@@ -464,20 +443,22 @@ class Market(dict):
             assert(amount["asset"]["symbol"] == self["quote"]["symbol"]), \
                 "Price: {} does not match amount: {}".format(
                     str(price), str(amount))
+        elif isinstance(amount, str):
+            amount = Amount(amount, steem_instance=self.steem)
         else:
             amount = Amount(amount, self["quote"]["symbol"], steem_instance=self.steem)
 
         order = operations.Limit_order_create(**{
-            "fee": {"amount": 0, "asset_id": "1.3.0"},
-            "seller": account["id"],
-            "amount_to_sell": {
-                "amount": int(float(amount) * 10 ** self["quote"]["precision"]),
-                "asset_id": self["quote"]["id"]
-            },
-            "min_to_receive": {
-                "amount": int(float(amount) * float(price) * 10 ** self["base"]["precision"]),
-                "asset_id": self["base"]["id"]
-            },
+            "owner": account["name"],
+            "orderid": orderid or random.getrandbits(32),
+            "amount_to_sell": Amount(
+                float(amount),
+                self["quote"]["symbol"]
+            ),
+            "min_to_receive": Amount(
+                float(amount) * float(price),
+                self["base"]["symbol"]
+            ),
             "expiration": formatTimeFromNow(expiration),
             "fill_or_kill": killfill,
         })
@@ -494,41 +475,26 @@ class Market(dict):
 
         return tx
 
-    def cancel(self, orderNumber, account=None):
+    def cancel(self, orderNumbers, account=None, **kwargs):
         """ Cancels an order you have placed in a given market. Requires
-            only the "orderNumber". An order number takes the form
+            only the "orderNumbers". An order number takes the form
             ``1.7.xxx``.
-
-            :param str orderNumber: The Order Object ide of the form ``1.7.xxxx``
-        """
-        return self.steem.cancel(orderNumber, account=account)
-
-    def core_quote_market(self):
-        """ This returns an instance of the market that has the core market of the quote asset.
-            It means that quote needs to be a market pegged asset and returns a
-            market to it's collateral asset.
+            :param str orderNumbers: The Order Object ide of the form ``1.7.xxxx``
         """
-        if not self["quote"].is_bitasset:
-            raise ValueError("Quote (%s) is not a bitasset!" % self["quote"]["symbol"])
-        self["quote"].full = True
-        self["quote"].refresh()
-        collateral = Asset(
-            self["quote"]["bitasset_data"]["options"]["short_backing_asset"],
-            steem_instance=self.steem
-        )
-        return Market(quote=self["quote"], base=collateral)
-
-    def core_base_market(self):
-        """ This returns an instance of the market that has the core market of the base asset.
-            It means that base needs to be a market pegged asset and returns a
-            market to it's collateral asset.
-        """
-        if not self["base"].is_bitasset:
-            raise ValueError("base (%s) is not a bitasset!" % self["base"]["symbol"])
-        self["base"].full = True
-        self["base"].refresh()
-        collateral = Asset(
-            self["base"]["bitasset_data"]["options"]["short_backing_asset"],
-            steem_instance=self.steem
-        )
-        return Market(quote=self["base"], base=collateral)
+        if not account:
+            if "default_account" in self.steem.config:
+                account = self.steem.config["default_account"]
+        if not account:
+            raise ValueError("You need to provide an account")
+        account = Account(account, full=False, steem_instance=self.steem)
+
+        if not isinstance(orderNumbers, (list, set, tuple)):
+            orderNumbers = {orderNumbers}
+
+        op = []
+        for order in orderNumbers:
+            op.append(
+                operations.Limit_order_cancel(**{
+                    "owner": account["name"],
+                    "orderid": order}))
+        return self.steem.finalizeOp(op, account["name"], "active", **kwargs)
diff --git a/beem/steem.py b/beem/steem.py
index c5c6ea03..b84e8a40 100644
--- a/beem/steem.py
+++ b/beem/steem.py
@@ -17,7 +17,7 @@ from .exceptions import (
 )
 from .wallet import Wallet
 from .transactionbuilder import TransactionBuilder
-from .utils import formatTime
+from .utils import formatTime, resolve_authorperm
 
 log = logging.getLogger(__name__)
 
@@ -108,6 +108,7 @@ class Steem(object):
                  rpcuser="",
                  rpcpassword="",
                  debug=False,
+                 data_refresh_time_seconds=900,
                  **kwargs):
 
         # More specific set of APIs to register to
@@ -115,9 +116,9 @@ class Steem(object):
             kwargs["apis"] = [
                 "database",
                 "network_broadcast",
-                # "market_history",
-                # "follow",
-                # "account_by_key",
+                "market_history",
+                "follow",
+                "account_by_key",
                 # "tag",
                 # "raw_block"
             ]
@@ -142,13 +143,17 @@ class Steem(object):
                          **kwargs)
 
         # Try Optional APIs
-        try:
-            self.rpc.register_apis(["account_by_key", "follow"])
-        except NoAccessApi as e:
-            log.info(str(e))
+        self.register_apis()
 
         self.wallet = Wallet(self.rpc, **kwargs)
 
+        self.data = {'last_refresh': None, 'dynamic_global_properties': None, 'feed_history': None,
+                     'current_median_history_price': None, 'next_scheduled_hardfork': None,
+                     'hardfork_version': None, 'network': None, 'chain_properties': None,
+                     'config': None, 'reward_fund': None}
+        self.data_refresh_time_seconds = data_refresh_time_seconds
+        # self.refresh_data()
+
         # txbuffers/propbuffer are initialized and cleared
         self.clear()
 
@@ -176,6 +181,205 @@ class Steem(object):
 
         self.rpc = SteemNodeRPC(node, rpcuser, rpcpassword, **kwargs)
 
+    def register_apis(self, apis=["network_broadcast", "account_by_key", "follow", "market_history"]):
+
+        # Try Optional APIs
+        try:
+            self.rpc.register_apis(apis)
+        except NoAccessApi as e:
+            log.info(str(e))
+
+    def refresh_data(self, force_refresh=False):
+        if self.offline:
+            return
+        if self.data['last_refresh'] is not None and not force_refresh:
+            if (datetime.now() - self.data['last_refresh']).total_seconds() < self.data_refresh_time_seconds:
+                return
+        self.data['last_refresh'] = datetime.now()
+        self.data["dynamic_global_properties"] = self.get_dynamic_global_properties(False)
+        self.data['feed_history'] = self.get_feed_history(False)
+        self.data['current_median_history_price'] = self.get_current_median_history_price(False)
+        self.data['next_scheduled_hardfork'] = self.get_next_scheduled_hardfork(False)
+        self.data['hardfork_version'] = self.get_hardfork_version(False)
+        self.data['network'] = self.get_network(False)
+        self.data['chain_properties'] = self.get_chain_properties(False)
+        self.data['config'] = self.get_config(False)
+        self.data['reward_fund'] = {"post": self.get_reward_fund("post", False)}
+
+    def get_dynamic_global_properties(self, use_stored_data=True):
+        """ This call returns the *dynamic global properties*
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['dynamic_global_properties']
+        else:
+            return self.rpc.get_dynamic_global_properties()
+
+    def get_feed_history(self, use_stored_data=True):
+        """ Returns the feed_history
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['feed_history']
+        else:
+            return self.rpc.get_feed_history()
+
+    def get_reward_fund(self, fund_name="post", use_stored_data=True):
+        """ Get details for a reward fund.
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['reward_fund'][fund_name]
+        else:
+            return self.rpc.get_reward_fund(fund_name)
+
+    def get_current_median_history_price(self, use_stored_data=True):
+        """ Returns the current median price
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['current_median_history_price']
+        else:
+            return self.rpc.get_current_median_history_price()
+
+    def get_next_scheduled_hardfork(self, use_stored_data=True):
+        """ Returns Hardfork and live_time of the hardfork
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['next_scheduled_hardfork']
+        else:
+            return self.rpc.get_next_scheduled_hardfork()
+
+    def get_hardfork_version(self, use_stored_data=True):
+        """ Current Hardfork Version as String
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['hardfork_version']
+        else:
+            return self.rpc.get_hardfork_version()
+
+    def get_network(self, use_stored_data=True):
+        """ Identify the network
+
+            :returns: Network parameters
+            :rtype: dict
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['network']
+        else:
+            return self.rpc.get_network()
+
+    def get_median_price(self):
+        median_price = self.get_current_median_history_price()
+        a = (
+            Amount(median_price['base']) /
+            Amount(median_price['quote'])
+        )
+        return a.as_base("SBD")
+
+    def get_payout_from_rshares(self, rshares):
+        reward_fund = self.get_reward_fund()
+        reward_balance = Amount(reward_fund["reward_balance"]).amount
+        recent_claims = float(reward_fund["recent_claims"])
+
+        fund_per_share = reward_balance / (recent_claims)
+        SBD_price = (self.get_median_price() * Amount("1 STEEM")).amount
+        payout = float(rshares) * fund_per_share * SBD_price
+        return payout
+
+    def get_steem_per_mvest(self, time_stamp=None):
+
+        if time_stamp is not None:
+            a = 2.1325476281078992e-05
+            b = -31099.685481490847
+            a2 = 2.9019227739473682e-07
+            b2 = 48.41432402074669
+
+            if (time_stamp < (b2 - b) / (a - a2)):
+                return a * time_stamp + b
+            else:
+                return a2 * time_stamp + b2
+        global_properties = self.get_dynamic_global_properties()
+
+        return (
+            Amount(global_properties['total_vesting_fund_steem']).amount /
+            (Amount(global_properties['total_vesting_shares']).amount / 1e6)
+        )
+
+    def vests_to_sp(self, vests, timestamp=None):
+        if isinstance(vests, Amount):
+            vests = vests.amount
+        return vests / 1e6 * self.get_steem_per_mvest(timestamp)
+
+    def sp_to_vests(self, sp, timestamp=None):
+        return sp * 1e6 / self.get_steem_per_mvest(timestamp)
+
+    def sp_to_sbd(self, sp, voting_power=10000, vote_pct=10000):
+        reward_fund = self.get_reward_fund()
+        reward_balance = Amount(reward_fund["reward_balance"]).amount
+        recent_claims = float(reward_fund["recent_claims"])
+        reward_share = reward_balance / recent_claims
+
+        resulting_vote = ((voting_power * (vote_pct) / 10000) + 49) / 50
+        vesting_shares = int(self.sp_to_vests(sp))
+        SBD_price = (self.get_median_price() * Amount("1 STEEM")).amount
+        VoteValue = (vesting_shares * resulting_vote * 100) * reward_share * SBD_price
+        return VoteValue
+
+    def sp_to_rshares(self, sp, voting_power=10000, vote_pct=10000):
+        """ Obtain the r-shares
+            :param number sp: Steem Power
+            :param int voting_power: voting power (100% = 10000)
+            :param int vote_pct: voting participation (100% = 10000)
+        """
+        # calculate our account voting shares (from vests), mine is 6.08b
+        vesting_shares = int(self.sp_to_vests(sp) * 1e6)
+
+        # get props
+        global_properties = self.get_dynamic_global_properties()
+        vote_power_reserve_rate = global_properties['vote_power_reserve_rate']
+
+        # determine voting power used
+        used_power = int((voting_power * vote_pct) / 10000)
+        max_vote_denom = vote_power_reserve_rate * (5 * 60 * 60 * 24) / (60 * 60 * 24)
+        used_power = int((used_power + max_vote_denom - 1) / max_vote_denom)
+        # calculate vote rshares
+        rshares = ((vesting_shares * used_power) / 10000)
+
+        return rshares
+
+    def get_chain_properties(self, use_stored_data=True):
+        """ Return witness elected chain properties
+
+            ::
+                {'account_creation_fee': '30.000 STEEM',
+                 'maximum_block_size': 65536,
+                 'sbd_interest_rate': 250}
+
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['chain_properties']
+        else:
+            return self.rpc.get_chain_properties()
+
+    def get_state(self, path="value"):
+        """ get_state
+        """
+        return self.rpc.get_state(path)
+
+    def get_config(self, use_stored_data=True):
+        """ Returns internal chain configuration.
+        """
+        if use_stored_data:
+            self.refresh_data()
+            return self.data['config']
+        else:
+            return self.rpc.get_config()
+
     @property
     def chain_params(self):
         if self.offline:
@@ -341,44 +545,6 @@ class Steem(object):
         self.new_tx()
         # self.new_proposal()
 
-    # -------------------------------------------------------------------------
-    # Simple Transfer
-    # -------------------------------------------------------------------------
-    def transfer(self, to, amount, asset, memo="", account=None, **kwargs):
-        """ Transfer an asset to another account.
-
-            :param str to: Recipient
-            :param float amount: Amount to transfer
-            :param str asset: Asset to transfer
-            :param str memo: (optional) Memo, may begin with `#` for encrypted
-                messaging
-            :param str account: (optional) the source account for the transfer
-                if not ``default_account``
-        """
-        from .memo import Memo
-        if not account:
-            if "default_account" in config:
-                account = config["default_account"]
-        if not account:
-            raise ValueError("You need to provide an account")
-
-        account = Account(account, steem_instance=self)
-        amount = Amount(amount, asset, steem_instance=self)
-        to = Account(to, steem_instance=self)
-
-        memoObj = Memo(
-            from_account=account,
-            to_account=to,
-            steem_instance=self
-        )
-        op = operations.Transfer(**{
-            "amount": amount,
-            "to": to["name"],
-            "memo": memoObj.encrypt(memo),
-            "from": account["name"],
-        })
-        return self.finalizeOp(op, account, "active", **kwargs)
-
     # -------------------------------------------------------------------------
     # Account related calls
     # -------------------------------------------------------------------------
@@ -640,9 +806,9 @@ class Steem(object):
             "json_metadata": account["json_metadata"],
         })
         if permission == "owner":
-            return self.finalizeOp(op, account["name"], "owner", **kwargs)
+            return self.finalizeOp(op, account, "owner", **kwargs)
         else:
-            return self.finalizeOp(op, account["name"], "active", **kwargs)
+            return self.finalizeOp(op, account, "active", **kwargs)
 
     def disallow(
         self, foreign, permission="posting",
@@ -723,73 +889,35 @@ class Steem(object):
             "json_metadata": account["json_metadata"]
         })
         if permission == "owner":
-            return self.finalizeOp(op, account["name"], "owner", **kwargs)
+            return self.finalizeOp(op, account, "owner", **kwargs)
         else:
-            return self.finalizeOp(op, account["name"], "active", **kwargs)
-
-    def update_memo_key(self, key, account=None, **kwargs):
-        """ Update an account's memo public key
-
-            This method does **not** add any private keys to your
-            wallet but merely changes the memo public key.
-
-            :param str key: New memo public key
-            :param str account: (optional) the account to allow access
-                to (defaults to ``default_account``)
-        """
-        if not account:
-            if "default_account" in config:
-                account = config["default_account"]
-        if not account:
-            raise ValueError("You need to provide an account")
-
-        PublicKey(key, prefix=self.prefix)
-
-        account = Account(account, steem_instance=self)
-        account["memo_key"] = key
-        op = operations.Account_update(**{
-            "account": account["name"],
-            "memo_key": account["memo_key"],
-            "json_metadata": account["json_metadata"]
-        })
-        return self.finalizeOp(op, account["name"], "active", **kwargs)
-
-    # -------------------------------------------------------------------------
-    #  Approval and Disapproval of witnesses
-    # -------------------------------------------------------------------------
-    def approvewitness(self, witness, account=None, approve=True, **kwargs):
-        """ Approve a witness
-
-            :param list witnesses: list of Witness name or id
-            :param str account: (optional) the account to allow access
-                to (defaults to ``default_account``)
-        """
-        if not account:
-            if "default_account" in config:
-                account = config["default_account"]
-        if not account:
-            raise ValueError("You need to provide an account")
-        account = Account(account, steem_instance=self)
-
-        # if not isinstance(witnesses, (list, set, tuple)):
-        #     witnesses = {witnesses}
-
-        # for witness in witnesses:
-        #     witness = Witness(witness, steem_instance=self)
-
-        op = operations.Account_witness_vote(**{
-            "account": account["name"],
-            "witness": witness,
-            "approve": approve
-        })
-        return self.finalizeOp(op, account["name"], "active", **kwargs)
-
-    def disapprovewitness(self, witness, account=None, **kwargs):
-        """ Disapprove a witness
-
-            :param list witnesses: list of Witness name or id
-            :param str account: (optional) the account to allow access
-                to (defaults to ``default_account``)
+            return self.finalizeOp(op, account, "active", **kwargs)
+
+    def custom_json(self,
+                    id,
+                    json_data,
+                    required_auths=[],
+                    required_posting_auths=[]):
+        """ Create a custom json operation
+            :param str id: identifier for the custom json (max length 32 bytes)
+            :param json json_data: the json data to put into the custom_json
+                operation
+            :param list required_auths: (optional) required auths
+            :param list required_posting_auths: (optional) posting auths
         """
-        return self.approvewitness(
-            witness=witness, account=account, approve=False)
+        account = None
+        if len(required_auths):
+            account = required_auths[0]
+        elif len(required_posting_auths):
+            account = required_posting_auths[0]
+        else:
+            raise Exception("At least one account needs to be specified")
+        account = Account(account, full=False, steem_instance=self)
+        op = operations.Custom_json(
+            **{
+                "json": json_data,
+                "required_auths": required_auths,
+                "required_posting_auths": required_posting_auths,
+                "id": id
+            })
+        return self.finalizeOp(op, account, "posting")
diff --git a/beem/transactionbuilder.py b/beem/transactionbuilder.py
index 07a4577c..c4c89e8f 100644
--- a/beem/transactionbuilder.py
+++ b/beem/transactionbuilder.py
@@ -269,7 +269,7 @@ class TransactionBuilder(dict):
             log.warning("Not broadcasting anything!")
             self.clear()
             return ret
-
+        self.steem.register_apis(["network_broadcast"])
         # Broadcast
         try:
             if self.steem.blocking:
diff --git a/beem/utils.py b/beem/utils.py
index 5fc5330d..53c4e955 100644
--- a/beem/utils.py
+++ b/beem/utils.py
@@ -1,6 +1,7 @@
 import re
 import time
 from datetime import datetime
+import difflib
 from .exceptions import ObjectNotInProposalBuffer
 
 timeFormat = '%Y-%m-%dT%H:%M:%S'
@@ -111,6 +112,17 @@ def construct_authorperm(*args, username_prefix='@'):
     return "{prefix}{author}/{permlink}".format(**fields)
 
 
+def resolve_root_identifier(url):
+    m = re.match("/([^/]*)/@([^/]*)/([^#]*).*", url)
+    if not m:
+        return "", ""
+    else:
+        category = m.group(1)
+        author = m.group(2)
+        permlink = m.group(3)
+        return construct_authorperm(author, permlink), category
+
+
 def resolve_authorpermvoter(identifier):
     """Correctly split a string containing an authorpermvoter.
 
@@ -176,3 +188,37 @@ def test_proposal_in_buffer(buf, operation_name, id):
                 id
             )
         )
+
+
+def keep_in_dict(obj, allowed_keys=list()):
+    """ Prune a class or dictionary of all but allowed keys.
+    """
+    if type(obj) == dict:
+        items = obj.items()
+    else:
+        items = obj.__dict__.items()
+
+    return {k: v for k, v in items if k in allowed_keys}
+
+
+def remove_from_dict(obj, remove_keys=list()):
+    """ Prune a class or dictionary of specified keys.
+    """
+    if type(obj) == dict:
+        items = obj.items()
+    else:
+        items = obj.__dict__.items()
+
+    return {k: v for k, v in items if k not in remove_keys}
+
+
+def make_patch(a, b, n=3):
+    # _no_eol = '\n' + "\ No newline at end of file" + '\n'
+    _no_eol = '\n'
+    diffs = difflib.unified_diff(a.splitlines(True), b.splitlines(True), n=n)
+    try:
+        _, _ = next(diffs), next(diffs)
+        del _
+    except StopIteration:
+        pass
+    return ''.join([d if d[-1] == '\n' else d + _no_eol for d in diffs])
diff --git a/beem/wallet.py b/beem/wallet.py
index 1d957a0e..59e0ce4f 100644
--- a/beem/wallet.py
+++ b/beem/wallet.py
@@ -10,8 +10,9 @@ from .exceptions import (
     WalletLocked,
     WrongMasterPasswordException,
     NoWalletException,
-    RPCConnectionRequired
+    RPCConnectionRequired,
 )
+from beemapi.exceptions import NoAccessApi
 
 log = logging.getLogger(__name__)
 
@@ -51,6 +52,7 @@ class Wallet():
 
            from beem import Steem
            steem = Steem()
+           steem.wallet.purgeWallet()
            steem.wallet.create("supersecret-passphrase")
 
         This will raise an exception if you already have a wallet installed.
@@ -211,6 +213,11 @@ class Wallet():
         """
         self.newWallet(pwd)
 
+    def purge(self):
+        """ Alias for purgeWallet()
+        """
+        self.purgeWallet()
+
     def newWallet(self, pwd):
         """ Create a new wallet database
         """
@@ -364,7 +371,11 @@ class Wallet():
     def getAccountsFromPublicKey(self, pub):
         """ Obtain all accounts associated with a public key
         """
-        names = self.rpc.get_key_references([pub])
+        try:
+            self.rpc.register_apis(["account_by_key"])
+        except NoAccessApi as e:
+            print(str(e))
+        names = self.rpc.get_key_references([pub], api="account_by_key")
         for name in names:
             for i in name:
                 yield i
@@ -375,7 +386,11 @@ class Wallet():
         # FIXME, this only returns the first associated key.
         # If the key is used by multiple accounts, this
         # will surely lead to undesired behavior
-        names = self.rpc.get_key_references([pub])[0]
+        try:
+            self.rpc.register_apis(["account_by_key"])
+        except NoAccessApi as e:
+            print(str(e))
+        names = self.rpc.get_key_references([pub], api="account_by_key")[0]
         if not names:
             return None
         else:
@@ -415,7 +430,7 @@ class Wallet():
     def getKeyType(self, account, pub):
         """ Get key type
         """
-        for authority in ["owner", "active"]:
+        for authority in ["owner", "active", "posting"]:
             for key in account[authority]["key_auths"]:
                 if pub == key[0]:
                     return authority
diff --git a/beem/witness.py b/beem/witness.py
index 69e3d93c..00860d53 100644
--- a/beem/witness.py
+++ b/beem/witness.py
@@ -1,9 +1,12 @@
 from beem.instance import shared_steem_instance
 from .account import Account
+from .amount import Amount
 from .exceptions import WitnessDoesNotExistsException
 from .blockchainobject import BlockchainObject
 from .utils import formatTimeString, parse_time
 from datetime import datetime, timedelta
+from beembase import transactions, operations
+from beembase.account import PrivateKey, PublicKey
 
 
 class Witness(BlockchainObject):
@@ -39,6 +42,8 @@ class Witness(BlockchainObject):
         )
 
     def refresh(self):
+        if not self.identifier:
+            return
         witness = self.steem.rpc.get_witness_by_account(self.identifier)
         if not witness:
             raise WitnessDoesNotExistsException
@@ -49,6 +54,86 @@ class Witness(BlockchainObject):
     def account(self):
         return Account(self["owner"], steem_instance=self.steem)
 
+    def feed_publish(self,
+                     base,
+                     quote="1.000 STEEM",
+                     account=None):
+        """ Publish a feed price as a witness.
+            :param float base: USD Price of STEEM in SBD (implied price)
+            :param float quote: (optional) Quote Price. Should be 1.000, unless
+            we are adjusting the feed to support the peg.
+            :param str account: (optional) the source account for the transfer
+            if not self["owner"]
+        """
+        if not account:
+            account = self["owner"]
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        account = Account(account, steem_instance=self)
+        if isinstance(base, Amount):
+            base = Amount(base, steem_instance=self.steem)
+        elif isinstance(base, str):
+            base = Amount(base, steem_instance=self.steem)
+        else:
+            base = Amount(base, "SBD", steem_instance=self.steem)
+
+        if isinstance(quote, Amount):
+            quote = Amount(quote, steem_instance=self.steem)
+        elif isinstance(quote, str):
+            quote = Amount(quote, steem_instance=self.steem)
+        else:
+            quote = Amount(quote, "STEEM", steem_instance=self.steem)
+
+        assert base.symbol == "SBD"
+        assert quote.symbol == "STEEM"
+
+        op = operations.Feed_publish(
+            **{
+                "publisher": account["name"],
+                "exchange_rate": {
+                    "base": base,
+                    "quote": quote,
+                }
+            })
+        return self.steem.finalizeOp(op, account, "active")
+
+    def update(self, signing_key, url, props, account=None):
+        """ Update witness
+            :param pubkey signing_key: Signing key
+            :param str url: URL
+            :param dict props: Properties
+            :param str account: (optional) witness account name
+             Properties:::
+                {
+                    "account_creation_fee": x,
+                    "maximum_block_size": x,
+                    "sbd_interest_rate": x,
+                }
+        """
+        if not account:
+            account = self["owner"]
+        if not account:
+            raise ValueError("You need to provide an account")
+
+        account = Account(account, steem_instance=self)
+
+        try:
+            PublicKey(signing_key)
+        except Exception as e:
+            raise e
+
+        op = operations.Witness_update(
+            **{
+                "owner": account["name"],
+                "url": url,
+                "block_signing_key": signing_key,
+                "props": props,
+                "fee": "0.000 STEEM",
+                "prefix": self.steem.chain_params["prefix"]
+            })
+        return self.steem.finalizeOp(op, account, "active")
+
 
 class WitnessesObject(list):
     def printAsTable(self, sort_key="votes", reverse=True):
diff --git a/beembase/operations.py b/beembase/operations.py
index 6512ca92..71dbf125 100644
--- a/beembase/operations.py
+++ b/beembase/operations.py
@@ -73,7 +73,7 @@ class Vote(GrapheneObject):
             if len(args) == 1 and len(kwargs) == 0:
                 kwargs = args[0]
             super().__init__(OrderedDict([
-                ('Voter', String(kwargs["voter"])),
+                ('voter', String(kwargs["voter"])),
                 ('author', String(kwargs["author"])),
                 ('permlink', String(kwargs["permlink"])),
                 ('weight', Int16(kwargs["weight"])),
diff --git a/docs/index.rst b/docs/index.rst
index c8e3c2a5..70c32f00 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -86,19 +86,11 @@ Quickstart
 .. code-block:: python
 
    from beem.market import Market
-   # Not working at the moment
-   # market = Market("STEEM:SBD")
-   # print(market.ticker())
-   # market.steem.wallet.unlock("wallet-passphrase")
-   # print(market.sell(300, 100)  # sell 100 STEEM for 300 STEEM/SBD
+   market = Market()
+   print(market.ticker())
+   market.steem.wallet.unlock("wallet-passphrase")
+   print(market.sell(300, 100)  # sell 100 STEEM for 300 STEEM/SBD
 
-.. code-block:: python
-
-   from beem.dex import Dex
-   # not working at the moment
-   # dex = Dex()
-   # dex.steem.wallet.unlock("wallet-passphrase")
-   
 
 General
 -------
diff --git a/docs/tutorials.rst b/docs/tutorials.rst
index db41326f..6612a9e1 100644
--- a/docs/tutorials.rst
+++ b/docs/tutorials.rst
@@ -17,7 +17,6 @@ executed in the same order as they are added to the transaction.
   from beem import Steem
 
   testnet = Steem(
-      "wss://testnet.steem.vc",
       nobroadcast=True,
       bundle=True,
   )
@@ -46,7 +45,6 @@ attribute:
   from beem import Steem
 
   testnet = Steem(
-      "wss://testnet.steem.vc",
       proposer="test"
   )
   testnet.wallet.unlock("supersecret")
@@ -66,7 +64,6 @@ Simple Sell Script
     # Instanciate Steem (pick network via API node)
     #
     steem = Steem(
-        "wss://node.testnet.steem.eu",
         nobroadcast=True   # <<--- set this to False when you want to fire!
     )
 
@@ -81,7 +78,6 @@ Simple Sell Script
     # Sell and buy calls always refer to the *quote*
     #
     market = Market(
-        "GOLD:USD",
         steem_instance=steem
     )
 
@@ -89,8 +85,8 @@ Simple Sell Script
     # Sell an asset for a price with amount (quote)
     #
     print(market.sell(
-        Price(100.0, "USD/GOLD"),
-        Amount("0.01 GOLD")
+        Price(100.0, "STEEM/SBD"),
+        Amount("0.01 STEEM")
     ))
 
 
@@ -122,7 +118,6 @@ Sell at a timely rate
         # Instanciate Steem (pick network via API node)
         #
         steem = Steem(
-            "wss://node.testnet.steem.eu",
             nobroadcast=True   # <<--- set this to False when you want to fire!
         )
 
@@ -137,7 +132,6 @@ Sell at a timely rate
         # Sell and buy calls always refer to the *quote*
         #
         market = Market(
-            "GOLD:USD",
             steem_instance=steem
         )
 
diff --git a/setup.py b/setup.py
index 9f875fde..3624536b 100755
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,7 @@ except LookupError:
     ascii = codecs.lookup('ascii')
     codecs.register(lambda name, enc=ascii: {True: enc}.get(name == 'mbcs'))
 
-VERSION = '0.19.4'
+VERSION = '0.19.5'
 
 
 def write_version_py(filename):
diff --git a/tests/test_steem.py b/tests/test_steem.py
index a7d5f28f..4e3e176b 100644
--- a/tests/test_steem.py
+++ b/tests/test_steem.py
@@ -6,6 +6,8 @@ from pprint import pprint
 from beem import Steem
 from beembase.operationids import getOperationNameForId
 from beem.amount import Amount
+from beem.witness import Witness
+from beem.account import Account
 from beembase.account import PrivateKey
 from beem.instance import set_shared_steem_instance
 
@@ -34,7 +36,8 @@ class Testcases(unittest.TestCase):
     def test_transfer(self):
         bts = self.bts
         # bts.prefix ="STX"
-        tx = bts.transfer(
+        acc = Account("test", steem_instance=bts)
+        tx = acc.transfer(
             "test", 1.33, "SBD", memo="Foobar", account="test1")
         self.assertEqual(
             tx["operations"][0][0],
@@ -119,9 +122,11 @@ class Testcases(unittest.TestCase):
         bts = self.bts
         tx1 = bts.new_tx()
         tx2 = bts.new_tx()
-        self.bts.transfer("test1", 1, "STEEM", append_to=tx1)
-        self.bts.transfer("test1", 2, "STEEM", append_to=tx2)
-        self.bts.transfer("test1", 3, "STEEM", append_to=tx1)
+
+        acc = Account("test1", steem_instance=bts)
+        acc.transfer("test1", 1, "STEEM", append_to=tx1)
+        acc.transfer("test1", 2, "STEEM", append_to=tx2)
+        acc.transfer("test1", 3, "STEEM", append_to=tx1)
         tx1 = tx1.json()
         tx2 = tx2.json()
         ops1 = tx1["operations"]
@@ -189,7 +194,8 @@ class Testcases(unittest.TestCase):
 
     def test_update_memo_key(self):
         bts = self.bts
-        tx = bts.update_memo_key("STM55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n")
+        acc = Account("test1", steem_instance=bts)
+        tx = acc.update_memo_key("STM55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n")
         self.assertEqual(
             (tx["operations"][0][0]),
             "account_update"
@@ -201,7 +207,8 @@ class Testcases(unittest.TestCase):
 
     def test_approvewitness(self):
         bts = self.bts
-        tx = bts.approvewitness("test1")
+        w = Account("test", steem_instance=bts)
+        tx = w.approvewitness("test1")
         self.assertEqual(
             (tx["operations"][0][0]),
             "account_witness_vote"
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 03940750..34360a7a 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -6,7 +6,9 @@ from beem.utils import (
     construct_authorperm,
     construct_authorpermvoter,
     sanitize_permlink,
-    derive_permlink
+    derive_permlink,
+    resolve_root_identifier,
+    make_patch
 )
 
 
@@ -15,6 +17,9 @@ class Testcases(unittest.TestCase):
         self.assertEqual(construct_authorperm("A", "B"), "@A/B")
         self.assertEqual(construct_authorperm({'author': "A", 'permlink': "B"}), "@A/B")
 
+    def test_resolve_root_identifier(self):
+        self.assertEqual(resolve_root_identifier("/a/@b/c"), ("@b/c", "a"))
+
     def test_constructAuthorpermvoter(self):
         self.assertEqual(construct_authorpermvoter("A", "B", "C"), "@A/B|C")
         self.assertEqual(construct_authorpermvoter({'author': "A", 'permlink': "B", 'voter': 'C'}), "@A/B|C")
@@ -44,3 +49,8 @@ class Testcases(unittest.TestCase):
         self.assertEqual(derive_permlink("Hello World"), "hello-world")
         self.assertEqual(derive_permlink("aAf_0.12"), "aaf-0-12")
         self.assertEqual(derive_permlink("[](){}"), "")
+
+    def test_patch(self):
+        self.assertEqual(make_patch("aa", "ab"), '@@ -1 +1 @@\n-aa\n+ab\n')
+        self.assertEqual(make_patch("Hello!\n Das ist ein Test!\nEnd.\n", "Hello!\n This is a Test\nEnd.\n"),
+                         '@@ -1,3 +1,3 @@\n Hello!\n- Das ist ein Test!\n+ This is a Test\n End.\n')
diff --git a/tox.ini b/tox.ini
index f9198763..9691a382 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
 [tox]
-envlist = py35,py36,py37,lint,docs
+envlist = py{34,35,36,37},lint,docs
 skip_missing_interpreters = true
 
 [testenv]
@@ -24,3 +24,4 @@ deps=-rdocs/requirements.txt
      sphinx
 commands=
     sphinx-build -b html ./ ./html
+
-- 
GitLab