def appendTransferOpToTx(builder, from_name, to_name, amount, symbol):

    ## TODO: Cleanup exception catching for better user feedback

    try:
        account = Account(from_name, blockchain_instance=blockchain)
        amountAsset = Amount(amount, symbol, blockchain_instance=blockchain)
        to = Account(to_name, blockchain_instance=blockchain)
    except NumRetriesReached:
        Logger.Write("ERROR: Can't reach API node: 'NumRetries' reached.  Check network connection.")
        raise
    except:
        Logger.Write("Problem locating source or destination account, or asset. Might not exist.")
        raise

    memoObj = Memo(from_account=account, to_account=to, blockchain_instance=blockchain)
    memo_text = "" #"Signed by BitShares App on Ledger Nano S!"

    op = operations.Transfer(
        **{
            "fee": {"amount": 0, "asset_id": "1.3.0"},
            "from": account["id"],
            "to": to["id"],
            "amount": {"amount": int(amountAsset), "asset_id": amountAsset.asset["id"]},
            "memo": memoObj.encrypt(memo_text),
        }
    )

    builder.appendOps(op)
    return builder
Example #2
0
def append_transfer_tx(append_to, dest_account_name):
    #
    #  `append_to` is a TransactionBuilder object. (E.g. from BitShares.new_tx())
    #  `dest_account_name` is a string account name.
    #
    account = Account(tip_sender, blockchain_instance=blockchain)
    amount = Amount(tip_amount,
                    tip_asset_symbol,
                    blockchain_instance=blockchain)
    try:
        to = Account(dest_account_name, blockchain_instance=blockchain)
    except NumRetriesReached:
        Logger.Write(
            "ERROR: Can't reach API node: 'NumRetries' reached.  Check network connection."
        )
        raise
    except:
        Logger.Write("Problem locating destination account. Might not exist.")
        raise
    memoObj = Memo(from_account=account,
                   to_account=to,
                   blockchain_instance=blockchain)
    memo_text = ""  #"Signed by BitShares App on Ledger Nano S!"

    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_text),
        })

    append_to.appendOps(op)
    return append_to
Example #3
0
    def getMemo(self, from_account, to_account, text=None, data=None):
        if (data):
            from_account = data["from"]
            to_account = data["to"]
            #data = data["message"]

        #import traceback
        #try:
        #	from_account = self.getAccount(from_account)
        #except:
        #	traceback.print_exc()
        #	#print(from_account
        #	#print(from_account)
        try:
            to_account = self.getAccount(to_account)
            to_account_name = to_account["name"]
        except:
            to_account_name = to_account
            to_account = None
        #invert = True

        #to_account = self.getAccount(to_account)
        try:
            from_account = self.getAccount(from_account)
            from_account_name = from_account["name"]
        except:
            from_account_name = from_account
            from_account = None

        #print("TO:", to_account)
        #print("FROM:", from_account)
        #if (self.isCachedAccount(to_account['name'])):
        #	invert = False
        #else:
        #	if not(from_account):
        #		showerror(from_account_name)
        #		return False

        from bitshares.memo import Memo
        memoObj = Memo(
            from_account=None,  #from_account,
            to_account=None,  #to_account,
            bitshares_instance=self.bts)
        memoObj.chain_prefix = self.chain_prefix()
        memoObj.from_account = from_account
        memoObj.to_account = to_account
        if text:
            # { nonce, to from, message } json:
            return memoObj.encrypt(text)
        # plaintext:
        return {
            'message': memoObj.decrypt(data),
            'from': from_account_name,  #from_account['name']
            'to': to_account_name,  #to_account['name'],
        }
def play_game(account, amountBet, userRoll):

    memo = Memo('bts-dice-game', account)

    nodeForConnections = 'wss://api.bitsharesdex.com'

    blockchain = Blockchain(node=nodeForConnections, mode='head')

    bitshares = BitShares(
        node=nodeForConnections,
        nobroadcast=False,
    )

    blockNum = blockchain.get_current_block_num()

    userGuess = userRoll
    seed(blockNum * (userGuess + 1))

    diceRatios = btsDiceRatios
    winRatio = diceRatios.get_ratio(userGuess)

    value = randint(1, 100)
    correctGuess = value < int(userGuess)

    bitshares.wallet.unlock('superSecretPassword')

    if winRatio is None:
        print('You submitted something not correct to this contract.')
    elif correctGuess:
        winningAmount = winRatio * amountBet
        winningString = "Correct! You guessed {0} and rolled {1}. You won {2:.5f} BTS.".format(
            userRoll, value, winningAmount)
        bitshares.transfer(account,
                           winningAmount,
                           'BTS',
                           winningString,
                           account='bts-dice-game')
        print(winningString)
    else:
        losingString = "Incorrect. You guessed {0} and rolled {1}.".format(
            userRoll, value)
        bitshares.transfer(account,
                           0.00001,
                           'BTS',
                           losingString,
                           account='bts-dice-game')
        print(
            'You guessed incorrectly. Your guess was {a} and you rolled {b}.'.
            format(a=userRoll, b=value))
Example #5
0
    def decode_memo(self, payload):
        """ This method decodes the memo for us.

            .. note:: Three cases exist that prevent us from decoding a memo
                that lead to a special message being used instead:

                * ``memo_key_missing``: In the case the decryption key was not provided
                * ````: In the case no memo was provided
                * ``decoding_not_possible``: In the case, the memo couldn't be decrypted

        """
        try:
            decoded_memo = Memo(
                bitshares_instance=self.bitshares
            ).decrypt(payload["memo"])
        except MissingKeyError:
            decoded_memo = "memo_key_missing"
        except KeyError:
            decoded_memo = ""
        except ValueError:
            decoded_memo = "decoding_not_possible"
        return decoded_memo
Example #6
0
def pprintOperation(op, show_memo=False, ctx=None):
    from bitshares.price import Order, FilledOrder

    if isinstance(op, dict) and "op" in op:
        id = op["op"][0]
        op = op["op"][1]
    else:
        id = op[0]
        op = op[1]
    if id == 1:
        return str(Order(op))
    elif id == 4:
        return str(FilledOrder(op))
    elif id == 5:
        return "New account created for {}".format(op["name"])
    elif id == 2:
        return "Canceled order %s" % op["order"]
    elif id == 6:
        return "Account {} updated".format(Account(op["account"])["name"])
    elif id == 33:
        return "Claiming from vesting: %s" % str(Amount(op["amount"]))
    elif id == 15:
        return "Reserve {}".format(str(Amount(op["amount_to_reserve"])))
    elif id == 0:
        from_account = Account(op["from"])
        to_account = Account(op["to"])
        amount = Amount(op["amount"])
        memo = ""
        if show_memo and ctx is not None:
            try:
                plain_memo = Memo(blockchain_instance=ctx.blockchain).decrypt(
                    op["memo"])
            except Exception as e:
                plain_memo = str(e)
            memo = " (memo: {plain_memo})".format(**locals())
        return "Transfer from {from_account[name]} to {to_account[name]}: {amount}{memo}".format(
            **locals())
    else:
        return format_dict(op)
Example #7
0
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)

NODE_URL = os.getenv('BITSHARES_NODE_URL', 'wss://node.testnet.bitshares.eu')
GATEWAY_ACCOUNT_ID = os.getenv('GATEWAY_ACCOUNT', '1.2.3604')
GATEWAY_ACCOUNT_WIF = '5KiQYz2MBTWH676biQcVwx6zJ1J3cYb65bZThRBav1z7gU2MoMu'
START_BLOCK = os.getenv('START_BLOCK', cache.get('START_BLOCK'))

if START_BLOCK:
    START_BLOCK = int(START_BLOCK)

bitshares = BitShares(node=NODE_URL, keys=[GATEWAY_ACCOUNT_WIF])
set_shared_blockchain_instance(bitshares)

m = Memo()

logger.info('Gateway running..')
logger.info(f'START_BLOCK: {START_BLOCK}')
logger.info(f'NODE_URL: {NODE_URL}')
logger.info(f'GATEWAY_ACCOUNT_ID: {GATEWAY_ACCOUNT_ID}')
logger.info(f'GATEWAY_ACCOUNT_WIF: {GATEWAY_ACCOUNT_WIF[:5]}..')
logger.info(f'EOS_NODE_URL: {eosio_config.url}')
logger.info(f'EOS_NODE_PORT: {eosio_config.port}')
logger.info(f'ISSUER_WIF: {ISSUER_WIF}')
logger.info(f'ISSUER_NAME: {ISSUER_NAME}')
logger.info(f'ISSUE_ASSET: {ISSUE_ASSET}')


def parse_transfer(op):
    # TODO Разобраться где обрабатывать пресижн
    def send(self, amount, address, memo=None, from_address=None) -> dict:
        """
        Send tokens to a given address/account, optionally specifying a memo. The Bitshares network transaction fee
        will be subtracted from the amount before sending.

        There must be a valid :py:class:`models.CryptoKeyPair` in the database for both 'active' and 'memo' keys for the
        from_address account, or an AuthorityMissing exception will be thrown.

        Example - send 1.23 BUILDTEAM from @someguy123 to @privex with memo 'hello'

            >>> s = BitsharesManager('BUILDTEAM')
            >>> s.send(from_address='someguy123', address='privex', amount=Decimal('1.23'), memo='hello')

        :param Decimal amount:      Amount of tokens to send, as a Decimal()
        :param address:             Account to send the tokens to
        :param from_address:        Account to send the tokens from
        :param memo:                Memo to send tokens with
        :raises AttributeError:     When both `from_address` and `self.coin.our_account` are blank.
        :raises ArithmeticError:    When the amount is lower than the lowest amount allowed by the token's precision
                                    (after subtracting the network transaction fee)
        :raises AuthorityMissing:   Cannot send because we don't have authority to (missing key etc.)
        :raises AccountNotFound:    The requested account/address doesn't exist
        :raises TokenNotFound:      When the requested token `symbol` does not exist
        :raises NotEnoughBalance:   The account `from_address` does not have enough balance to send this amount.
        :return dict: Result Information

        Format::

          {
              txid:str - Transaction ID - None if not known,
              coin:str - Symbol that was sent,
              amount:Decimal - The amount that was sent (after fees),
              fee:Decimal    - TX Fee that was taken from the amount,
              from:str       - The account/address the coins were sent from,
              send_type:str       - Should be statically set to "send"
          }

        """
        # Try from_address first. If that's empty, try using self.coin.our_account. If both are empty, abort.
        if empty(from_address):
            if empty(self.coin.our_account):
                raise AttributeError(
                    "Both 'from_address' and 'coin.our_account' are empty. Cannot send."
                )
            from_address = self.coin.our_account

        # make sure we have the necessary private keys loaded (memo key for encrypting memo, active key for sending coins)
        self.set_wallet_keys(from_address, ['memo', 'active'])

        asset_obj = self.get_asset_obj(self.symbol)
        if asset_obj is None:
            raise exceptions.TokenNotFound(
                f'Failed to send because {self.symbol} is an invalid token symbol.'
            )

        # trim input amount to the token's precision just to be safe
        str_amount = ('{0:.' + str(asset_obj['precision']) +
                      'f}').format(amount)
        amount = Decimal(str_amount)

        if not self.is_amount_above_minimum(amount, asset_obj['precision']):
            raise ArithmeticError(
                f'Failed to send because {amount} is less than the minimum amount allowed for {self.symbol} tokens.'
            )

        from_account = self.get_account_obj(from_address)
        if from_account is None:
            raise exceptions.AccountNotFound(
                f'Failed to send because source account {from_address} could not be found.'
            )
        to_account = self.get_account_obj(address)
        if to_account is None:
            raise exceptions.AccountNotFound(
                f'Failed to send because destination account {address} could not be found.'
            )

        # verify from_account balance is sufficient for the transaction
        from_account_balance = self.get_decimal_from_amount(
            from_account.balance(self.symbol))
        if from_account_balance < amount:
            raise exceptions.NotEnoughBalance(
                f'Failed to send because source account {from_address} balance {from_account_balance} {self.symbol} is less than amount to send ({amount} {self.symbol}).'
            )

        amount_obj = Amount(str_amount,
                            self.symbol,
                            blockchain_instance=self.bitshares)

        try:
            if memo is None:
                memo = ''
            memo_obj = Memo(from_account=from_account,
                            to_account=to_account,
                            blockchain_instance=self.bitshares)
            encrypted_memo = memo_obj.encrypt(memo)

            # build preliminary transaction object, without network transaction fee
            op = operations.Transfer(
                **{
                    'fee': {
                        'amount': 0,
                        'asset_id': amount_obj.asset['id']
                    },
                    'from': from_account['id'],
                    'to': to_account['id'],
                    'amount': {
                        'amount': int(amount_obj),
                        'asset_id': amount_obj.asset['id']
                    },
                    'memo': encrypted_memo,
                    'prefix': self.bitshares.prefix,
                })

            # calculate how much the transaction fee is - rather clunky method here but it works
            ops = [self.bitshares.txbuffer.operation_class(op)]
            ops_with_fees = self.bitshares.txbuffer.add_required_fees(
                ops, asset_id=amount_obj.asset['id'])
            raw_fee_amount = Decimal(
                str(ops_with_fees[0][1].data['fee']['amount']))
            fee_amount_str = '{0:f}'.format(
                raw_fee_amount / (10**amount_obj.asset['precision']))
            fee_amount = Amount(fee_amount_str,
                                self.symbol,
                                blockchain_instance=self.bitshares)
            amount_obj = amount_obj - fee_amount

            # verify the amount still makes sense after subtracting the transaction fee
            if int(amount_obj) < 1:
                raise ArithmeticError(
                    f'Failed to send because {amount} is less than the network transaction fee of {fee_amount_str} {self.symbol} tokens.'
                )

            # correct the transaction object to take into account the transaction fee
            adj_op = operations.Transfer(
                **{
                    'fee': {
                        'amount': int(fee_amount),
                        'asset_id': amount_obj.asset['id']
                    },
                    'from': from_account['id'],
                    'to': to_account['id'],
                    'amount': {
                        'amount': int(amount_obj),
                        'asset_id': amount_obj.asset['id']
                    },
                    'memo': encrypted_memo,
                    'prefix': self.bitshares.prefix,
                })

            log.debug(
                'doing Bitshares transaction - from_address[%s], address[%s], amount[%s %s], fee_amount[%s], amount_obj[%s], memo[%s]',
                from_address, address, str_amount, self.symbol, fee_amount,
                amount_obj, memo)

            # and finally, do the op!
            self.bitshares.finalizeOp(adj_op,
                                      from_address,
                                      "active",
                                      fee_asset=amount_obj.asset['id'])
            result = self.bitshares.broadcast()
        except KeyNotFound as e:
            raise exceptions.AuthorityMissing(str(e))

        return {
            'txid':
            None,  # transaction ID is not readily available from the Bitshares API
            'coin': self.orig_symbol,
            'amount': self.get_decimal_from_amount(amount_obj),
            'fee': self.get_decimal_from_amount(fee_amount),
            'from': from_address,
            'send_type': 'send'
        }
Example #9
0
from bitshares.memo import Memo
from bitshares.instance import set_shared_blockchain_instance
from raven.contrib.django.raven_compat.models import client

# TODO Сентри

# FIXME
# GATEWAY_ACCOUNT = os.getenv('GATEWAY_ACCOUNT')
NODE_URL = os.getenv('BITSHARES_NODE_URL', 'wss://node.testnet.bitshares.eu')
GATEWAY_ACCOUNT_ID = os.getenv('GATEWAY_ACCOUNT', '1.2.3604')
GATEWAY_ACCOUNT_WIF = '5KiQYz2MBTWH676biQcVwx6zJ1J3cYb65bZThRBav1z7gU2MoMu'

bitshares = BitShares(node=NODE_URL, keys=[GATEWAY_ACCOUNT_WIF])
set_shared_blockchain_instance(bitshares)

m = Memo()

logging.basicConfig(
    level=logging.WARN,
    format="%(asctime)s [%(threadName)s] [%(levelname)s]  %(message)s",
    handlers=[
        # logging.FileHandler("{0}/{1}.log".format(logPath, fileName)),
        logging.StreamHandler()
    ])

logger.info(f'Gateway running..')
logger.info(f'NODE_URL: {NODE_URL}')
logger.info(f'GATEWAY_ACCOUNT_ID: {GATEWAY_ACCOUNT_ID}')
logger.info(f'GATEWAY_ACCOUNT_WIF: {GATEWAY_ACCOUNT_WIF[:5]}..')
logger.info(f'EOS_NODE_URL: {eosio_config.url}')
logger.info(f'EOS_NODE_PORT: {eosio_config.port}')
    def issue(self, amount: Decimal, address: str, memo: str = None, trigger_data=None) -> dict:
        """
        Issue (create/print) tokens to a given address/account, optionally specifying a memo if desired.
        The network transaction fee for issuance will be paid by the issuing account in BTS.

        Example - Issue 5.10 SGTK to @privex

            >>> s = BitsharesManager('SGTK')
            >>> s.issue(address='privex', amount=Decimal('5.10'))

        :param Decimal amount:      Amount of tokens to issue, as a Decimal
        :param address:             Account to issue the tokens to (which is also the issuer account)
        :param memo:                Optional memo for the issuance transaction
        :raises IssuerKeyError:     Cannot issue because we don't have authority to (missing key etc.)
        :raises TokenNotFound:      When the requested token `symbol` does not exist
        :raises AccountNotFound:    The requested account doesn't exist
        :raises ArithmeticError:    When the amount is lower than the lowest amount allowed by the token's precision
        :return dict: Result Information

        Format::

          {
              txid:str - Transaction ID - None if not known,
              coin:str - Symbol that was sent,
              amount:Decimal - The amount that was issued,
              fee:Decimal    - TX Fee that was taken from the amount (will be 0 if fee is in BTS rather than the issuing token),
              from:str       - The account/address the coins were issued from,
              send_type:str       - Should be statically set to "issue"
          }

        """
        asset_obj = self.get_asset_obj(self.symbol)
        if asset_obj is None:
            raise exceptions.TokenNotFound(f'Failed to issue because {self.symbol} is an invalid token symbol.')

        # trim input amount to the token's precision just to be safe
        str_amount = ('{0:.' + str(asset_obj['precision']) + 'f}').format(amount)
        amount = Decimal(str_amount)

        if not self.is_amount_above_minimum(amount, asset_obj['precision']):
            raise ArithmeticError(f'Failed to issue because {amount} is less than the minimum amount allowed for {self.symbol} tokens.')

        to_account = self.get_account_obj(address)
        if to_account is None:
            raise exceptions.AccountNotFound(f'Failed to issue because issuing account {address} could not be found.')

        amount_obj = Amount(str_amount, self.symbol, blockchain_instance=self.bitshares)

        try:
            # make sure we have the necessary private keys loaded (memo key for encrypting memo, active key for issuing coins)
            self.set_wallet_keys(address, [ 'memo', 'active' ])

            if memo is None:
                memo = ''
            memo_obj = Memo(from_account=to_account, to_account=to_account, blockchain_instance=self.bitshares)
            encrypted_memo = memo_obj.encrypt(memo)

            # construct the transaction - note that transaction fee for issuance will be paid in BTS, but we retain the
            # flexibility to add support for paying the fee in the issuing token if deemed necessary
            op = operations.Asset_issue(
                **{
                    "fee": {"amount": 0, "asset_id": "1.3.0"},
                    "issuer": to_account["id"],
                    "asset_to_issue": {"amount": int(amount_obj), "asset_id": amount_obj.asset["id"]},
                    "memo": encrypted_memo,
                    "issue_to_account": to_account["id"],
                    "extensions": [],
                    "prefix": self.bitshares.prefix,
                }
            )

            log.debug('doing token issuance - address[%s], amount[%s %s], memo[%s]', address, str_amount, self.symbol, memo)

            # broadcast the transaction
            self.bitshares.finalizeOp(op, to_account, "active")
            result = self.bitshares.broadcast()
        except KeyNotFound as e:
            raise exceptions.IssuerKeyError(str(e))
        except exceptions.AuthorityMissing as e:
            raise exceptions.IssuerKeyError(str(e))

        return {
            'txid': None,     # transaction ID is not readily available from the Bitshares API
            'coin': self.orig_symbol,
            'amount': self.get_decimal_from_amount(amount_obj),
            'fee': Decimal(0),     # fee is in BTS, not the issuing token
            'from': address,
            'send_type': 'issue'
        }
from bitshares.blockchain import Blockchain
from bitshares.account import Account
from bitshares.asset import Asset
from bitshares.memo import Memo
import btsDiceGameTransactions

nodeForConnections = 'wss://api.bitsharesdex.com'

blockchain = Blockchain(node=nodeForConnections, mode='head')

btsDGT = btsDiceGameTransactions

memo = Memo()
memo.blockchain.wallet.unlock('superSecretPassword')

for op in blockchain.stream(['transfer']):
    payee = Account(op['to']).name
    if payee == 'bts-dice-game':
        payor = Account(op['from']).name
        decryptedMemo = int(memo.decrypt(op['memo']))
        amount = op['amount']['amount'] / 100000
        btsDGT.play_game(payor, amount, decryptedMemo)
Example #12
0
def cast_money(s, _):
    if s is None: return None
    return Decimal(s[1:].replace(
        ",", ""))  # hive off the dollar sign and filter out commas


money_type = psycopg2.extensions.new_type((790, ), "MONEY", cast_money)

config = ConfigParser()
config.read(
    ['/etc/gateway.conf',
     os.path.expanduser('~/.config/gateway.conf')])

bitshares = BitShares()
bitshares.wallet.unlock(config['gateway']['passphrase'])
memoObj = Memo()
memoObj.unlock_wallet(config['gateway']['passphrase'])

acct = Account(config['gateway']['account'])

conn = psycopg2.connect(host=config['db'].get('host', ''),
                        database=config['db']['name'],
                        user=config['db']["user"],
                        cursor_factory=psycopg2.extras.NamedTupleCursor)
psycopg2.extensions.register_type(money_type, conn)

InputTx = collections.namedtuple("InputTx",
                                 "bts_account amount date comment fiat_txid")

registry = {}
Example #13
0
File: views.py Project: Devilla/vid
owner_key = "5JrC9FxgxtJ8fi8UjPgL57KH27ToVngEZ7Aka6uSSNBFb2XTVgB"
memo_key = "5KUVfh5zagFRheVT8G4PQXjknw25nuEXizsh3k3JhY3J5cEkq1H"
active_key = "5JyorRk13HM1WDJ4VvPE7WimPa4KB21qhLQMTcuZkNxBdafdQnV"

bitshares = BitShares(node=["wss://openledger.hk/ws", "wss://us.nodes.bitshares.ws", "wss://bitshares.openledger.info/ws"])

try:
    bitshares.wallet.create(pass_phrase)
    bitshares.wallet.addPrivateKey(owner_key)
    bitshares.wallet.addPrivateKey(active_key)
    bitshares.wallet.addPrivateKey(memo_key)
except:
    bitshares.wallet.unlock(pass_phrase)

acc = bitshares.wallet.getAccounts()[0]['account']
m = Memo(blockchain=bitshares)
m.blockchain.wallet.unlock(pass_phrase)

def verify_transaction(acc, m, to_send_text, to_send_amount, payment_id, loop_time):
    starting_time = time.time()

    while True:
        started_time = time.time() - starting_time
        if started_time > loop_time:
            break

        ad_object = advertisement.objects.get(id=payment_id)

        for transaction in acc.history():
            currInfo = transaction['op'][1]
Example #14
0
def build_transaction(incidentId, fromAddress, fromMemoWif, toAddress, asset_id,
                      amount, includeFee, bitshares_instance=None):
    """ Builds a transaction (without signature)

        :param guid incidentId: Lykke unique operation ID
        :param str fromAddress: Source address
        :param str toAddress: Destination address
        :param str assetId: Asset ID to transfer
        :param str amount: Amount to transfer. Integer as string, aligned to the asset
                accuracy. Actual value can be calculated as x = amount / (10 ^
                asset.Accuracy)
        :param bool includeFee: Flag, which indicates, that the fee should
                be included in the specified amount
    """

    def obtain_raw_tx():
#         if old_operation is None:
        _memo = memo.encrypt(memo_plain)
        _expiration = Config.get("bitshares", "transaction_expiration_in_sec", 60 * 60 * 24)  # 24 hours
#         else:
#             memo_encrypted = memo.encrypt(memo_plain)

        op = operations.Transfer(**{
            "fee": {
                "amount": 0,
                "asset_id": "1.3.0"
            },  # will be replaced
            "from": from_account["id"],
            "to": to_account["id"],
            "amount": amount.json(),
            "memo": _memo,
            "prefix": bitshares_instance.prefix
        })

        tx = TransactionBuilder(
            bitshares_instance=bitshares_instance
        )
        tx.appendOps(op)
        tx.set_expiration(_expiration)

        # Build the transaction, obtain fee to be paid
        tx.constructTx()
        return tx.json()

    operation_formatter.validate_incident_id(incidentId)

    if not is_valid_address(fromAddress):
        raise AccountDoesNotExistsException()

    if not is_valid_address(toAddress):
        raise AccountDoesNotExistsException()

#     # check if this was already built
#     old_operation = None
#     try:
#         old_operation = _get_os().get_operation(incidentId)
#     except OperationNotFoundException:
#         pass

    # Decode addresses
    from_address = split_unique_address(fromAddress)
    to_address = split_unique_address(toAddress)

    # obtain chain accounts from addresses
    from_account = Account(
        from_address["account_id"],
        bitshares_instance=bitshares_instance)
    to_account = Account(
        to_address["account_id"],
        bitshares_instance=bitshares_instance)

    memo_plain = create_memo(from_address, to_address, incidentId)

    try:
        # Construct amount
        amount = Amount(
            {
                "amount": amount,
                "asset_id": asset_id
            },
            bitshares_instance=bitshares_instance
        )
    except AssetDoesNotExistsException:
        raise AssetNotFoundException()

    # encrypt memo
    # TODO this is a hack. python-bitshares issue is opened, once resolve, fix
    if not fromMemoWif:
        if from_address["account_id"] == Config.get("bitshares", "exchange_account_id"):
            fromMemoWif = Config.get("bitshares", "exchange_account_memo_key")

    if fromMemoWif:
        bitshares_instance.wallet.setKeys(fromMemoWif)

    # memo key of the account must be known!
    if not from_account["options"]["memo_key"] in Wallet.keys:
            raise MemoMatchingFailedException()

    memo = Memo(
        from_account=from_account,
        to_account=to_account,
        bitshares_instance=bitshares_instance
    )

    try:
        tx = obtain_raw_tx()
    except MissingKeyError:
        raise MemoMatchingFailedException()

    fee = Amount(tx["operations"][0][1]["fee"],
                 bitshares_instance=bitshares_instance)

    # virtual internal transfers always do full amount
    if includeFee and from_account != to_account:
        # Reduce fee from amount to transfer
        amount -= fee
        tx = obtain_raw_tx()

    # Add additional/optional information
    #   - add incident_id as fallback for internal database
    #   - add decoded memo to avoid double decoding
    tx.update({
        "incident_id": incidentId,
        "decoded_memo": memo_plain,
    })

    if bitshares_instance.prefix != "BTS":
        tx["prefix"] = bitshares_instance.prefix

    return {"transactionContext": json.dumps(tx)}