diff --git a/.gitignore b/.gitignore
index bd2eb18e5004e410231c59a0f5e0cb4c52591ae2..c07bf5af7eccadcefa2449603dc75cd6b9dd8577 100644
--- a/.gitignore
+++ b/.gitignore
@@ -61,3 +61,5 @@ target/
 
 # Vim temp files
 *.swp
+.ropeproject/
+*/.ropeproject/
diff --git a/.travis.yml b/.travis.yml
index a0164f0624344c5094e0ee46358edc9a9bbe89c7..2d9438abff68c18e630d0ec2358300fe1d032b11 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,10 +1,9 @@
 language: python
-python: "3.5"
+python:
+  - 3.5
 install:
  - pip install tox codecov
 script:
  - tox
-env:
-  - TOXENV=py35
 after_success:
   - codecov
diff --git a/bitshares/account.py b/bitshares/account.py
index 25efca5a740d74da2a3b630fd7bf2ba286009c69..a646f7d105afe3e2b3004debfce15769d9704e45 100644
--- a/bitshares/account.py
+++ b/bitshares/account.py
@@ -165,7 +165,7 @@ class Account(BlockchainObject):
             api="history"
         )
         if not mostrecent:
-            raise StopIteration
+            return
 
         if not first:
             # first = int(mostrecent[0].get("id").split(".")[2]) + 1
@@ -191,7 +191,7 @@ class Account(BlockchainObject):
                     cnt += 1
                     yield i
                     if limit >= 0 and cnt >= limit:
-                        raise StopIteration
+                        return
 
             if not txs:
                 break
diff --git a/bitshares/asset.py b/bitshares/asset.py
index 9682d71d7de608736b6205032e4977fef712c46c..a4c83dd5fffd7691e98ee68d8c2da2049dbb10db 100644
--- a/bitshares/asset.py
+++ b/bitshares/asset.py
@@ -48,7 +48,7 @@ class Asset(BlockchainObject):
         """
         asset = self.bitshares.rpc.get_asset(self.identifier)
         if not asset:
-            raise AssetDoesNotExistsException
+            raise AssetDoesNotExistsException(self.identifier)
         super(Asset, self).__init__(asset)
         if self.full:
             if "bitasset_data_id" in asset:
diff --git a/bitshares/bitshares.py b/bitshares/bitshares.py
index f974c5b8c3406f6e550e7cf9136635a58f1f3a7c..a87eb593bae688f6b89eab20ff0bcaf26f864033 100644
--- a/bitshares/bitshares.py
+++ b/bitshares/bitshares.py
@@ -24,7 +24,8 @@ from .exceptions import (
     MissingKeyError,
 )
 from .wallet import Wallet
-from .transactionbuilder import TransactionBuilder
+from .transactionbuilder import TransactionBuilder, ProposalBuilder
+from .utils import formatTime, test_proposal_in_buffer
 
 log = logging.getLogger(__name__)
 
@@ -35,16 +36,26 @@ class BitShares(object):
         :param str node: Node to connect to *(optional)*
         :param str rpcuser: RPC user *(optional)*
         :param str rpcpassword: RPC password *(optional)*
-        :param bool nobroadcast: Do **not** broadcast a transaction! *(optional)*
+        :param bool nobroadcast: Do **not** broadcast a transaction!
+            *(optional)*
         :param bool debug: Enable Debugging *(optional)*
-        :param array,dict,string keys: Predefine the wif keys to shortcut the wallet database *(optional)*
-        :param bool offline: Boolean to prevent connecting to network (defaults to ``False``) *(optional)*
-        :param str proposer: Propose a transaction using this proposer *(optional)*
-        :param int proposal_expiration: Expiration time (in seconds) for the proposal *(optional)*
-        :param int proposal_review: Review period (in seconds) for the proposal *(optional)*
-        :param int expiration: Delay in seconds until transactions are supposed to expire *(optional)*
-        :param str blocking: Wait for broadcasted transactions to be included in a block and return full transaction (can be "head" or "irrversible")
-        :param bool bundle: Do not broadcast transactions right away, but allow to bundle operations *(optional)*
+        :param array,dict,string keys: Predefine the wif keys to shortcut the
+            wallet database *(optional)*
+        :param bool offline: Boolean to prevent connecting to network (defaults
+            to ``False``) *(optional)*
+        :param str proposer: Propose a transaction using this proposer
+            *(optional)*
+        :param int proposal_expiration: Expiration time (in seconds) for the
+            proposal *(optional)*
+        :param int proposal_review: Review period (in seconds) for the proposal
+            *(optional)*
+        :param int expiration: Delay in seconds until transactions are supposed
+            to expire *(optional)*
+        :param str blocking: Wait for broadcasted transactions to be included
+            in a block and return full transaction (can be "head" or
+            "irrversible")
+        :param bool bundle: Do not broadcast transactions right away, but allow
+            to bundle operations *(optional)*
 
         Three wallet operation modes are possible:
 
@@ -64,8 +75,8 @@ class BitShares(object):
           signatures!
 
         If no node is provided, it will connect to the node of
-        http://uptick.rocks. It is **highly** recommended that you pick your own
-        node instead. Default settings can be changed with:
+        http://uptick.rocks. It is **highly** recommended that you
+        pick your own node instead. Default settings can be changed with:
 
         .. code-block:: python
 
@@ -84,7 +95,8 @@ class BitShares(object):
             bitshares = BitShares()
             print(bitshares.info())
 
-        All that is requires is for the user to have added a key with uptick
+        All that is requires is for the user to have added a key with
+        ``uptick``
 
         .. code-block:: bash
 
@@ -120,14 +132,14 @@ class BitShares(object):
         self.nobroadcast = bool(kwargs.get("nobroadcast", False))
         self.unsigned = bool(kwargs.get("unsigned", False))
         self.expiration = int(kwargs.get("expiration", 30))
-        self.proposer = kwargs.get("proposer", None)
-        self.proposal_expiration = int(kwargs.get("proposal_expiration", 60 * 60 * 24))
-        self.proposal_review = int(kwargs.get("proposal_review", 0))
         self.bundle = bool(kwargs.get("bundle", False))
         self.blocking = kwargs.get("blocking", False)
 
-        # Multiple txbuffers can be stored here
-        self._txbuffers = []
+        # Legacy Proposal attributes
+        self.proposer = kwargs.get("proposer", None)
+        self.proposal_expiration = int(
+            kwargs.get("proposal_expiration", 60 * 60 * 24))
+        self.proposal_review = int(kwargs.get("proposal_review", 0))
 
         # Store config for access through other Classes
         self.config = config
@@ -139,7 +151,9 @@ class BitShares(object):
                          **kwargs)
 
         self.wallet = Wallet(self.rpc, **kwargs)
-        self.new_txbuffer()
+
+        # txbuffers/propbuffer are initialized and cleared
+        self.clear()
 
     # -------------------------------------------------------------------------
     # Basic Calls
@@ -170,20 +184,35 @@ class BitShares(object):
             :func:`bitshares.wallet.create`.
 
             :param str pwd: Password to use for the new wallet
-            :raises bitshares.exceptions.WalletExists: if there is already a wallet created
+            :raises bitshares.exceptions.WalletExists: if there is already a
+                wallet created
         """
         self.wallet.create(pwd)
 
-    def finalizeOp(self, ops, account, permission):
+    def set_default_account(self, account):
+        """ Set the default account to be used
+        """
+        Account(account)
+        config["default_account"] = account
+
+    def finalizeOp(self, ops, account, permission, **kwargs):
         """ This method obtains the required private keys if present in
             the wallet, finalizes the transaction, signs it and
             broadacasts it
 
-            :param operation ops: The operation (or list of operaions) to broadcast
+            :param operation ops: The operation (or list of operaions) to
+                broadcast
             :param operation account: The account that authorizes the
                 operation
             :param string permission: The required permission for
                 signing (active, owner, posting)
+            :param object append_to: This allows to provide an instance of
+                ProposalsBuilder (see :func:`bitshares.new_proposal`) or
+                TransactionBuilder (see :func:`bitshares.new_tx()`) to specify
+                where to put a specific operation.
+
+            ... note:: ``append_to`` is exposed to every method used in the
+                BitShares class
 
             ... note::
 
@@ -197,9 +226,34 @@ class BitShares(object):
                 :class:`bitshares.transactionbuilder.TransactionBuilder`.
                 You may want to use your own txbuffer
         """
-        # Append transaction
-        self.txbuffer.appendOps(ops)
+        if "append_to" in kwargs and kwargs["append_to"]:
+            if self.proposer:
+                log.warn(
+                    "You may not use append_to and bitshares.proposer at "
+                    "the same time. Append bitshares.new_proposal(..) instead"
+                )
+            # Append to the append_to and return
+            append_to = kwargs["append_to"]
+            parent = append_to.get_parent()
+            assert isinstance(append_to, (TransactionBuilder, ProposalBuilder))
+            append_to.appendOps(ops)
+            # Add the signer to the buffer so we sign the tx properly
+            parent.appendSigner(account, permission)
+            # This returns as we used append_to, it does NOT broadcast, or sign
+            return append_to.get_parent()
+        elif self.proposer:
+            # Legacy proposer mode!
+            proposal = self.proposal()
+            proposal.set_proposer(self.proposer)
+            proposal.set_expiration(self.proposal_expiration)
+            proposal.set_review(self.proposal_review)
+            proposal.appendOps(ops)
+            # Go forward to see what the other options do ...
+        else:
+            # Append tot he default buffer
+            self.txbuffer.appendOps(ops)
 
+        # Add signing information, signer, sign and optionally broadcast
         if self.unsigned:
             # In case we don't want to sign anything
             self.txbuffer.addSigningInformation(account, permission)
@@ -255,35 +309,144 @@ class BitShares(object):
     def txbuffer(self):
         """ Returns the currently active tx buffer
         """
-        return self._txbuffers[self._current_txbuffer]
+        return self.tx()
 
-    def set_txbuffer(self, i):
-        """ Lets you switch the current txbuffer
+    @property
+    def propbuffer(self):
+        """ Return the default proposal buffer
+        """
+        return self.proposal()
 
-            :param int i: Id of the txbuffer
+    def tx(self):
+        """ Returns the default transaction buffer
         """
-        self._current_txbuffer = i
+        return self._txbuffers[0]
 
-    def get_txbuffer(self, i):
-        """ Returns the txbuffer with id i
+    def proposal(
+        self,
+        proposer=None,
+        proposal_expiration=None,
+        proposal_review=None
+    ):
+        """ Return the default proposal buffer
+
+            ... note:: If any parameter is set, the default proposal
+               parameters will be changed!
         """
-        if i < len(self._txbuffers):
-            return self._txbuffers[i]
+        if not self._propbuffer:
+            return self.new_proposal(
+                self.tx(),
+                proposer,
+                proposal_expiration,
+                proposal_review
+            )
+        if proposer:
+            self._propbuffer[0].set_proposer(proposer)
+        if proposal_expiration:
+            self._propbuffer[0].set_expiration(proposal_expiration)
+        if proposal_review:
+            self._propbuffer[0].set_review(proposal_review)
+        return self._propbuffer[0]
+
+    def new_proposal(
+        self,
+        parent=None,
+        proposer=None,
+        proposal_expiration=None,
+        proposal_review=None
+    ):
+        if not parent:
+            parent = self.tx()
+        if not proposal_expiration:
+            proposal_expiration = self.proposal_expiration
+
+        if not proposal_review:
+            proposal_review = self.proposal_review
 
-    def new_txbuffer(self, *args, **kwargs):
+        if not proposer:
+            if "default_account" in config:
+                proposer = config["default_account"]
+
+        # Else, we create a new object
+        proposal = ProposalBuilder(
+            proposer,
+            proposal_expiration,
+            proposal_review,
+            bitshares_instance=self,
+            parent=parent
+        )
+        if parent:
+            parent.appendOps(proposal)
+        self._propbuffer.append(proposal)
+        return proposal
+
+    def new_tx(self, *args, **kwargs):
         """ Let's obtain a new txbuffer
 
             :returns int txid: id of the new txbuffer
         """
-        self._txbuffers.append(TransactionBuilder(
+        builder = TransactionBuilder(
             *args,
             bitshares_instance=self,
             **kwargs
-        ))
-        id = len(self._txbuffers) - 1
-        self.set_txbuffer(id)
-        return id
+        )
+        self._txbuffers.append(builder)
+        return builder
+
+    def clear(self):
+        self._txbuffers = []
+        self._propbuffer = []
+        # Base/Default proposal/tx buffers
+        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, bitshares_instance=self)
+        amount = Amount(amount, asset, bitshares_instance=self)
+        to = Account(to, bitshares_instance=self)
+
+        memoObj = Memo(
+            from_account=account,
+            to_account=to,
+            bitshares_instance=self
+        )
+
+        op = operations.Transfer(**{
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "from": account["id"],
+            "to": to["id"],
+            "amount": {
+                "amount": int(amount),
+                "asset_id": amount.asset["id"]
+            },
+            "memo": memoObj.encrypt(memo),
+            "prefix": self.rpc.chain_params["prefix"]
+        })
+        return self.finalizeOp(op, account, "active", **kwargs)
 
+    # -------------------------------------------------------------------------
+    # Account related calls
+    # -------------------------------------------------------------------------
     def create_account(
         self,
         account_name,
@@ -300,11 +463,12 @@ class BitShares(object):
         additional_active_accounts=[],
         proxy_account="proxy-to-self",
         storekeys=True,
+        **kwargs
     ):
         """ Create new account on BitShares
 
-            The brainkey/password can be used to recover all generated keys (see
-            `bitsharesbase.account` for more details.
+            The brainkey/password can be used to recover all generated keys
+            (see `bitsharesbase.account` for more details.
 
             By default, this call will use ``default_account`` to
             register a new name ``account_name`` with all keys being
@@ -335,10 +499,14 @@ class BitShares(object):
                                  keys will be derived
             :param array additional_owner_keys:  Additional owner public keys
             :param array additional_active_keys: Additional active public keys
-            :param array additional_owner_accounts: Additional owner account names
-            :param array additional_active_accounts: Additional acctive account names
-            :param bool storekeys: Store new keys in the wallet (default: ``True``)
-            :raises AccountExistsException: if the account already exists on the blockchain
+            :param array additional_owner_accounts: Additional owner account
+                names
+            :param array additional_active_accounts: Additional acctive account
+                names
+            :param bool storekeys: Store new keys in the wallet (default:
+                ``True``)
+            :raises AccountExistsException: if the account already exists on
+                the blockchain
 
         """
         if not registrar and config["default_account"]:
@@ -379,9 +547,12 @@ class BitShares(object):
                 self.wallet.addPrivateKey(active_privkey)
                 self.wallet.addPrivateKey(memo_privkey)
         elif (owner_key and active_key and memo_key):
-            active_pubkey = PublicKey(active_key, prefix=self.rpc.chain_params["prefix"])
-            owner_pubkey = PublicKey(owner_key, prefix=self.rpc.chain_params["prefix"])
-            memo_pubkey = PublicKey(memo_key, prefix=self.rpc.chain_params["prefix"])
+            active_pubkey = PublicKey(
+                active_key, prefix=self.rpc.chain_params["prefix"])
+            owner_pubkey = PublicKey(
+                owner_key, prefix=self.rpc.chain_params["prefix"])
+            memo_pubkey = PublicKey(
+                memo_key, prefix=self.rpc.chain_params["prefix"])
         else:
             raise ValueError(
                 "Call incomplete! Provide either a password or public keys!"
@@ -409,7 +580,8 @@ class BitShares(object):
             active_accounts_authority.append([addaccount["id"], 1])
 
         # voting account
-        voting_account = Account(proxy_account or "proxy-to-self")
+        voting_account = Account(
+            proxy_account or "proxy-to-self", bitshares_instance=self)
 
         op = {
             "fee": {"amount": 0, "asset_id": "1.3.0"},
@@ -436,46 +608,27 @@ class BitShares(object):
             "prefix": self.rpc.chain_params["prefix"]
         }
         op = operations.Account_create(**op)
-        return self.finalizeOp(op, registrar, "active")
+        return self.finalizeOp(op, registrar, "active", **kwargs)
 
-    def transfer(self, to, amount, asset, memo="", account=None):
-        """ Transfer an asset to another account.
+    def upgrade_account(self, account=None, **kwargs):
+        """ Upgrade an account to Lifetime membership
 
-            :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``
+            :param str account: (optional) the account to allow access
+                to (defaults to ``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, bitshares_instance=self)
-        amount = Amount(amount, asset, bitshares_instance=self)
-        to = Account(to, bitshares_instance=self)
-
-        memoObj = Memo(
-            from_account=account,
-            to_account=to,
-            bitshares_instance=self
-        )
-
-        op = operations.Transfer(**{
+        op = operations.Account_upgrade(**{
             "fee": {"amount": 0, "asset_id": "1.3.0"},
-            "from": account["id"],
-            "to": to["id"],
-            "amount": {
-                "amount": int(amount),
-                "asset_id": amount.asset["id"]
-            },
-            "memo": memoObj.encrypt(memo),
+            "account_to_upgrade": account["id"],
+            "upgrade_to_lifetime_member": True,
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account, "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
     def _test_weights_treshold(self, authority):
         """ This method raises an error if the threshold of an authority cannot
@@ -486,14 +639,18 @@ class BitShares(object):
         """
         weights = 0
         for a in authority["account_auths"]:
-            weights += a[1]
+            weights += int(a[1])
         for a in authority["key_auths"]:
-            weights += a[1]
+            weights += int(a[1])
         if authority["weight_threshold"] > weights:
             raise ValueError("Threshold too restrictive!")
+        if authority["weight_threshold"] == 0:
+            raise ValueError("Cannot have threshold of 0")
 
-    def allow(self, foreign, weight=None, permission="active",
-              account=None, threshold=None):
+    def allow(
+        self, foreign, weight=None, permission="active",
+        account=None, threshold=None, **kwargs
+    ):
         """ Give additional access to an account by some other public
             key or account.
 
@@ -555,12 +712,14 @@ class BitShares(object):
             "prefix": self.rpc.chain_params["prefix"]
         })
         if permission == "owner":
-            return self.finalizeOp(op, account["name"], "owner")
+            return self.finalizeOp(op, account["name"], "owner", **kwargs)
         else:
-            return self.finalizeOp(op, account["name"], "active")
+            return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def disallow(self, foreign, permission="active",
-                 account=None, threshold=None):
+    def disallow(
+        self, foreign, permission="active",
+        account=None, threshold=None, **kwargs
+    ):
         """ Remove additional access to an account by some other public
             key or account.
 
@@ -609,6 +768,8 @@ class BitShares(object):
                     "Unknown foreign account or unvalid public key"
                 )
 
+        if not affected_items:
+            raise ValueError("Changes nothing!")
         removed_weight = affected_items[0][1]
 
         # Define threshold
@@ -634,11 +795,11 @@ class BitShares(object):
             "extensions": {}
         })
         if permission == "owner":
-            return self.finalizeOp(op, account["name"], "owner")
+            return self.finalizeOp(op, account["name"], "owner", **kwargs)
         else:
-            return self.finalizeOp(op, account["name"], "active")
+            return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def update_memo_key(self, key, account=None):
+    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
@@ -664,12 +825,12 @@ class BitShares(object):
             "new_options": account["options"],
             "extensions": {}
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
     # -------------------------------------------------------------------------
     #  Approval and Disapproval of witnesses, workers, committee, and proposals
     # -------------------------------------------------------------------------
-    def approvewitness(self, witnesses, account=None):
+    def approvewitness(self, witnesses, account=None, **kwargs):
         """ Approve a witness
 
             :param list witnesses: list of Witness name or id
@@ -704,9 +865,9 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def disapprovewitness(self, witnesses, account=None):
+    def disapprovewitness(self, witnesses, account=None, **kwargs):
         """ Disapprove a witness
 
             :param list witnesses: list of Witness name or id
@@ -742,9 +903,9 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def approvecommittee(self, committees, account=None):
+    def approvecommittee(self, committees, account=None, **kwargs):
         """ Approve a committee
 
             :param list committees: list of committee member name or id
@@ -779,9 +940,9 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def disapprovecommittee(self, committees, account=None):
+    def disapprovecommittee(self, committees, account=None, **kwargs):
         """ Disapprove a committee
 
             :param list committees: list of committee name or id
@@ -817,9 +978,95 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def approveworker(self, workers, account=None):
+    def approveproposal(
+        self, proposal_ids, account=None, approver=None, **kwargs
+    ):
+        """ Approve Proposal
+
+            :param list proposal_id: Ids of the proposals
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        from .proposal import Proposal
+        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, bitshares_instance=self)
+        is_key = approver and approver[:3] == self.rpc.chain_params["prefix"]
+        if not approver and not is_key:
+            approver = account
+        elif approver and not is_key:
+            approver = Account(approver, bitshares_instance=self)
+        else:
+            approver = PublicKey(approver)
+
+        if not isinstance(proposal_ids, (list, set, tuple)):
+            proposal_ids = {proposal_ids}
+
+        op = []
+        for proposal_id in proposal_ids:
+            proposal = Proposal(proposal_id, bitshares_instance=self)
+            update_dict = {
+                "fee": {"amount": 0, "asset_id": "1.3.0"},
+                'fee_paying_account': account["id"],
+                'proposal': proposal["id"],
+                'active_approvals_to_add': [approver["id"]],
+                "prefix": self.rpc.chain_params["prefix"]
+            }
+            if is_key:
+                update_dict.update({
+                    'key_approvals_to_add': [str(approver)],
+                })
+            else:
+                update_dict.update({
+                    'active_approvals_to_add': [approver["id"]],
+                })
+            op.append(operations.Proposal_update(**update_dict))
+        if is_key:
+            self.txbuffer.appendSigner(account["name"], "active")
+        return self.finalizeOp(op, approver["name"], "active", **kwargs)
+
+    def disapproveproposal(
+        self, proposal_ids, account=None, approver=None, **kwargs
+    ):
+        """ Disapprove Proposal
+
+            :param list proposal_ids: Ids of the proposals
+            :param str account: (optional) the account to allow access
+                to (defaults to ``default_account``)
+        """
+        from .proposal import Proposal
+        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, bitshares_instance=self)
+        if not approver:
+            approver = account
+        else:
+            approver = Account(approver, bitshares_instance=self)
+
+        if not isinstance(proposal_ids, (list, set, tuple)):
+            proposal_ids = {proposal_ids}
+
+        op = []
+        for proposal_id in proposal_ids:
+            proposal = Proposal(proposal_id, bitshares_instance=self)
+            op.append(operations.Proposal_update(**{
+                "fee": {"amount": 0, "asset_id": "1.3.0"},
+                'fee_paying_account': account["id"],
+                'proposal': proposal["id"],
+                'active_approvals_to_remove': [approver["id"]],
+                "prefix": self.rpc.chain_params["prefix"]
+            }))
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
+
+    def approveworker(self, workers, account=None, **kwargs):
         """ Approve a worker
 
             :param list workers: list of worker member name or id
@@ -849,9 +1096,9 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def disapproveworker(self, workers, account=None):
+    def disapproveworker(self, workers, account=None, **kwargs):
         """ Disapprove a worker
 
             :param list workers: list of worker name or id
@@ -882,9 +1129,9 @@ class BitShares(object):
             "extensions": {},
             "prefix": self.rpc.chain_params["prefix"]
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def cancel(self, orderNumbers, account=None):
+    def cancel(self, orderNumbers, account=None, **kwargs):
         """ Cancels an order you have placed in a given market. Requires
             only the "orderNumbers". An order number takes the form
             ``1.7.xxx``.
@@ -910,9 +1157,9 @@ class BitShares(object):
                     "order": order,
                     "extensions": [],
                     "prefix": self.rpc.chain_params["prefix"]}))
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def vesting_balance_withdraw(self, vesting_id, amount=None, account=None):
+    def vesting_balance_withdraw(self, vesting_id, amount=None, account=None, **kwargs):
         """ Withdraw vesting balance
 
             :param str vesting_id: Id of the vesting object
@@ -944,89 +1191,6 @@ class BitShares(object):
         })
         return self.finalizeOp(op, account["name"], "active")
 
-    def approveproposal(self, proposal_ids, account=None, approver=None):
-        """ Approve Proposal
-
-            :param list proposal_id: Ids of the proposals
-            :param str account: (optional) the account to allow access
-                to (defaults to ``default_account``)
-        """
-        from .proposal import Proposal
-        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, bitshares_instance=self)
-        is_key = approver and approver[:3] == self.rpc.chain_params["prefix"]
-        if not approver and not is_key:
-            approver = account
-        elif approver and not is_key:
-            approver = Account(approver, bitshares_instance=self)
-        else:
-            approver = PublicKey(approver)
-
-        if not isinstance(proposal_ids, (list, set, tuple)):
-            proposal_ids = {proposal_ids}
-
-        op = []
-        for proposal_id in proposal_ids:
-            proposal = Proposal(proposal_id, bitshares_instance=self)
-            update_dict = {
-                "fee": {"amount": 0, "asset_id": "1.3.0"},
-                'fee_paying_account': account["id"],
-                'proposal': proposal["id"],
-                "prefix": self.rpc.chain_params["prefix"]
-            }
-            if is_key:
-                update_dict.update({
-                    'key_approvals_to_add': [str(approver)],
-                })
-            else:
-                update_dict.update({
-                    'active_approvals_to_add': [approver["id"]],
-                })
-            op.append(operations.Proposal_update(**update_dict))
-        if is_key:
-            self.txbuffer.appendSigner(account["name"], "active")
-            return self.finalizeOp(op, approver, "active")
-        else:
-            return self.finalizeOp(op, approver["name"], "active")
-
-    def disapproveproposal(self, proposal_ids, account=None, approver=None):
-        """ Disapprove Proposal
-
-            :param list proposal_ids: Ids of the proposals
-            :param str account: (optional) the account to allow access
-                to (defaults to ``default_account``)
-        """
-        from .proposal import Proposal
-        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, bitshares_instance=self)
-        if not approver:
-            approver = account
-        else:
-            approver = Account(approver, bitshares_instance=self)
-
-        if not isinstance(proposal_ids, (list, set, tuple)):
-            proposal_ids = {proposal_ids}
-
-        op = []
-        for proposal_id in proposal_ids:
-            proposal = Proposal(proposal_id, bitshares_instance=self)
-            op.append(operations.Proposal_update(**{
-                "fee": {"amount": 0, "asset_id": "1.3.0"},
-                'fee_paying_account': account["id"],
-                'proposal': proposal["id"],
-                'active_approvals_to_remove': [approver["id"]],
-                "prefix": self.rpc.chain_params["prefix"]
-            }))
-        return self.finalizeOp(op, account["name"], "active")
-
     def publish_price_feed(
         self,
         symbol,
@@ -1092,27 +1256,7 @@ class BitShares(object):
         })
         return self.finalizeOp(op, account["name"], "active")
 
-    def upgrade_account(self, account=None):
-        """ Upgrade an account to Lifetime membership
-
-            :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, bitshares_instance=self)
-        op = operations.Account_upgrade(**{
-            "fee": {"amount": 0, "asset_id": "1.3.0"},
-            "account_to_upgrade": account["id"],
-            "upgrade_to_lifetime_member": True,
-            "prefix": self.rpc.chain_params["prefix"]
-        })
-        return self.finalizeOp(op, account["name"], "active")
-
-    def update_witness(self, witness_identifier, url=None, key=None):
+    def update_witness(self, witness_identifier, url=None, key=None, **kwargs):
         """ Upgrade a witness account
 
             :param str witness_identifier: Identifier for the witness
@@ -1129,9 +1273,9 @@ class BitShares(object):
             "new_url": url,
             "new_signing_key": key,
         })
-        return self.finalizeOp(op, account["name"], "active")
+        return self.finalizeOp(op, account["name"], "active", **kwargs)
 
-    def reserve(self, amount, account=None):
+    def reserve(self, amount, account=None, **kwargs):
         """ Reserve/Burn an amount of this shares
 
             This removes the shares from the supply
@@ -1155,7 +1299,7 @@ class BitShares(object):
                 "asset_id": amount["asset"]["id"]},
             "extensions": []
         })
-        return self.finalizeOp(op, account, "active")
+        return self.finalizeOp(op, account, "active", **kwargs)
 
     def create_worker(
         self,
@@ -1166,7 +1310,8 @@ class BitShares(object):
         begin=None,
         payment_type="vesting",
         pay_vesting_period_days=0,
-        account=None
+        account=None,
+        **kwargs
     ):
         """ Reserve/Burn an amount of this shares
 
@@ -1220,9 +1365,9 @@ class BitShares(object):
             "url": url,
             "initializer": initializer
         })
-        return self.finalizeOp(op, account, "active")
+        return self.finalizeOp(op, account, "active", **kwargs)
 
-    def fund_fee_pool(self, symbol, amount, account=None):
+    def fund_fee_pool(self, symbol, amount, account=None, **kwargs):
         """ Fund the fee pool of an asset
 
             :param str symbol: The symbol to fund the fee pool of
@@ -1245,4 +1390,4 @@ class BitShares(object):
             "amount": int(float(amount) * 10 ** asset["precision"]),
             "extensions": []
         })
-        return self.finalizeOp(op, account, "active")
+        return self.finalizeOp(op, account, "active", **kwargs)
diff --git a/bitshares/block.py b/bitshares/block.py
index 79ea6737309bc2cc4a5c17da405251123f93fd2c..4a3af34a5345254e2a02f06a8a2a3082f4aa9b4b 100644
--- a/bitshares/block.py
+++ b/bitshares/block.py
@@ -1,14 +1,14 @@
-from bitshares.instance import shared_bitshares_instance
-from .blockchainobject import BlockchainObject
 from .exceptions import BlockDoesNotExistsException
 from .utils import parse_time
+from .blockchainobject import BlockchainObject
 
 
 class Block(BlockchainObject):
     """ Read a single block from the chain
 
         :param int block: block number
-        :param bitshares.bitshares.BitShares bitshares_instance: BitShares instance
+        :param bitshares.bitshares.BitShares bitshares_instance: BitShares
+            instance
         :param bool lazy: Use lazy loading
 
         Instances of this class are dictionaries that come with additional
diff --git a/bitshares/blockchainobject.py b/bitshares/blockchainobject.py
index d47df3a6bfb99b868b77f7b9ab84a12c76f8291b..af90fe7eef7ac724a5783bf03bb39a8e3d576a8f 100644
--- a/bitshares/blockchainobject.py
+++ b/bitshares/blockchainobject.py
@@ -4,17 +4,21 @@ from datetime import datetime, timedelta
 
 class ObjectCache(dict):
 
-    max_cache_objects = 1000
-
-    def __init__(self, initial_data={}, max_cache_objects=1000):
+    def __init__(self, initial_data={}, default_expiration=10):
         super().__init__(initial_data)
-        ObjectCache.max_cache_objects = max_cache_objects
+        self.default_expiration = default_expiration
+
+    def clear(self):
+        """ Clears the whole cache
+        """
+        dict.__init__(self, dict())
 
     def __setitem__(self, key, value):
         if key in self:
             del self[key]
         data = {
-            "expires": datetime.utcnow() + timedelta(seconds=10),
+            "expires": datetime.utcnow() + timedelta(
+                seconds=self.default_expiration),
             "data": value
         }
         dict.__setitem__(self, key, data)
@@ -38,7 +42,8 @@ class ObjectCache(dict):
         return False
 
     def __str__(self):
-        return "ObjectCache(n={}, max_cache_objects={})".format(len(self.keys()), self.max_cache_objects)
+        return "ObjectCache(n={}, default_expiration={})".format(
+            len(self.keys()), self.default_expiration)
 
 
 class BlockchainObject(dict):
@@ -65,21 +70,10 @@ class BlockchainObject(dict):
         self.cached = False
         self.identifier = None
 
-        def test_valid_objectid(i):
-            if "." not in i:
-                return False
-            parts = i.split(".")
-            if len(parts) == 3:
-                try:
-                    [int(x) for x in parts]
-                    return True
-                except:
-                    pass
-                return False
-
         # We don't read lists, sets, or tuples
         if isinstance(data, (list, set, tuple)):
-            raise ValueError("Cannot interpret lists! Please load elements individually!")
+            raise ValueError(
+                "Cannot interpret lists! Please load elements individually!")
 
         if klass and isinstance(data, klass):
             self.identifier = data.get("id")
@@ -98,7 +92,7 @@ class BlockchainObject(dict):
             self.identifier = data
         else:
             self.identifier = data
-            if test_valid_objectid(self.identifier):
+            if self.test_valid_objectid(self.identifier):
                 # Here we assume we deal with an id
                 self.testid(self.identifier)
             if self.iscached(data):
@@ -110,6 +104,23 @@ class BlockchainObject(dict):
             self.cache()
             self.cached = True
 
+    @staticmethod
+    def clear_cache():
+        if BlockchainObject._cache:
+            BlockchainObject._cache.clear()
+
+    def test_valid_objectid(self, i):
+        if "." not in i:
+            return False
+        parts = i.split(".")
+        if len(parts) == 3:
+            try:
+                [int(x) for x in parts]
+                return True
+            except:
+                pass
+            return False
+
     def testid(self, id):
         parts = id.split(".")
         if not self.type_id:
@@ -119,9 +130,11 @@ class BlockchainObject(dict):
             self.type_ids = [self.type_id]
 
         assert int(parts[0]) == self.space_id,\
-            "Valid id's for {} are {}.{}.x".format(self.__class__.__name__, self.space_id, self.type_ida)
+            "Valid id's for {} are {}.{}.x".format(
+                self.__class__.__name__, self.space_id, self.type_id)
         assert int(parts[1]) in self.type_ids,\
-            "Valid id's for {} are {}.{}.x".format(self.__class__.__name__, self.space_id, self.type_ids)
+            "Valid id's for {} are {}.{}.x".format(
+                self.__class__.__name__, self.space_id, self.type_ids)
 
     def cache(self):
         # store in cache
@@ -150,4 +163,5 @@ class BlockchainObject(dict):
         return super().__contains__(key)
 
     def __repr__(self):
-        return "<%s %s>" % (self.__class__.__name__, str(self.identifier))
+        return "<%s %s>" % (
+            self.__class__.__name__, str(self.identifier))
diff --git a/bitshares/committee.py b/bitshares/committee.py
index ca17118814989b707f2bc7d547b977b47c18decf..9d0cece7a52ceaeed52ea1cced7c62bae6e41a77 100644
--- a/bitshares/committee.py
+++ b/bitshares/committee.py
@@ -1,4 +1,3 @@
-from bitshares.instance import shared_bitshares_instance
 from .account import Account
 from .exceptions import CommitteeMemberDoesNotExistsException
 from .blockchainobject import BlockchainObject
@@ -8,20 +7,35 @@ class Committee(BlockchainObject):
     """ Read data about a Committee Member in the chain
 
         :param str member: Name of the Committee Member
-        :param bitshares bitshares_instance: BitShares() instance to use when accesing a RPC
+        :param bitshares bitshares_instance: BitShares() instance to use when
+            accesing a RPC
         :param bool lazy: Use lazy loading
 
     """
     type_id = 5
 
     def refresh(self):
-        account = Account(self.identifier)
-        member = self.bitshares.rpc.get_committee_member_by_account(account["id"])
+        if self.test_valid_objectid(self.identifier):
+            _, i, _ = self.identifier.split(".")
+            if int(i) == 2:
+                account = Account(self.identifier)
+                member = self.bitshares.rpc.get_committee_member_by_account(
+                    account["id"])
+            elif int(i) == 5:
+                member = self.bitshares.rpc.get_object(self.identifier)
+            else:
+                raise CommitteeMemberDoesNotExistsException
+        else:
+            # maybe identifier is an account name
+            account = Account(self.identifier)
+            member = self.bitshares.rpc.get_committee_member_by_account(
+                account["id"])
+
         if not member:
             raise CommitteeMemberDoesNotExistsException
         super(Committee, self).__init__(member)
-        self.cached = True
+        self.account_id = account["id"]
 
     @property
     def account(self):
-        return Account(self.identifier)
+        return Account(self.account_id)
diff --git a/bitshares/exceptions.py b/bitshares/exceptions.py
index 2b5e8288e02d54333f59b55c468a1c63fcfc0775..0443b118398772fd83acb625eb521db33a608543 100644
--- a/bitshares/exceptions.py
+++ b/bitshares/exceptions.py
@@ -24,68 +24,68 @@ class AssetDoesNotExistsException(Exception):
 
 
 class InvalidAssetException(Exception):
-    """ The used asset is invalid in this context
+    """ An invalid asset has been provided
     """
     pass
 
 
-class BlockDoesNotExistsException(Exception):
-    """ The block does not exist
+class InsufficientAuthorityError(Exception):
+    """ The transaction requires signature of a higher authority
     """
     pass
 
 
-class WitnessDoesNotExistsException(Exception):
-    """ The witness does not exist
+class MissingKeyError(Exception):
+    """ A required key couldn't be found in the wallet
     """
     pass
 
 
-class CommitteeMemberDoesNotExistsException(Exception):
-    """ Committee Member does not exist
+class InvalidWifError(Exception):
+    """ The provided private Key has an invalid format
     """
     pass
 
 
-class VestingBalanceDoesNotExistsException(Exception):
-    """ Vesting Balance does not exist
+class ProposalDoesNotExistException(Exception):
+    """ The proposal does not exist
     """
     pass
 
 
-class ProposalDoesNotExistException(Exception):
-    """ The proposal does not exist
+class BlockDoesNotExistsException(Exception):
+    """ The block does not exist
     """
     pass
 
 
-class InsufficientAuthorityError(Exception):
-    """ The transaction requires signature of a higher authority
+class NoWalletException(Exception):
+    """ No Wallet could be found, please use :func:`peerplays.wallet.create` to
+        create a new wallet
     """
     pass
 
 
-class MissingKeyError(Exception):
-    """ A required key couldn't be found in the wallet
+class WitnessDoesNotExistsException(Exception):
+    """ The witness does not exist
     """
     pass
 
 
-class InvalidWifError(Exception):
-    """ The provided private Key has an invalid format
+class WrongMasterPasswordException(Exception):
+    """ The password provided could not properly unlock the wallet
     """
     pass
 
 
-class NoWalletException(Exception):
-    """ No Wallet could be found, please use :func:`bitshares.wallet.create` to
-        create a new wallet
+class CommitteeMemberDoesNotExistsException(Exception):
+    """ Committee Member does not exist
     """
     pass
 
 
-class WrongMasterPasswordException(Exception):
-    """ The password provided could not properly unlock the wallet
+class VestingBalanceDoesNotExistsException(Exception):
+    """ Vesting Balance does not exist
     """
     pass
 
@@ -94,3 +94,9 @@ class WorkerDoesNotExistsException(Exception):
     """ Worker does not exist
     """
     pass
+
+
+class ObjectNotInProposalBuffer(Exception):
+    """ Object was not found in proposal
+    """
+    pass
diff --git a/bitshares/instance.py b/bitshares/instance.py
index c604f6c6c408238e3eb7f640e65cea0c7f88cd39..72c85a7e4716a8896f4d2112d43b8291e67ea9f1 100644
--- a/bitshares/instance.py
+++ b/bitshares/instance.py
@@ -1,24 +1,33 @@
 import bitshares as bts
 
-_shared_bitshares_instance = None
+
+class SharedInstance():
+    instance = None
 
 
 def shared_bitshares_instance():
-    """ This method will initialize ``_shared_bitshares_instance`` and return it.
+    """ This method will initialize ``SharedInstance.instance`` and return it.
         The purpose of this method is to have offer single default
         bitshares instance that can be reused by multiple classes.
     """
-    global _shared_bitshares_instance
-    if not _shared_bitshares_instance:
-        _shared_bitshares_instance = bts.BitShares()
-    return _shared_bitshares_instance
+    if not SharedInstance.instance:
+        clear_cache()
+        SharedInstance.instance = bts.BitShares()
+    return SharedInstance.instance
 
 
 def set_shared_bitshares_instance(bitshares_instance):
     """ This method allows us to override default bitshares instance for all users of
-        ``_shared_bitshares_instance``.
+        ``SharedInstance.instance``.
 
         :param bitshares.bitshares.BitShares bitshares_instance: BitShares instance
     """
-    global _shared_bitshares_instance
-    _shared_bitshares_instance = bitshares_instance
+    clear_cache()
+    SharedInstance.instance = bitshares_instance
+
+
+def clear_cache():
+    """ Clear Caches
+    """
+    from .blockchainobject import BlockchainObject
+    BlockchainObject.clear_cache()
diff --git a/bitshares/price.py b/bitshares/price.py
index f94f90024ac5f7658dc364817c7e7f5d8dfd9865..8161c45f84cc2b30e586f4cb8503bed77794733a 100644
--- a/bitshares/price.py
+++ b/bitshares/price.py
@@ -5,7 +5,6 @@ from .account import Account
 from .amount import Amount
 from .asset import Asset
 from .utils import formatTimeString
-from .witness import Witness
 from .utils import parse_time
 
 
@@ -393,7 +392,6 @@ class Order(Price):
                 'deleted' key which is set to ``True`` and all other
                 data be ``None``.
     """
-
     def __init__(self, *args, bitshares_instance=None, **kwargs):
 
         self.bitshares = bitshares_instance or shared_bitshares_instance()
diff --git a/bitshares/transactionbuilder.py b/bitshares/transactionbuilder.py
index fdae81ee07b75fd9f67c2ffd4edc6f7aea89480b..17f1a6f71e826cd430a8001875a7f5bd811de05e 100644
--- a/bitshares/transactionbuilder.py
+++ b/bitshares/transactionbuilder.py
@@ -13,6 +13,116 @@ import logging
 log = logging.getLogger(__name__)
 
 
+class ProposalBuilder:
+    """ Proposal Builder allows us to construct an independent Proposal
+        that may later be added to an instance ot TransactionBuilder
+
+        :param str proposer: Account name of the proposing user
+        :param int proposal_expiration: Number seconds until the proposal is
+            supposed to expire
+        :param int proposal_review: Number of seconds for review of the
+            proposal
+        :param bitshares.transactionbuilder.TransactionBuilder: Specify
+            your own instance of transaction builder (optional)
+        :param bitshares.bitshares.BitShares bitshares_instance: BitShares
+            instance
+    """
+    def __init__(
+        self,
+        proposer,
+        proposal_expiration=None,
+        proposal_review=None,
+        parent=None,
+        bitshares_instance=None,
+        *args,
+        **kwargs
+    ):
+        self.bitshares = bitshares_instance or shared_bitshares_instance()
+
+        self.set_expiration(proposal_expiration or 2 * 24 * 60 * 60)
+        self.set_review(proposal_review)
+        self.set_parent(parent)
+        self.set_proposer(proposer)
+        self.ops = list()
+
+    def is_empty(self):
+        return not (len(self.ops) > 0)
+
+    def set_proposer(self, p):
+        self.proposer = p
+
+    def set_expiration(self, p):
+        self.proposal_expiration = p
+
+    def set_review(self, p):
+        self.proposal_review = p
+
+    def set_parent(self, p):
+        self.parent = p
+
+    def appendOps(self, ops, append_to=None):
+        """ Append op(s) to the transaction builder
+
+            :param list ops: One or a list of operations
+        """
+        if isinstance(ops, list):
+            self.ops.extend(ops)
+        else:
+            self.ops.append(ops)
+        parent = self.parent
+        if parent:
+            parent._set_require_reconstruction()
+
+    def list_operations(self):
+        return [Operation(o) for o in self.ops]
+
+    def broadcast(self):
+        assert self.parent, "No parent transaction provided!"
+        self.parent._set_require_reconstruction()
+        return self.parent.broadcast()
+
+    def get_parent(self):
+        """ This allows to referr to the actual parent of the Proposal
+        """
+        return self.parent
+
+    def __repr__(self):
+        return "<Proposal ops=%s>" % str(self.ops)
+
+    def json(self):
+        """ Return the json formated version of this proposal
+        """
+        raw = self.get_raw()
+        if not raw:
+            return dict()
+        return raw.json()
+
+    def get_raw(self):
+        """ Returns an instance of base "Operations" for further processing
+        """
+        if not self.ops:
+            return
+        ops = [operations.Op_wrapper(op=o) for o in list(self.ops)]
+        proposer = Account(
+            self.proposer,
+            bitshares_instance=self.bitshares
+        )
+        data = {
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "fee_paying_account": proposer["id"],
+            "expiration_time": transactions.formatTimeFromNow(
+                self.proposal_expiration),
+            "proposed_ops": [o.json() for o in ops],
+            "extensions": []
+        }
+        if self.proposal_review:
+            data.update({
+                "review_period_seconds": self.proposal_review
+            })
+        ops = operations.Proposal_create(**data)
+        return Operation(ops)
+
+
 class TransactionBuilder(dict):
     """ This class simplifies the creation of transactions by adding
         operations and signers.
@@ -31,22 +141,51 @@ class TransactionBuilder(dict):
         # Do we need to reconstruct the tx from self.ops?
         self._require_reconstruction = True
 
-    def is_signed(self):
+    def is_empty(self):
+        return not (len(self.ops) > 0)
+
+    def list_operations(self):
+        return [Operation(o) for o in self.ops]
+
+    def _is_signed(self):
         return "signatures" in self and self["signatures"]
 
-    def is_constructed(self):
+    def _is_constructed(self):
         return "expiration" in self and self["expiration"]
 
-    def is_require_reconstruction(self):
+    def _is_require_reconstruction(self):
         return self._require_reconstruction
 
-    def set_require_reconstruction(self):
+    def _set_require_reconstruction(self):
         self._require_reconstruction = True
 
-    def unset_require_reconstruction(self):
+    def _unset_require_reconstruction(self):
         self._require_reconstruction = False
 
-    def appendOps(self, ops):
+    def __repr__(self):
+        return str(self)
+
+    def __str__(self):
+        return str(self.json())
+
+    def __getitem__(self, key):
+        if key not in self:
+            self.constructTx()
+        return dict(self).__getitem__(key)
+
+    def get_parent(self):
+        """ TransactionBuilders don't have parents, they are their own parent
+        """
+        return self
+
+    def json(self):
+        """ Show the transaction as plain json
+        """
+        if not self._is_constructed() or self._is_require_reconstruction():
+            self.constructTx()
+        return dict(self)
+
+    def appendOps(self, ops, append_to=None):
         """ Append op(s) to the transaction builder
 
             :param list ops: One or a list of operations
@@ -55,7 +194,7 @@ class TransactionBuilder(dict):
             self.ops.extend(ops)
         else:
             self.ops.append(ops)
-        self.set_require_reconstruction()
+        self._set_require_reconstruction()
 
     def appendSigner(self, account, permission):
         """ Try to obtain the wif key from the wallet by telling which account
@@ -84,10 +223,10 @@ class TransactionBuilder(dict):
 
             return r
 
-        if account not in self.available_signers:
+        if account not in self.signing_accounts:
             # is the account an instance of public key?
             if isinstance(account, PublicKey):
-                self.wifs.append(
+                self.wifs.add(
                     self.bitshares.wallet.getPrivateKeyForPublicKey(
                         str(account)
                     )
@@ -98,9 +237,10 @@ class TransactionBuilder(dict):
                 keys = fetchkeys(account, permission)
                 if permission != "owner":
                     keys.extend(fetchkeys(account, "owner"))
-                self.wifs.extend([x[0] for x in keys])
+                for x in keys:
+                    self.wifs.add(x[0])
 
-            self.available_signers.append(account)
+            self.signing_accounts.append(account)
 
     def appendWif(self, wif):
         """ Add a wif that should be used for signing of the transaction.
@@ -108,7 +248,7 @@ class TransactionBuilder(dict):
         if wif:
             try:
                 PrivateKey(wif)
-                self.wifs.append(wif)
+                self.wifs.add(wif)
             except:
                 raise InvalidWifError
 
@@ -116,25 +256,19 @@ class TransactionBuilder(dict):
         """ Construct the actual transaction and store it in the class's dict
             store
         """
-        if self.bitshares.proposer:
-            ops = [operations.Op_wrapper(op=o) for o in list(self.ops)]
-            proposer = Account(
-                self.bitshares.proposer,
-                bitshares_instance=self.bitshares
-            )
-            ops = operations.Proposal_create(**{
-                "fee": {"amount": 0, "asset_id": "1.3.0"},
-                "fee_paying_account": proposer["id"],
-                "expiration_time": transactions.formatTimeFromNow(
-                    self.bitshares.proposal_expiration),
-                "proposed_ops": [o.json() for o in ops],
-                "review_period_seconds": self.bitshares.proposal_review,
-                "extensions": []
-            })
-            ops = [Operation(ops)]
-        else:
-            ops = [Operation(o) for o in list(self.ops)]
+        ops = list()
+        for op in self.ops:
+            if isinstance(op, ProposalBuilder):
+                # This operation is a proposal an needs to be deal with
+                # differently
+                proposals = op.get_raw()
+                if proposals:
+                    ops.append(proposals)
+            else:
+                # otherwise, we simply wrap ops into Operations
+                ops.extend([Operation(op)])
 
+        # We no wrap everything into an actual transaction
         ops = transactions.addRequiredFees(self.bitshares.rpc, ops)
         expiration = transactions.formatTimeFromNow(self.bitshares.expiration)
         ref_block_num, ref_block_prefix = transactions.getBlockParams(
@@ -146,7 +280,7 @@ class TransactionBuilder(dict):
             operations=ops
         )
         super(TransactionBuilder, self).__init__(self.tx.json())
-        self.unset_require_reconstruction()
+        self._unset_require_reconstruction()
 
     def sign(self):
         """ Sign a provided transaction witht he provided key(s)
@@ -159,12 +293,17 @@ class TransactionBuilder(dict):
         """
         self.constructTx()
 
+        if "operations" not in self or not self["operations"]:
+            return
+
+        # Legacy compatibility!
         # If we are doing a proposal, obtain the account from the proposer_id
         if self.bitshares.proposer:
             proposer = Account(
                 self.bitshares.proposer,
                 bitshares_instance=self.bitshares)
-            self.wifs = []
+            self.wifs = set()
+            self.signing_accounts = list()
             self.appendSigner(proposer["id"], "active")
 
         # We need to set the default prefix, otherwise pubkeys are
@@ -200,9 +339,13 @@ class TransactionBuilder(dict):
 
             :param tx tx: Signed transaction to broadcast
         """
-        if not self.is_signed():
+        # Cannot broadcast an empty transaction
+        if not self._is_signed():
             self.sign()
 
+        if "operations" not in self or not self["operations"]:
+            return
+
         ret = self.json()
 
         if self.bitshares.nobroadcast:
@@ -229,9 +372,9 @@ class TransactionBuilder(dict):
         """ Clear the transaction builder and start from scratch
         """
         self.ops = []
-        self.wifs = []
-        self.available_signers = []
-        # This makes sure that is_constructed will return False afterwards
+        self.wifs = set()
+        self.signing_accounts = []
+        # This makes sure that _is_constructed will return False afterwards
         self["expiration"] = None
         super(TransactionBuilder, self).__init__({})
 
@@ -275,13 +418,6 @@ class TransactionBuilder(dict):
                     [x[0] for x in account_auth_account[permission]["key_auths"]]
                 )
 
-    def json(self):
-        """ Show the transaction as plain json
-        """
-        if not self.is_constructed() or self.is_require_reconstruction():
-            self.constructTx()
-        return dict(self)
-
     def appendMissingSignatures(self):
         """ Store which accounts/keys are supposed to sign the transaction
 
diff --git a/bitshares/utils.py b/bitshares/utils.py
index 0546a4a0f28d05150a03401d598dca73a6407c32..52e27e1e36723a6d9919bfa1e449c94d72e96461 100644
--- a/bitshares/utils.py
+++ b/bitshares/utils.py
@@ -1,5 +1,6 @@
 import time
 from datetime import datetime
+from .exceptions import ObjectNotInProposalBuffer
 
 timeFormat = '%Y-%m-%dT%H:%M:%S'
 
@@ -28,10 +29,38 @@ def formatTimeFromNow(secs=0):
         :rtype: str
 
     """
-    return datetime.utcfromtimestamp(time.time() + int(secs)).strftime(timeFormat)
+    return datetime.utcfromtimestamp(
+        time.time() + int(secs)).strftime(timeFormat)
 
 
 def parse_time(block_time):
-    """Take a string representation of time from the blockchain, and parse it into datetime object.
+    """Take a string representation of time from the blockchain, and parse it
+       into datetime object.
     """
     return datetime.strptime(block_time, timeFormat)
+
+
+def test_proposal_in_buffer(buf, operation_name, id):
+    from .transactionbuilder import ProposalBuilder
+    from peerplaysbase.operationids import operations
+    assert isinstance(buf, ProposalBuilder)
+
+    operationid = operations.get(operation_name)
+    _, _, j = id.split(".")
+
+    ops = buf.list_operations()
+    if (len(ops) <= int(j)):
+        raise ObjectNotInProposalBuffer(
+            "{} with id {} not found".format(
+                operation_name,
+                id
+            )
+        )
+    op = ops[int(j)].json()
+    if op[0] != operationid:
+        raise ObjectNotInProposalBuffer(
+            "{} with id {} not found".format(
+                operation_name,
+                id
+            )
+        )
diff --git a/bitshares/wallet.py b/bitshares/wallet.py
index 355d93b1632f8880c56b2f70d6a43a3a6e381844..8a26a0a96457475118f3bc2ae1816c936d0faf05 100644
--- a/bitshares/wallet.py
+++ b/bitshares/wallet.py
@@ -19,7 +19,8 @@ class Wallet():
         or uses a SQLite database managed by storage.py.
 
         :param BitSharesNodeRPC rpc: RPC connection to a BitShares node
-        :param array,dict,string keys: Predefine the wif keys to shortcut the wallet database
+        :param array,dict,string keys: Predefine the wif keys to shortcut the
+               wallet database
 
         Three wallet operation modes are possible:
 
@@ -84,7 +85,8 @@ class Wallet():
         """ This method is strictly only for in memory keys that are
             passed to Wallet/BitShares with the ``keys`` argument
         """
-        log.debug("Force setting of private keys. Not using the wallet database!")
+        log.debug(
+            "Force setting of private keys. Not using the wallet database!")
         if isinstance(loadkeys, dict):
             Wallet.keyMap = loadkeys
             loadkeys = list(loadkeys.values())
@@ -168,7 +170,8 @@ class Wallet():
         """ Encrypt a wif key
         """
         assert not self.locked()
-        return format(bip38.encrypt(PrivateKey(wif), self.masterpassword), "encwif")
+        return format(
+            bip38.encrypt(PrivateKey(wif), self.masterpassword), "encwif")
 
     def decrypt_wif(self, encwif):
         """ decrypt a wif key
@@ -185,13 +188,15 @@ class Wallet():
     def addPrivateKey(self, wif):
         """ Add a private key to the wallet database
         """
-        # it could be either graphenebase or bitsharesbase so we can't check the type directly
+        # it could be either graphenebase or peerplaysbase so we can't check
+        # the type directly
         if isinstance(wif, PrivateKey) or isinstance(wif, GPHPrivateKey):
             wif = str(wif)
         try:
             pub = format(PrivateKey(wif).pubkey, self.prefix)
         except:
-            raise InvalidWifError("Invalid Private Key Format. Please use WIF!")
+            raise InvalidWifError(
+                "Invalid Private Key Format. Please use WIF!")
 
         if self.keyStorage:
             # Test if wallet exists
@@ -217,7 +222,8 @@ class Wallet():
             if not self.created():
                 raise NoWalletException
 
-            return self.decrypt_wif(self.keyStorage.getPrivateKeyForPublicKey(pub))
+            return self.decrypt_wif(
+                self.keyStorage.getPrivateKeyForPublicKey(pub))
 
     def removePrivateKeyFromPublicKey(self, pub):
         """ Remove a key from the wallet database
@@ -260,7 +266,8 @@ class Wallet():
             account = self.rpc.get_account(name)
             if not account:
                 return
-            key = self.getPrivateKeyForPublicKey(account["options"]["memo_key"])
+            key = self.getPrivateKeyForPublicKey(
+                account["options"]["memo_key"])
             if key:
                 return key
             return False
@@ -286,8 +293,16 @@ class Wallet():
         pub = format(PrivateKey(wif).pubkey, self.prefix)
         return self.getAccountFromPublicKey(pub)
 
+    def getAccountsFromPublicKey(self, pub):
+        """ Obtain all accounts associated with a public key
+        """
+        names = self.rpc.get_key_references([pub])
+        for name in names:
+            for i in name:
+                yield i
+
     def getAccountFromPublicKey(self, pub):
-        """ Obtain account name from public key
+        """ Obtain the first account name from public key
         """
         # FIXME, this only returns the first associated key.
         # If the key is used by multiple accounts, this
@@ -298,26 +313,36 @@ class Wallet():
         else:
             return names[0]
 
+    def getAllAccounts(self, pub):
+        """ Get the account data for a public key (all accounts found for this
+            public key)
+        """
+        for id in self.getAccountsFromPublicKey(pub):
+            try:
+                account = Account(id)
+            except:
+                continue
+            yield {"name": account["name"],
+                   "account": account,
+                   "type": self.getKeyType(account, pub),
+                   "pubkey": pub}
+
     def getAccount(self, pub):
-        """ Get the account data for a public key
+        """ Get the account data for a public key (first account found for this
+            public key)
         """
         name = self.getAccountFromPublicKey(pub)
         if not name:
-            return {"name": None,
-                    "type": None,
-                    "pubkey": pub
-                    }
+            return {"name": None, "type": None, "pubkey": pub}
         else:
             try:
                 account = Account(name)
             except:
                 return
-            keyType = self.getKeyType(account, pub)
             return {"name": account["name"],
                     "account": account,
-                    "type": keyType,
-                    "pubkey": pub
-                    }
+                    "type": self.getKeyType(account, pub),
+                    "pubkey": pub}
 
     def getKeyType(self, account, pub):
         """ Get key type
@@ -338,7 +363,7 @@ class Wallet():
         for pubkey in pubkeys:
             # Filter those keys not for our network
             if pubkey[:len(self.prefix)] == self.prefix:
-                accounts.append(self.getAccount(pubkey))
+                accounts.extend(self.getAllAccounts(pubkey))
         return accounts
 
     def getPublicKeys(self):
diff --git a/bitshares/witness.py b/bitshares/witness.py
index b127e3124e2a4df6cf1dacd06e87986dfb3753e0..dff4ca44a74e7ab2aa6789b89d118f0bc3bed1cc 100644
--- a/bitshares/witness.py
+++ b/bitshares/witness.py
@@ -8,26 +8,23 @@ class Witness(BlockchainObject):
     """ Read data about a witness in the chain
 
         :param str account_name: Name of the witness
-        :param bitshares bitshares_instance: BitShares() instance to use when accesing a RPC
+        :param bitshares bitshares_instance: BitShares() instance to use when
+            accesing a RPC
 
     """
     type_ids = [6, 2]
 
     def refresh(self):
-        parts = self.identifier.split(".")
-        valid_objectid = False
-        try:
-            [int(x) for x in parts]
-            valid_objectid = True
-        except:
-            pass
-        if valid_objectid and len(parts) > 2:
-            if int(parts[1]) == 6:
+        if self.test_valid_objectid(self.identifier):
+            _, i, _ = self.identifier.split(".")
+            if int(i) == 6:
                 witness = self.bitshares.rpc.get_object(self.identifier)
             else:
-                witness = self.bitshares.rpc.get_witness_by_account(self.identifier)
+                witness = self.bitshares.rpc.get_witness_by_account(
+                    self.identifier)
         else:
-            account = Account(self.identifier, bitshares_instance=self.bitshares)
+            account = Account(
+                self.identifier, bitshares_instance=self.bitshares)
             witness = self.bitshares.rpc.get_witness_by_account(account["id"])
         if not witness:
             raise WitnessDoesNotExistsException
@@ -41,11 +38,13 @@ class Witness(BlockchainObject):
 class Witnesses(list):
     """ Obtain a list of **active** witnesses and the current schedule
 
-        :param bitshares bitshares_instance: BitShares() instance to use when accesing a RPC
+        :param bitshares bitshares_instance: BitShares() instance to use when
+        accesing a RPC
     """
     def __init__(self, bitshares_instance=None):
         self.bitshares = bitshares_instance or shared_bitshares_instance()
-        self.schedule = self.bitshares.rpc.get_object("2.12.0").get("current_shuffled_witnesses", [])
+        self.schedule = self.bitshares.rpc.get_object(
+            "2.12.0").get("current_shuffled_witnesses", [])
 
         super(Witnesses, self).__init__(
             [
diff --git a/bitsharesbase/objects.py b/bitsharesbase/objects.py
index 38b8fe6a34ae73e660bdabb1e1809b611e748a4e..89b9fba023da033abbfa55c0ac416b18e7e2a799 100644
--- a/bitsharesbase/objects.py
+++ b/bitsharesbase/objects.py
@@ -88,20 +88,8 @@ class Memo(GrapheneObject):
         else:
             if len(args) == 1 and len(kwargs) == 0:
                 kwargs = args[0]
+            prefix = kwargs.pop("prefix", default_prefix)
             if "message" in kwargs and kwargs["message"]:
-                if "chain" not in kwargs:
-                    chain = default_prefix
-                else:
-                    chain = kwargs["chain"]
-                if isinstance(chain, str) and chain in known_chains:
-                    chain_params = known_chains[chain]
-                elif isinstance(chain, dict):
-                    chain_params = chain
-                else:
-                    raise Exception("Memo() only takes a string or a dict as chain!")
-                if "prefix" not in chain_params:
-                    raise Exception("Memo() needs a 'prefix' in chain params!")
-                prefix = chain_params["prefix"]
                 super().__init__(OrderedDict([
                     ('from', PublicKey(kwargs["from"], prefix=prefix)),
                     ('to', PublicKey(kwargs["to"], prefix=prefix)),
diff --git a/bitsharesbase/operations.py b/bitsharesbase/operations.py
index 157f74ca53afc8eb373fec65df3d477caee9aa94..32a77bef72a2a2c57d0aeb18f5c7b13375281aca 100644
--- a/bitsharesbase/operations.py
+++ b/bitsharesbase/operations.py
@@ -39,13 +39,19 @@ def getOperationNameForId(i):
 
 class Transfer(GrapheneObject):
     def __init__(self, *args, **kwargs):
+        # Allow for overwrite of prefix
         if isArgsThisClass(self, args):
                 self.data = args[0].data
         else:
             if len(args) == 1 and len(kwargs) == 0:
                 kwargs = args[0]
+            prefix = kwargs.get("prefix", default_prefix)
             if "memo" in kwargs and kwargs["memo"]:
-                memo = Optional(Memo(kwargs["memo"]))
+                if isinstance(kwargs["memo"], dict):
+                    kwargs["memo"]["prefix"] = prefix
+                    memo = Optional(Memo(**kwargs["memo"]))
+                else:
+                    memo = Optional(Memo(kwargs["memo"]))
             else:
                 memo = Optional(None)
             super().__init__(OrderedDict([
diff --git a/requirements-test.txt b/requirements-test.txt
index d8696895da3095eb60bedfb6f9acf7120f074914..bad8ffde726bf6c1e78cb30202ed206b1e6efdba 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -4,4 +4,5 @@ scrypt==0.7.1
 Events==0.2.2
 pyyaml
 pytest
-coverage
\ No newline at end of file
+coverage
+mock
diff --git a/setup.py b/setup.py
index 588ac31de95fe7b993f4ca2e5c3cb1e60f7ae4cf..3cc4ea46dc1651aae4079b28494faed20be0556c 100755
--- a/setup.py
+++ b/setup.py
@@ -40,7 +40,7 @@ setup(
         'Topic :: Office/Business :: Financial',
     ],
     install_requires=[
-        "graphenelib>=0.5.3",
+        "graphenelib>=0.5.5",
         "websockets",
         "appdirs",
         "Events",
diff --git a/tests/.ropeproject/globalnames b/tests/.ropeproject/globalnames
new file mode 100644
index 0000000000000000000000000000000000000000..08fd0f766b4e0b248fa073425328342f538b4bcd
--- /dev/null
+++ b/tests/.ropeproject/globalnames
@@ -0,0 +1,4 @@
+€}q(Ubitsharesbase.operations]q(Udefault_prefixqUAsset_fund_fee_poolqUTransferqUAsset_publish_feedqUBid_collateralqUgetOperationNameForIdqUAccount_updateq	U
Asset_reserveq
+U
Worker_createqUAccount_upgradeqUAsset_update_feed_producersq
UProposal_updateqUAccount_createqUVesting_balance_withdrawqUCall_order_updateqULimit_order_cancelqUAccount_whitelistqUWitness_updateqU
+Op_wrapperqUOverride_transferqUProposal_createqUAsset_updateqULimit_order_createqeUpeerplays.committee]qU	CommitteeqaUtest_amount]qU	TestcasesqaUtest_txbuffers]q(UwifqheUbitshares.transactionbuilder]q (Ulogq!UProposalBuilderq"UTransactionBuilderq#eUbitshares.bitshares]q$(h!U	BitSharesq%eUbitshares.exceptions]q&(UNoWalletExceptionq'UBlockDoesNotExistsExceptionq(UAssetDoesNotExistsExceptionq)UInsufficientAuthorityErrorq*U%CommitteeMemberDoesNotExistsExceptionq+UMissingKeyErrorq,UWorkerDoesNotExistsExceptionq-UProposalDoesNotExistExceptionq.UInvalidAssetExceptionq/UWitnessDoesNotExistsExceptionq0UWalletExistsq1UWrongMasterPasswordExceptionq2UInvalidWifErrorq3U$VestingBalanceDoesNotExistsExceptionq4UObjectNotInProposalBufferq5UAccountDoesNotExistsExceptionq6UAccountExistsExceptionq7eUtest_proposals]q8(hheUbitshares.price]q9(U	PriceFeedq:UUpdateCallOrderq;UOrderq<UFilledOrderq=UPriceq>eUbitsharesbase.objects]q?(hh>h:UAccountOptionsq@UWorker_initializerqAUMemoqBU
+PermissionqCUAssetqDU	AccountIdqEUAccountCreateExtensionsqFUAssetIdqGUSpecialAuthorityqHU	ExtensionqIU	OperationqJUAssetOptionsqKUObjectIdqLeUbitshares.account]qM(UAccountqNU
AccountUpdateqOeUtest_objectcache]qPhaUtest_bitshares]qQ(hhU	core_unitqReUtest_wallet]qS(hheUpeerplays.witness]qT(U	WitnessesqUUWitnessqVeu.
\ No newline at end of file
diff --git a/tests/.ropeproject/history b/tests/.ropeproject/history
new file mode 100644
index 0000000000000000000000000000000000000000..fcd9c963caaed6b4d36d014b5595385deef83d9e
--- /dev/null
+++ b/tests/.ropeproject/history
@@ -0,0 +1 @@
+€]q(]q]qe.
\ No newline at end of file
diff --git a/tests/.ropeproject/objectdb b/tests/.ropeproject/objectdb
new file mode 100644
index 0000000000000000000000000000000000000000..29c40cda94351229c1ed18850732694b1aa17920
--- /dev/null
+++ b/tests/.ropeproject/objectdb
@@ -0,0 +1 @@
+€}q.
\ No newline at end of file
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/tests/test_account.py b/tests/test_account.py
new file mode 100644
index 0000000000000000000000000000000000000000..6dfe2aa830a26d416572edf23712c3bdfba74188
--- /dev/null
+++ b/tests/test_account.py
@@ -0,0 +1,71 @@
+import unittest
+import mock
+from pprint import pprint
+from bitshares import BitShares
+from bitshares.account import Account
+from bitshares.amount import Amount
+from bitshares.asset import Asset
+from bitshares.instance import set_shared_bitshares_instance
+from bitsharesbase.operationids import getOperationNameForId
+
+wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            "wss://node.testnet.bitshares.eu",
+            nobroadcast=True,
+            # We want to bundle many operations into a single transaction
+            bundle=True,
+            # Overwrite wallet to use this list of wifs only
+            wif={"active": wif}
+        )
+        self.bts.set_default_account("init0")
+        set_shared_bitshares_instance(self.bts)
+
+    def test_account(self):
+        Account("witness-account")
+        Account("1.2.3")
+        asset = Asset("1.3.0")
+        symbol = asset["symbol"]
+        account = Account("witness-account", full=True)
+        self.assertEqual(account.name, "witness-account")
+        self.assertEqual(account["name"], account.name)
+        self.assertEqual(account["id"], "1.2.1")
+        self.assertIsInstance(account.balance("1.3.0"), Amount)
+        # self.assertIsInstance(account.balance({"symbol": symbol}), Amount)
+        self.assertIsInstance(account.balances, list)
+        for h in account.history(limit=1):
+            pass
+
+        # BlockchainObjects method
+        account.cached = False
+        self.assertTrue(account.items())
+        account.cached = False
+        self.assertIn("id", account)
+        account.cached = False
+        self.assertEqual(account["id"], "1.2.1")
+        self.assertEqual(str(account), "<Account 1.2.1>")
+        self.assertIsInstance(Account(account), Account)
+
+    def test_account_upgrade(self):
+        account = Account("witness-account")
+        tx = account.upgrade()
+        ops = tx["operations"]
+        op = ops[0][1]
+        self.assertEqual(len(ops), 1)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "account_upgrade"
+        )
+        self.assertTrue(
+            op["upgrade_to_lifetime_member"]
+        )
+        self.assertEqual(
+            op["account_to_upgrade"],
+            "1.2.1",
+        )
diff --git a/tests/test_aes.py b/tests/test_aes.py
new file mode 100644
index 0000000000000000000000000000000000000000..e2ad8e19f8d4629a667fc08ab11aa302592c285d
--- /dev/null
+++ b/tests/test_aes.py
@@ -0,0 +1,51 @@
+import string
+import random
+import unittest
+import base64
+from pprint import pprint
+from bitshares.aes import AESCipher
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.aes = AESCipher("Foobar")
+
+    def test_str(self):
+        self.assertIsInstance(AESCipher.str_to_bytes("foobar"), bytes)
+        self.assertIsInstance(AESCipher.str_to_bytes(b"foobar"), bytes)
+
+    def test_key(self):
+        self.assertEqual(
+            base64.b64encode(self.aes.key),
+            b"6BGBj4DZw8ItV3uoPWGWeI5VO7QIU1u0IQXN/3JqYKs="
+        )
+
+    def test_pad(self):
+        self.assertEqual(
+            base64.b64encode(self.aes._pad(b"123456")),
+            b"MTIzNDU2GhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGho="
+        )
+
+    def test_unpad(self):
+        self.assertEqual(
+            self.aes._unpad(base64.b64decode(b"MTIzNDU2GhoaGhoaGhoaGhoaGhoaGhoaGhoaGhoaGho=")),
+            b"123456"
+        )
+
+    def test_padding(self):
+        for n in range(1, 64):
+            name = ''.join(random.choice(string.ascii_lowercase) for _ in range(n))
+            self.assertEqual(
+                self.aes._unpad(self.aes._pad(
+                    bytes(name, "utf-8"))),
+                bytes(name, "utf-8")
+            )
+
+    def test_encdec(self):
+        for n in range(1, 16):
+            name = ''.join(random.choice(string.ascii_lowercase) for _ in range(64))
+            self.assertEqual(
+                self.aes.decrypt(self.aes.encrypt(name)),
+                name)
diff --git a/tests/test_amount.py b/tests/test_amount.py
new file mode 100644
index 0000000000000000000000000000000000000000..e255a95a88ac96834001379bbac35f2a5da90770
--- /dev/null
+++ b/tests/test_amount.py
@@ -0,0 +1,225 @@
+import unittest
+from bitshares import BitShares
+from bitshares.amount import Amount
+from bitshares.asset import Asset
+from bitshares.instance import set_shared_bitshares_instance, SharedInstance
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            "wss://node.testnet.bitshares.eu",
+            nobroadcast=True,
+        )
+        set_shared_bitshares_instance(self.bts)
+        self.asset = Asset("1.3.0")
+        self.symbol = self.asset["symbol"]
+        self.precision = self.asset["precision"]
+        self.asset2 = Asset("1.3.1")
+
+    def dotest(self, ret, amount, symbol):
+        self.assertEqual(float(ret), float(amount))
+        self.assertEqual(ret["symbol"], symbol)
+        self.assertIsInstance(ret["asset"], dict)
+        self.assertIsInstance(ret["amount"], float)
+
+    def test_init(self):
+        # String init
+        amount = Amount("1 {}".format(self.symbol))
+        self.dotest(amount, 1, self.symbol)
+
+        # Amount init
+        amount = Amount(amount)
+        self.dotest(amount, 1, self.symbol)
+
+        # blockchain dict init
+        amount = Amount({
+            "amount": 1 * 10 ** self.precision,
+            "asset_id": self.asset["id"]
+        })
+        self.dotest(amount, 1, self.symbol)
+
+        # API dict init
+        amount = Amount({
+            "amount": 1.3 * 10 ** self.precision,
+            "asset": self.asset["id"]
+        })
+        self.dotest(amount, 1.3, self.symbol)
+
+        # Asset as symbol
+        amount = Amount(1.3, Asset("1.3.0"))
+        self.dotest(amount, 1.3, self.symbol)
+
+        # Asset as symbol
+        amount = Amount(1.3, self.symbol)
+        self.dotest(amount, 1.3, self.symbol)
+
+        # keyword inits
+        amount = Amount(amount=1.3, asset=Asset("1.3.0"))
+        self.dotest(amount, 1.3, self.symbol)
+
+        # keyword inits
+        amount = Amount(amount=1.3, asset=dict(Asset("1.3.0")))
+        self.dotest(amount, 1.3, self.symbol)
+
+        # keyword inits
+        amount = Amount(amount=1.3, asset=self.symbol)
+        self.dotest(amount, 1.3, self.symbol)
+
+    def test_copy(self):
+        amount = Amount("1", self.symbol)
+        self.dotest(amount.copy(), 1, self.symbol)
+
+    def test_properties(self):
+        amount = Amount("1", self.symbol)
+        self.assertEqual(amount.amount, 1.0)
+        self.assertEqual(amount.symbol, self.symbol)
+        self.assertIsInstance(amount.asset, Asset)
+        self.assertEqual(amount.asset["symbol"], self.symbol)
+
+    def test_tuple(self):
+        amount = Amount("1", self.symbol)
+        self.assertEqual(
+            amount.tuple(),
+            (1.0, self.symbol))
+
+    def test_json(self):
+        amount = Amount("1", self.symbol)
+        self.assertEqual(
+            amount.json(),
+            {
+                "asset_id": self.asset["id"],
+                "amount": 1 * 10 ** self.precision
+            })
+
+    def test_string(self):
+        self.assertEqual(
+            str(Amount("1", self.symbol)),
+            "1.00000 {}".format(self.symbol))
+
+    def test_int(self):
+        self.assertEqual(
+            int(Amount("1", self.symbol)),
+            100000)
+
+    def test_float(self):
+        self.assertEqual(
+            float(Amount("1", self.symbol)),
+            1.00000)
+
+    def test_plus(self):
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(2, self.symbol)
+        self.dotest(a1 + a2, 3, self.symbol)
+        with self.assertRaises(Exception):
+            a1 + Amount(1, asset=self.asset2)
+        # inline
+        a2 = Amount(2, self.symbol)
+        a2 += a1
+        self.dotest(a2, 3, self.symbol)
+        a2 += 5
+        self.dotest(a2, 8, self.symbol)
+        with self.assertRaises(Exception):
+            a1 += Amount(1, asset=self.asset2)
+
+    def test_minus(self):
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(2, self.symbol)
+        self.dotest(a1 - a2, -1, self.symbol)
+        self.dotest(a1 - 5, -4, self.symbol)
+        with self.assertRaises(Exception):
+            a1 - Amount(1, asset=self.asset2)
+        # inline
+        a2 = Amount(2, self.symbol)
+        a2 -= a1
+        self.dotest(a2, 1, self.symbol)
+        a2 -= 1
+        self.dotest(a2, 0, self.symbol)
+        self.dotest(a2 - 2, -2, self.symbol)
+        with self.assertRaises(Exception):
+            a1 -= Amount(1, asset=self.asset2)
+
+    def test_mul(self):
+        a1 = Amount(5, self.symbol)
+        a2 = Amount(2, self.symbol)
+        self.dotest(a1 * a2, 10, self.symbol)
+        self.dotest(a1 * 3, 15, self.symbol)
+        with self.assertRaises(Exception):
+            a1 * Amount(1, asset=self.asset2)
+        # inline
+        a2 = Amount(2, self.symbol)
+        a2 *= 5
+        self.dotest(a2, 10, self.symbol)
+        with self.assertRaises(Exception):
+            a1 *= Amount(2, asset=self.asset2)
+
+    def test_div(self):
+        a1 = Amount(15, self.symbol)
+        self.dotest(a1 / 3, 5, self.symbol)
+        self.dotest(a1 // 2, 7, self.symbol)
+        with self.assertRaises(Exception):
+            a1 / Amount(1, asset=self.asset2)
+        # inline
+        a2 = a1.copy()
+        a2 /= 3
+        self.dotest(a2, 5, self.symbol)
+        a2 = a1.copy()
+        a2 //= 2
+        self.dotest(a2, 7, self.symbol)
+        with self.assertRaises(Exception):
+            a1 *= Amount(2, asset=self.asset2)
+
+    def test_mod(self):
+        a1 = Amount(15, self.symbol)
+        self.dotest(a1 % 3, 0, self.symbol)
+        self.dotest(a1 % 2, 1, self.symbol)
+        with self.assertRaises(Exception):
+            a1 % Amount(1, asset=self.asset2)
+        # inline
+        a2 = a1.copy()
+        a2 %= 3
+        self.dotest(a2, 0, self.symbol)
+        with self.assertRaises(Exception):
+            a1 %= Amount(2, asset=self.asset2)
+
+    def test_pow(self):
+        a1 = Amount(15, self.symbol)
+        self.dotest(a1 ** 3, 15 ** 3, self.symbol)
+        self.dotest(a1 ** 2, 15 ** 2, self.symbol)
+        with self.assertRaises(Exception):
+            a1 ** Amount(1, asset=self.asset2)
+        # inline
+        a2 = a1.copy()
+        a2 **= 3
+        self.dotest(a2, 15 ** 3, self.symbol)
+        with self.assertRaises(Exception):
+            a1 **= Amount(2, asset=self.asset2)
+
+    def test_ltge(self):
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(2, self.symbol)
+        self.assertTrue(a1 < a2)
+        self.assertTrue(a2 > a1)
+        self.assertTrue(a2 > 1)
+        self.assertTrue(a1 < 5)
+
+    def test_leeq(self):
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(1, self.symbol)
+        self.assertTrue(a1 <= a2)
+        self.assertTrue(a1 >= a2)
+        self.assertTrue(a1 <= 1)
+        self.assertTrue(a1 >= 1)
+
+    def test_ne(self):
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(2, self.symbol)
+        self.assertTrue(a1 != a2)
+        self.assertTrue(a1 != 5)
+        a1 = Amount(1, self.symbol)
+        a2 = Amount(1, self.symbol)
+        self.assertTrue(a1 == a2)
+        self.assertTrue(a1 == 1)
diff --git a/tests/test_asset.py b/tests/test_asset.py
new file mode 100644
index 0000000000000000000000000000000000000000..21e06383513cb27705e726f254d616530811a2b6
--- /dev/null
+++ b/tests/test_asset.py
@@ -0,0 +1,39 @@
+import unittest
+from bitshares import BitShares
+from bitshares.asset import Asset
+from bitshares.instance import set_shared_bitshares_instance
+from bitshares.exceptions import AssetDoesNotExistsException
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            nobroadcast=True,
+        )
+        set_shared_bitshares_instance(self.bts)
+
+    def test_assert(self):
+        with self.assertRaises(AssetDoesNotExistsException):
+            Asset("FOObarNonExisting", full=False)
+
+    def test_refresh(self):
+        asset = Asset("1.3.0", full=False)
+        asset.ensure_full()
+        self.assertIn("dynamic_asset_data", asset)
+        self.assertIn("flags", asset)
+        self.assertIn("permissions", asset)
+        self.assertIsInstance(asset["flags"], dict)
+        self.assertIsInstance(asset["permissions"], dict)
+
+    def test_properties(self):
+        asset = Asset("1.3.0", full=False)
+        self.assertIsInstance(asset.symbol, str)
+        self.assertIsInstance(asset.precision, int)
+        self.assertIsInstance(asset.is_bitasset, bool)
+        self.assertIsInstance(asset.permissions, dict)
+        self.assertEqual(asset.permissions, asset["permissions"])
+        self.assertIsInstance(asset.flags, dict)
+        self.assertEqual(asset.flags, asset["flags"])
diff --git a/tests/test_base_objects.py b/tests/test_base_objects.py
new file mode 100644
index 0000000000000000000000000000000000000000..ca7c29006610453f3a7bac0a342e4f66af2f2abc
--- /dev/null
+++ b/tests/test_base_objects.py
@@ -0,0 +1,31 @@
+import unittest
+from bitshares import BitShares, exceptions
+from bitshares.instance import set_shared_bitshares_instance
+from bitshares.account import Account
+from bitshares.committee import Committee
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            nobroadcast=True,
+        )
+        set_shared_bitshares_instance(self.bts)
+
+    def test_Committee(self):
+        with self.assertRaises(
+            exceptions.AccountDoesNotExistsException
+        ):
+            Committee("FOObarNonExisting")
+
+        c = Committee("init0")
+        self.assertEqual(c["id"], "1.5.0")
+        self.assertIsInstance(c.account, Account)
+
+        with self.assertRaises(
+            exceptions.CommitteeMemberDoesNotExistsException
+        ):
+            Committee("nathan")
diff --git a/tests/test_bitshares.py b/tests/test_bitshares.py
new file mode 100644
index 0000000000000000000000000000000000000000..dcdf5374206ce5ff5dfe8ce1dc6a3c721b68a3af
--- /dev/null
+++ b/tests/test_bitshares.py
@@ -0,0 +1,227 @@
+import string
+import unittest
+import random
+from pprint import pprint
+from bitshares import BitShares
+from bitsharesbase.operationids import getOperationNameForId
+from bitshares.amount import Amount
+from bitsharesbase.account import PrivateKey
+from bitshares.instance import set_shared_bitshares_instance
+
+wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
+core_unit = "TEST"
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            "wss://node.testnet.bitshares.eu",
+            nobroadcast=True,
+            keys={"active": wif, "owner": wif},
+        )
+        # from getpass import getpass
+        # self.bts.wallet.unlock(getpass())
+        set_shared_bitshares_instance(self.bts)
+        self.bts.set_default_account("init0")
+
+    def test_connect(self):
+        self.bts.connect()
+
+    def test_set_default_account(self):
+        self.bts.set_default_account("init0")
+
+    def test_info(self):
+        info = self.bts.info()
+        for key in ['current_witness',
+                    'head_block_id',
+                    'head_block_number',
+                    'id',
+                    'last_irreversible_block_num',
+                    'next_maintenance_time',
+                    'recently_missed_count',
+                    'time']:
+            self.assertTrue(key in info)
+
+    def test_finalizeOps(self):
+        bts = self.bts
+        tx1 = bts.new_tx()
+        tx2 = bts.new_tx()
+        self.bts.transfer("init1", 1, core_unit, append_to=tx1)
+        self.bts.transfer("init1", 2, core_unit, append_to=tx2)
+        self.bts.transfer("init1", 3, core_unit, append_to=tx1)
+        tx1 = tx1.json()
+        tx2 = tx2.json()
+        ops1 = tx1["operations"]
+        ops2 = tx2["operations"]
+        self.assertEqual(len(ops1), 2)
+        self.assertEqual(len(ops2), 1)
+
+    def test_transfer(self):
+        bts = self.bts
+        tx = bts.transfer(
+            "1.2.8", 1.33, core_unit, memo="Foobar", account="1.2.7")
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "transfer"
+        )
+        op = tx["operations"][0][1]
+        self.assertIn("memo", op)
+        self.assertEqual(op["from"], "1.2.7")
+        self.assertEqual(op["to"], "1.2.8")
+        amount = Amount(op["amount"])
+        self.assertEqual(float(amount), 1.33)
+
+    def test_create_account(self):
+        bts = self.bts
+        name = ''.join(random.choice(string.ascii_lowercase) for _ in range(12))
+        key1 = PrivateKey()
+        key2 = PrivateKey()
+        key3 = PrivateKey()
+        key4 = PrivateKey()
+        tx = bts.create_account(
+            name,
+            registrar="init0",   # 1.2.7
+            referrer="init1",    # 1.2.8
+            referrer_percent=33,
+            owner_key=format(key1.pubkey, core_unit),
+            active_key=format(key2.pubkey, core_unit),
+            memo_key=format(key3.pubkey, core_unit),
+            additional_owner_keys=[format(key4.pubkey, core_unit)],
+            additional_active_keys=[format(key4.pubkey, core_unit)],
+            additional_owner_accounts=["committee-account"],  # 1.2.0
+            additional_active_accounts=["committee-account"],
+            proxy_account="init0",
+            storekeys=False
+        )
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "account_create"
+        )
+        op = tx["operations"][0][1]
+        role = "active"
+        self.assertIn(
+            format(key4.pubkey, core_unit),
+            [x[0] for x in op[role]["key_auths"]])
+        self.assertIn(
+            format(key4.pubkey, core_unit),
+            [x[0] for x in op[role]["key_auths"]])
+        self.assertIn(
+            "1.2.0",
+            [x[0] for x in op[role]["account_auths"]])
+        role = "owner"
+        self.assertIn(
+            format(key4.pubkey, core_unit),
+            [x[0] for x in op[role]["key_auths"]])
+        self.assertIn(
+            format(key4.pubkey, core_unit),
+            [x[0] for x in op[role]["key_auths"]])
+        self.assertIn(
+            "1.2.0",
+            [x[0] for x in op[role]["account_auths"]])
+        self.assertEqual(
+            op["options"]["voting_account"],
+            "1.2.6")
+        self.assertEqual(
+            op["registrar"],
+            "1.2.6")
+        self.assertEqual(
+            op["referrer"],
+            "1.2.7")
+        self.assertEqual(
+            op["referrer_percent"],
+            33 * 100)
+
+    def test_weight_threshold(self):
+        bts = self.bts
+
+        auth = {'account_auths': [['1.2.0', '1']],
+                'extensions': [],
+                'key_auths': [
+                    ['TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n', 1],
+                    ['TEST7GM9YXcsoAJAgKbqW2oVj7bnNXFNL4pk9NugqKWPmuhoEDbkDv', 1]],
+                'weight_threshold': 3}  # threshold fine
+        bts._test_weights_treshold(auth)
+        auth = {'account_auths': [['1.2.0', '1']],
+                'extensions': [],
+                'key_auths': [
+                    ['TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n', 1],
+                    ['TEST7GM9YXcsoAJAgKbqW2oVj7bnNXFNL4pk9NugqKWPmuhoEDbkDv', 1]],
+                'weight_threshold': 4}  # too high
+
+        with self.assertRaises(ValueError):
+            bts._test_weights_treshold(auth)
+
+    def test_allow(self):
+        bts = self.bts
+        tx = bts.allow(
+            "TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n",
+            weight=1,
+            threshold=1,
+            permission="owner"
+        )
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "account_update"
+        )
+        op = tx["operations"][0][1]
+        self.assertIn("owner", op)
+        self.assertIn(
+            ["TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n", '1'],
+            op["owner"]["key_auths"])
+        self.assertEqual(op["owner"]["weight_threshold"], 1)
+
+    def test_disallow(self):
+        bts = self.bts
+        with self.assertRaisesRegex(ValueError, ".*Changes nothing.*"):
+            bts.disallow(
+                "TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n",
+                weight=1,
+                threshold=1,
+                permission="owner"
+            )
+        with self.assertRaisesRegex(ValueError, ".*Changes nothing!.*"):
+            bts.disallow(
+                "TEST6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV",
+                weight=1,
+                threshold=1,
+                permission="owner"
+            )
+
+    def test_update_memo_key(self):
+        bts = self.bts
+        tx = bts.update_memo_key("TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n")
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "account_update"
+        )
+        op = tx["operations"][0][1]
+        self.assertEqual(
+            op["new_options"]["memo_key"],
+            "TEST55VCzsb47NZwWe5F3qyQKedX9iHBHMVVFSc96PDvV7wuj7W86n")
+
+    def test_approvewitness(self):
+        bts = self.bts
+        tx = bts.approvewitness("init0")
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "account_update"
+        )
+        op = tx["operations"][0][1]
+        self.assertIn(
+            "1:0",
+            op["new_options"]["votes"])
+
+    def test_approvecommittee(self):
+        bts = self.bts
+        tx = bts.approvecommittee("init0")
+        self.assertEqual(
+            getOperationNameForId(tx["operations"][0][0]),
+            "account_update"
+        )
+        op = tx["operations"][0][1]
+        self.assertIn(
+            "0:11",
+            op["new_options"]["votes"])
diff --git a/tests/test_objectcache.py b/tests/test_objectcache.py
new file mode 100644
index 0000000000000000000000000000000000000000..aa1a03fcc70b57e8e4497c7d0c08f7a22b9e8238
--- /dev/null
+++ b/tests/test_objectcache.py
@@ -0,0 +1,33 @@
+import time
+import unittest
+from bitshares import BitShares, exceptions
+from bitshares.instance import set_shared_bitshares_instance
+from bitshares.blockchainobject import ObjectCache
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            nobroadcast=True,
+        )
+        set_shared_bitshares_instance(self.bts)
+
+    def test_cache(self):
+        cache = ObjectCache(default_expiration=1)
+        self.assertEqual(str(cache), "ObjectCache(n=0, default_expiration=1)")
+
+        # Data
+        cache["foo"] = "bar"
+        self.assertIn("foo", cache)
+        self.assertEqual(cache["foo"], "bar")
+        self.assertEqual(cache.get("foo", "New"), "bar")
+
+        # Expiration
+        time.sleep(2)
+        self.assertNotIn("foo", cache)
+
+        # Get
+        self.assertEqual(cache.get("foo", "New"), "New")
diff --git a/tests/test_price.py b/tests/test_price.py
index 4353a1d633f8083152afaa97b52fb75ddf062de8..808577b04517209b7bae4bec56b95eacf32f2bd8 100644
--- a/tests/test_price.py
+++ b/tests/test_price.py
@@ -11,7 +11,8 @@ class Testcases(unittest.TestCase):
     def __init__(self, *args, **kwargs):
         super(Testcases, self).__init__(*args, **kwargs)
         bitshares = BitShares(
-            "wss://node.bitshares.eu"
+            "wss://node.bitshares.eu",
+            nobroadcast=True,
         )
         set_shared_bitshares_instance(bitshares)
 
diff --git a/tests/test_proposals.py b/tests/test_proposals.py
new file mode 100644
index 0000000000000000000000000000000000000000..d92b23ed26796c85368d8c093d7a65757654b679
--- /dev/null
+++ b/tests/test_proposals.py
@@ -0,0 +1,123 @@
+import unittest
+from pprint import pprint
+from bitshares import BitShares
+from bitsharesbase.operationids import getOperationNameForId
+from bitshares.instance import set_shared_bitshares_instance
+
+wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            "wss://node.testnet.bitshares.eu",
+            nobroadcast=True,
+            keys={"active": wif},
+        )
+        # from getpass import getpass
+        # self.bts.wallet.unlock(getpass())
+        set_shared_bitshares_instance(self.bts)
+        self.bts.set_default_account("init0")
+
+    def test_finalizeOps_proposal(self):
+        bts = self.bts
+        # proposal = bts.new_proposal(bts.tx())
+        proposal = bts.proposal()
+        self.bts.transfer("init1", 1, "TEST", append_to=proposal)
+        tx = bts.tx().json()  # default tx buffer
+        ops = tx["operations"]
+        self.assertEqual(len(ops), 1)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "proposal_create")
+        prop = ops[0][1]
+        self.assertEqual(len(prop["proposed_ops"]), 1)
+        self.assertEqual(
+            getOperationNameForId(prop["proposed_ops"][0]["op"][0]),
+            "transfer")
+
+    def test_finalizeOps_proposal2(self):
+        bts = self.bts
+        proposal = bts.new_proposal()
+        # proposal = bts.proposal()
+        self.bts.transfer("init1", 1, "TEST", append_to=proposal)
+        tx = bts.tx().json()  # default tx buffer
+        ops = tx["operations"]
+        self.assertEqual(len(ops), 1)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "proposal_create")
+        prop = ops[0][1]
+        self.assertEqual(len(prop["proposed_ops"]), 1)
+        self.assertEqual(
+            getOperationNameForId(prop["proposed_ops"][0]["op"][0]),
+            "transfer")
+
+    def test_finalizeOps_combined_proposal(self):
+        bts = self.bts
+        parent = bts.new_tx()
+        proposal = bts.new_proposal(parent)
+        self.bts.transfer("init1", 1, "TEST", append_to=proposal)
+        self.bts.transfer("init1", 1, "TEST", append_to=parent)
+        tx = parent.json()
+        ops = tx["operations"]
+        self.assertEqual(len(ops), 2)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "proposal_create")
+        self.assertEqual(
+            getOperationNameForId(ops[1][0]),
+            "transfer")
+        prop = ops[0][1]
+        self.assertEqual(len(prop["proposed_ops"]), 1)
+        self.assertEqual(
+            getOperationNameForId(prop["proposed_ops"][0]["op"][0]),
+            "transfer")
+
+    def test_finalizeOps_changeproposer_new(self):
+        bts = self.bts
+        proposal = bts.proposal(proposer="init5")
+        bts.transfer("init1", 1, "TEST", append_to=proposal)
+        tx = bts.tx().json()
+        ops = tx["operations"]
+        self.assertEqual(len(ops), 1)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "proposal_create")
+        prop = ops[0][1]
+        self.assertEqual(len(prop["proposed_ops"]), 1)
+        self.assertEqual(prop["fee_paying_account"], "1.2.11")
+        self.assertEqual(
+            getOperationNameForId(prop["proposed_ops"][0]["op"][0]),
+            "transfer")
+
+    def test_finalizeOps_changeproposer_legacy(self):
+        bts = self.bts
+        bts.proposer = "init5"
+        tx = bts.transfer("init1", 1, "TEST")
+        ops = tx["operations"]
+        self.assertEqual(len(ops), 1)
+        self.assertEqual(
+            getOperationNameForId(ops[0][0]),
+            "proposal_create")
+        prop = ops[0][1]
+        self.assertEqual(len(prop["proposed_ops"]), 1)
+        self.assertEqual(prop["fee_paying_account"], "1.2.11")
+        self.assertEqual(
+            getOperationNameForId(prop["proposed_ops"][0]["op"][0]),
+            "transfer")
+
+    def test_new_proposals(self):
+        bts = self.bts
+        p1 = bts.new_proposal()
+        p2 = bts.new_proposal()
+        self.assertIsNotNone(id(p1), id(p2))
+
+    def test_new_txs(self):
+        bts = self.bts
+        p1 = bts.new_tx()
+        p2 = bts.new_tx()
+        self.assertIsNotNone(id(p1), id(p2))
diff --git a/tests/test_transactions.py b/tests/test_transactions.py
index 9d015cb5474618ef083bb09e89dc9985f0291df7..4a62a49047c2b42e72050af2739ccb58a70378c9 100644
--- a/tests/test_transactions.py
+++ b/tests/test_transactions.py
@@ -139,7 +139,6 @@ class Testcases(unittest.TestCase):
             "to": pub,
             "nonce": nonce,
             "message": encrypted_memo,
-            "chain": prefix
         }
         memoObj = objects.Memo(**memoStruct)
         self.op = operations.Transfer(**{
@@ -147,7 +146,8 @@ class Testcases(unittest.TestCase):
             "from": from_account_id,
             "to": to_account_id,
             "amount": amount,
-            "memo": memoObj
+            "memo": memoObj,
+            "prefix": prefix
         })
         self.cm = ("f68585abf4dce7c804570100000000000000000000000140420"
                    "f0000000000040102c0ded2bc1f1305fb0faac5e6c03ee3a192"
@@ -264,7 +264,8 @@ class Testcases(unittest.TestCase):
                 "owner_special_authority":
                     [1, {"asset": "1.3.127",
                          "num_top_holders": 10}]
-            }
+            },
+            "prefix": "BTS"
         })
         self.cm = ("f68585abf4dce7c804570105f26416000000000000211b03000b666f"
                    "6f6261722d6631323401000000000202fe8cc11cc8251de6977636b5"
@@ -306,7 +307,8 @@ class Testcases(unittest.TestCase):
                             "votes": [],
                             "extensions": []
                             },
-            "extensions": {}
+            "extensions": {},
+            "prefix": "BTS"
         })
         self.cm = ("f68585abf4dce7c804570106f264160000000000000"
                    "f010100000001d6ee0501000102fe8cc11cc8251de6"
diff --git a/tests/test_txbuffers.py b/tests/test_txbuffers.py
new file mode 100644
index 0000000000000000000000000000000000000000..915ecdb095b78bd11af4384ad00a305965794ffc
--- /dev/null
+++ b/tests/test_txbuffers.py
@@ -0,0 +1,107 @@
+import unittest
+from bitshares import BitShares
+from bitsharesbase import operations
+from bitshares.instance import set_shared_bitshares_instance
+
+wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            "wss://node.testnet.bitshares.eu",
+            nobroadcast=True,
+            keys={"active": wif}
+        )
+        set_shared_bitshares_instance(self.bts)
+        self.bts.set_default_account("init0")
+
+    def test_add_one_proposal_one_op(self):
+        bts = self.bts
+        tx1 = bts.new_tx()
+        proposal1 = bts.new_proposal(tx1, proposer="init0")
+        op = operations.Transfer(**{
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "from": "1.2.0",
+            "to": "1.2.0",
+            "amount": {"amount": 0, "asset_id": "1.3.0"},
+            "prefix": "TEST"
+        })
+        proposal1.appendOps(op)
+        tx = tx1.json()
+        self.assertEqual(tx["operations"][0][0], 22)
+        self.assertEqual(len(tx["operations"]), 1)
+        ps = tx["operations"][0][1]
+        self.assertEqual(len(ps["proposed_ops"]), 1)
+        self.assertEqual(ps["proposed_ops"][0]["op"][0], 0)
+
+    def test_add_one_proposal_two_ops(self):
+        bts = self.bts
+        tx1 = bts.new_tx()
+        proposal1 = bts.new_proposal(tx1, proposer="init0")
+        op = operations.Transfer(**{
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "from": "1.2.0",
+            "to": "1.2.0",
+            "amount": {"amount": 0, "asset_id": "1.3.0"},
+            "prefix": "TEST"
+        })
+        proposal1.appendOps(op)
+        proposal1.appendOps(op)
+        tx = tx1.json()
+        self.assertEqual(tx["operations"][0][0], 22)
+        self.assertEqual(len(tx["operations"]), 1)
+        ps = tx["operations"][0][1]
+        self.assertEqual(len(ps["proposed_ops"]), 2)
+        self.assertEqual(ps["proposed_ops"][0]["op"][0], 0)
+        self.assertEqual(ps["proposed_ops"][1]["op"][0], 0)
+
+    def test_have_two_proposals(self):
+        bts = self.bts
+        tx1 = bts.new_tx()
+
+        # Proposal 1
+        proposal1 = bts.new_proposal(tx1, proposer="init0")
+        op = operations.Transfer(**{
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "from": "1.2.0",
+            "to": "1.2.0",
+            "amount": {"amount": 0, "asset_id": "1.3.0"},
+            "prefix": "TEST"
+        })
+        for i in range(0, 3):
+            proposal1.appendOps(op)
+
+        # Proposal 1
+        proposal2 = bts.new_proposal(tx1, proposer="init0")
+        op = operations.Transfer(**{
+            "fee": {"amount": 0, "asset_id": "1.3.0"},
+            "from": "1.2.0",
+            "to": "1.2.0",
+            "amount": {"amount": 5555555, "asset_id": "1.3.0"},
+            "prefix": "TEST"
+        })
+        for i in range(0, 2):
+            proposal2.appendOps(op)
+        tx = tx1.json()
+
+        self.assertEqual(len(tx["operations"]), 2)  # 2 proposals
+
+        # Test proposal 1
+        prop = tx["operations"][0]
+        self.assertEqual(prop[0], 22)
+        ps = prop[1]
+        self.assertEqual(len(ps["proposed_ops"]), 3)
+        for i in range(0, 3):
+            self.assertEqual(ps["proposed_ops"][i]["op"][0], 0)
+
+        # Test proposal 2
+        prop = tx["operations"][1]
+        self.assertEqual(prop[0], 22)
+        ps = prop[1]
+        self.assertEqual(len(ps["proposed_ops"]), 2)
+        for i in range(0, 2):
+            self.assertEqual(ps["proposed_ops"][i]["op"][0], 0)
diff --git a/tests/test_wallet.py b/tests/test_wallet.py
new file mode 100644
index 0000000000000000000000000000000000000000..07c464022ad55a72e211d0cd0abafec94942dd0b
--- /dev/null
+++ b/tests/test_wallet.py
@@ -0,0 +1,27 @@
+import unittest
+import mock
+from pprint import pprint
+from bitshares import BitShares
+from bitshares.account import Account
+from bitshares.amount import Amount
+from bitshares.asset import Asset
+from bitshares.instance import set_shared_bitshares_instance
+from bitsharesbase.operationids import getOperationNameForId
+
+wif = "5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3"
+
+
+class Testcases(unittest.TestCase):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+        self.bts = BitShares(
+            nobroadcast=True,
+            # We want to bundle many operations into a single transaction
+            bundle=True,
+            # Overwrite wallet to use this list of wifs only
+            wif=[wif]
+        )
+        self.bts.set_default_account("init0")
+        set_shared_bitshares_instance(self.bts)
diff --git a/tox.ini b/tox.ini
index f0d196647742202822a29f52fcbe49b5aa64cb90..49ea089606c6c2afae8d551dc36b0bcf537448eb 100644
--- a/tox.ini
+++ b/tox.ini
@@ -6,7 +6,7 @@ skip_missing_interpreters = true
 deps=-rrequirements-test.txt
 commands=
     coverage run -a setup.py test
-    coverage report --show-missing
+    coverage report --show-missing --ignore-errors
     coverage html -i
 
 [testenv:lint]