def send(self, amount, address, from_address=None, memo=None) -> dict: amount = Decimal(amount) fee = fake_amt(0, amount / 2) # If validate_addresses is true, verify the address from the static list if not self.address_valid(address): raise exceptions.AccountNotFound( '(random/fake) Dest addr {} is not valid'.format(address)) bal = self.balance( from_address) if from_address is not None else fake_amt() from_address = fake_user() if from_address is None else from_address if bal < amount: raise exceptions.NotEnoughBalance( 'Tried sending {} from {} but addr only has {}'.format( amount, from_address, bal)) return { 'txid': fake_txid(), 'coin': self.symbol, 'amount': amount - fee, 'fee': fee, 'from': from_address, 'send_type': 'send' }
def send(self, amount: Decimal, address: str, from_address: str = None, memo: str = None) -> dict: """ Send a supported currency to a given address/account, optionally specifying a memo if supported Example - send 1.23 STEEM from @someguy123 to @privex with memo 'hello' >>> s = SteemManager('STEEM') >>> s.send(from_address='someguy123', address='privex', amount=Decimal('1.23'), memo='hello') :param Decimal amount: Amount of currency to send, as a Decimal() :param address: Account to send the currency to :param from_address: Account to send the currency from :param memo: Memo to send currency 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 asset's precision :raises AuthorityMissing: Cannot send because we don't have authority to (missing key etc.) :raises AccountNotFound: The requested account doesn't 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 prec = self.precision sym = self.symbol memo = "" if empty(memo) else memo try: if type(amount) != Decimal: if type(amount) == float: amount = ('{0:.' + str(self.precision) + 'f}').format(amount) amount = Decimal(amount) ### # Various sanity checks, e.g. checking amount is valid, to/from account are valid, we have # enough balance to send this amt, etc. ### if amount < Decimal(pow(10, -prec)): log.warning( 'Amount %s was passed, but is lower than precision for %s', amount, sym) raise ArithmeticError( 'Amount {} is lower than token {}s precision of {} DP'. format(amount, sym, prec)) acc = Account(from_address, steem_instance=self.rpc) if not self.address_valid(address): raise exceptions.AccountNotFound( 'Destination account does not exist') if not self.address_valid(from_address): raise exceptions.AccountNotFound('From account does not exist') bal = self.balance(from_address) if bal < amount: raise exceptions.NotEnoughBalance( 'Account {} has balance {} but needs {} to send this tx'. format(from_address, bal, amount)) ### # Broadcast the transfer transaction on the network, and return the necessary data ### log.debug('Sending %f %s to @%s', amount, sym, address) tfr = acc.transfer(address, amount, sym, memo) # Beem's finalizeOp doesn't include TXID, so we try to find the TX on the blockchain after broadcast tx = self.find_steem_tx(tfr) log.debug('Success? TX Data - Transfer: %s Lookup TX: %s', tfr, tx) # Return TX data compatible with BaseManager standard return { # There's a risk we can't get the TXID, and so we fall back to None. 'txid': tx.get('transaction_id', None), 'coin': sym, 'amount': amount, 'fee': Decimal(0), 'from': from_address, 'send_type': 'send' } except MissingKeyError: raise exceptions.AuthorityMissing( 'Missing active key for sending account {}'.format( from_address))
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' }
def send(self, amount, address, memo=None, from_address=None) -> dict: """ Send tokens to a given address/account, optionally specifying a memo if supported Example - send 1.23 SGTK from @someguy123 to @privex with memo 'hello' >>> s = SteemEngineManager('SGTK') >>> 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 (if supported) :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 :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 try: token = self.eng_rpc.get_token(symbol=self.symbol) # If we get passed a float for some reason, make sure we trim it to the token's precision before # converting it to a Decimal. if type(amount) == float: amount = ('{0:.' + str(token['precision']) + 'f}').format(amount) amount = Decimal(amount) log.debug('Sending %f %s to @%s', amount, self.symbol, address) t = self.eng_rpc.send_token(symbol=self.symbol, from_acc=from_address, to_acc=address, amount=amount, memo=memo) txid = None # There's a risk we can't get the TXID, and so we fall back to None. if 'transaction_id' in t: txid = t['transaction_id'] return { 'txid': txid, 'coin': self.symbol, 'amount': amount, 'fee': Decimal(0), 'from': from_address, 'send_type': 'send' } except SENG.AccountNotFound as e: raise exceptions.AccountNotFound(str(e)) except SENG.TokenNotFound as e: raise exceptions.TokenNotFound(str(e)) except SENG.NotEnoughBalance as e: raise exceptions.NotEnoughBalance(str(e)) except MissingKeyError: raise exceptions.AuthorityMissing('Missing active key for sending account {}'.format(from_address))