def create_stellar_deposit(transaction_id): transaction = Transaction.objects.get(id=transaction_id) # Assume transaction has valid stellar_account, amount_in, asset.name stellar_account = transaction.stellar_account payment_amount = transaction.amount_in - transaction.amount_fee asset = transaction.asset.name # If the given Stellar account does not exist, create # the account with at least enough XLM for the minimum # reserve and a trust line (recommended 2.01 XLM), update # the transaction in our internal database, and return. address = Address(stellar_account) try: address.get() except HorizonError as e: # 404 code corresponds to Resource Missing. if e.status_code == 404: starting_balance = settings.ACCOUNT_STARTING_BALANCE builder = Builder(secret=settings.STELLAR_ACCOUNT_SEED) builder.append_create_account_op( destination=stellar_account, starting_balance=starting_balance, source=settings.STELLAR_ACCOUNT_ADDRESS, ) builder.sign() try: builder.submit() except HorizonError as exception: raise exception transaction.status = Transaction.STATUS.pending_trust transaction.save() return # If the account does exist, deposit the desired amount of the given # asset via a Stellar payment. If that payment succeeds, we update the # transaction to completed at the current time. If it fails due to a # trustline error, we update the database accordingly. Else, we do not update. builder = Builder(secret=settings.STELLAR_ACCOUNT_SEED) builder.append_payment_op( destination=stellar_account, asset_code=asset, asset_issuer=settings.STELLAR_ACCOUNT_ADDRESS, amount=str(payment_amount), ) builder.sign() try: builder.submit() except HorizonError as exception: if TRUSTLINE_FAILURE_XDR in exception.message: transaction.status = Transaction.STATUS.pending_trust transaction.save() return # If we reach here, the Stellar payment succeeded, so we # can mark the transaction as completed. transaction.status = Transaction.STATUS.completed transaction.completed_at = now() transaction.amount_out = payment_amount transaction.save()
def build_generate_trust_wallet_transaction( transaction_source_address: str, source_address: str, destination_address: str, xlm_amount: Decimal, hot_amount: Decimal = Decimal(0), sequence=None, ) -> Tuple[bytes, bytes]: """"Build transaction return unsigned XDR and transaction hash. Args: transaction_source_address: Owner of a transaction. source_address: Owner of creator address and payment operations. destination_address: wallet id of new wallet. xlm_amount: starting xlm_balance of new wallet. hot_amount: starting hot balance of new wallet. """ builder = Builder( address=transaction_source_address, horizon=settings['HORIZON_URL'], network=settings['PASSPHRASE'], sequence=sequence, ) builder.append_create_account_op(source=source_address, destination=destination_address, starting_balance=xlm_amount) try: builder.append_trust_op( source=destination_address, destination=settings['ISSUER'], code=settings['ASSET_CODE'], limit=settings['LIMIT_ASSET'], ) except DecodeError: raise web.HTTPBadRequest(reason='Parameter values are not valid.') except Exception as e: msg = str(e) raise web.HTTPInternalServerError(reason=msg) if hot_amount > 0: builder.append_payment_op( source=source_address, destination=destination_address, asset_code=settings['ASSET_CODE'], asset_issuer=settings['ISSUER'], amount=hot_amount, ) try: unsigned_xdr = builder.gen_xdr() except Exception as e: raise web.HTTPBadRequest( reason='Bad request, Please ensure parameters are valid.') tx_hash = builder.te.hash_meta() return unsigned_xdr, tx_hash
def build_generate_wallet_transaction(transaction_source_address: str, source_address: str, destination_address: str, amount: Decimal, sequence=None) -> Tuple[bytes, bytes]: """"Build transaction return unsigned XDR and transaction hash. Args: transaction_source_address: Owner of the transactoin source_address: Owner of creator wallet destination_address: wallet id of new wallet amount: starting balance of new wallet """ builder = Builder( address=transaction_source_address, horizon=settings['HORIZON_URL'], network=settings['PASSPHRASE'], sequence=sequence, ) try: builder.append_create_account_op(source=source_address, destination=destination_address, starting_balance=amount) except DecodeError: raise web.HTTPBadRequest(reason='Parameter values are not valid.') except Exception as e: msg = str(e) raise web.HTTPInternalServerError(reason=msg) try: unsigned_xdr = builder.gen_xdr() except Exception as e: raise web.HTTPBadRequest( reason='Bad request, Please ensure parameters are valid.') tx_hash = builder.te.hash_meta() return unsigned_xdr, tx_hash
def test_builder(self): cold_account = self.cold.address().decode() hot_account = self.hot.address().decode() fund(cold_account) cold = Builder(self.cold.seed().decode()) cold.append_create_account_op(hot_account, 200) cold.append_set_options_op( inflation_dest=cold_account, set_flags=1, home_domain='256kw.com', master_weight=10, low_threshold=5, ) cold.append_trust_op(cold_account, 'BEER', 1000, source=hot_account) cold.append_allow_trust_op(hot_account, 'BEER', True) # append twice for test cold.append_payment_op(hot_account, 50.123, 'BEER', cold_account) cold.append_payment_op(hot_account, 50.123, 'BEER', cold_account) cold.sign(self.hot.seed().decode()) try: # sign twice cold.sign(self.hot.seed().decode()) except SignatureExistError: assert True except: assert False cold.sign() assert len(cold.te.signatures) == 2 assert len(cold.ops) == 5 try: response = cold.submit() print(response) except: assert False
def test_builder(self): cold_account = self.cold.address().decode() hot_account = self.hot.address().decode() fund(cold_account) cold = Builder(self.cold.seed().decode()) cold.append_create_account_op(hot_account, 200) cold.append_set_options_op(inflation_dest=cold_account, set_flags=1, home_domain='256kw.com',master_weight=10, low_threshold=5,) cold.append_trust_op(cold_account, 'BEER', 1000, source=hot_account) cold.append_allow_trust_op(hot_account,'BEER', True) # append twice for test cold.append_payment_op(hot_account, 50.123, 'BEER', cold_account) cold.append_payment_op(hot_account, 50.123, 'BEER', cold_account) cold.sign(self.hot.seed().decode()) try: # sign twice cold.sign(self.hot.seed().decode()) except SignatureExistError: assert True except: assert False cold.sign() assert len(cold.te.signatures) == 2 assert len(cold.ops) == 5 try: response = cold.submit() print(response) except: assert False
def create_stellar_deposit(transaction_id): """Create and submit the Stellar transaction for the deposit.""" transaction = Transaction.objects.get(id=transaction_id) # We check the Transaction status to avoid double submission of a Stellar # transaction. The Transaction can be either `pending_anchor` if the task # is called from `GET deposit/confirm_transaction` or `pending_trust` if called # from the `check_trustlines()`. if transaction.status not in [ Transaction.STATUS.pending_anchor, Transaction.STATUS.pending_trust, ]: return transaction.status = Transaction.STATUS.pending_stellar transaction.save() # We can assume transaction has valid stellar_account, amount_in, and asset # because this task is only called after those parameters are validated. stellar_account = transaction.stellar_account payment_amount = round(transaction.amount_in - transaction.amount_fee, 7) asset = transaction.asset.name # If the given Stellar account does not exist, create # the account with at least enough XLM for the minimum # reserve and a trust line (recommended 2.01 XLM), update # the transaction in our internal database, and return. address = Address( stellar_account, network=settings.STELLAR_NETWORK, horizon_uri=settings.HORIZON_URI, ) try: address.get() except HorizonError as address_exc: # 404 code corresponds to Resource Missing. if address_exc.status_code != 404: return starting_balance = settings.ACCOUNT_STARTING_BALANCE builder = Builder( secret=settings.STELLAR_ACCOUNT_SEED, horizon_uri=settings.HORIZON_URI, network=settings.STELLAR_NETWORK, ) builder.append_create_account_op( destination=stellar_account, starting_balance=starting_balance, source=settings.STELLAR_ACCOUNT_ADDRESS, ) builder.sign() try: builder.submit() except HorizonError: return transaction.status = Transaction.STATUS.pending_trust transaction.save() return # If the account does exist, deposit the desired amount of the given # asset via a Stellar payment. If that payment succeeds, we update the # transaction to completed at the current time. If it fails due to a # trustline error, we update the database accordingly. Else, we do not update. builder = Builder( secret=settings.STELLAR_ACCOUNT_SEED, horizon_uri=settings.HORIZON_URI, network=settings.STELLAR_NETWORK, ) builder.append_payment_op( destination=stellar_account, asset_code=asset, asset_issuer=settings.STELLAR_ACCOUNT_ADDRESS, amount=str(payment_amount), ) builder.sign() try: response = builder.submit() # Functional errors at this stage are Horizon errors. except HorizonError as exception: if TRUSTLINE_FAILURE_XDR not in exception.message: return transaction.status = Transaction.STATUS.pending_trust transaction.save() return # If this condition is met, the Stellar payment succeeded, so we # can mark the transaction as completed. if response["result_xdr"] != SUCCESS_XDR: return transaction.stellar_transaction_id = response["hash"] transaction.status = Transaction.STATUS.completed transaction.completed_at = now() transaction.status_eta = 0 # No more status change. transaction.amount_out = payment_amount transaction.save()
async def build_generate_escrow_wallet_transaction( escrow_address: str, transaction_source_address: str, creator_address: str, destination_address: str, provider_address: str, cost_per_transaction: Decimal, starting_native_asset: Decimal, starting_custom_asset: Decimal, expiration_date: str = None, sequence: str = None, ) -> Tuple[Any, str]: '''Building transaction for generating escrow wallet with minimum balance of lumens and return unsigned XDR and transaction hash. Args: * escrow_address: an address of new wallet * destination_address: an address of wallet which is target * transaction_source_address an address from wallet pool * provider_address: an address which provides custom_asset to new wallet * creator_address: an address of source wallet which is owner of the transaction. * cost_per_transaction: cost for each promotion deals. * starting_native_asset: starting amount of XLM. * starting_custom_asset: starting amount of custom asset(HOT). * expiration_date: a date when escrow address is terminated. ''' builder = Builder( address=transaction_source_address, horizon=settings['HORIZON_URL'], network=settings['PASSPHRASE'], sequence=sequence, ) builder.append_create_account_op(source=creator_address, destination=escrow_address, starting_balance=starting_native_asset) try: builder.append_trust_op( source=escrow_address, destination=settings['ISSUER'], code=settings['ASSET_CODE'], limit=settings['LIMIT_ASSET'], ) except DecodeError: raise web.HTTPBadRequest( reason='Parameter escrow_address or issuer address are not valid.') except Exception as e: msg = str(e) raise web.HTTPInternalServerError(reason=msg) builder.append_manage_data_op(source=escrow_address, data_name='creator_address', data_value=creator_address) builder.append_manage_data_op(source=escrow_address, data_name='destination_address', data_value=destination_address) builder.append_manage_data_op(source=escrow_address, data_name='provider_address', data_value=provider_address) if expiration_date: builder.append_manage_data_op(source=escrow_address, data_name='expiration_date', data_value=expiration_date) builder.append_manage_data_op(source=escrow_address, data_name='cost_per_transaction', data_value=str(cost_per_transaction)) builder.append_set_options_op(source=escrow_address, signer_address=creator_address, signer_type='ed25519PublicKey', signer_weight=1) builder.append_set_options_op(source=escrow_address, signer_address=provider_address, signer_type='ed25519PublicKey', signer_weight=1) builder.append_set_options_op(source=escrow_address, master_weight=0, low_threshold=2, med_threshold=2, high_threshold=2) builder.append_payment_op( source=provider_address, destination=escrow_address, asset_code=settings['ASSET_CODE'], asset_issuer=settings['ISSUER'], amount=starting_custom_asset, ) try: xdr = builder.gen_xdr() except Exception as e: raise web.HTTPBadRequest( reason='Bad request, Please ensure parameters are valid.') tx_hash = builder.te.hash_meta() return xdr.decode(), binascii.hexlify(tx_hash).decode()
horizon = horizon_testnet() print('Initializing keypairs') kp_alice = initialize_keypair() kp_bob = initialize_keypair() kp_carol = initialize_keypair() print('Carol creates accounts a_1 and a_2') kp_a_1 = initialize_keypair(add_funds=False) kp_a_2 = initialize_keypair(add_funds=False) kp_a_1_address = kp_a_1.address().decode() kp_a_2_address = kp_a_2.address().decode() kp_a_1_seed = kp_a_1.seed().decode() kp_a_2_seed = kp_a_2.seed().decode() accounts_builder = Builder(secret=kp_carol.seed().decode()) accounts_builder.append_create_account_op( kp_a_1_address, '202.50009') accounts_builder.append_create_account_op( kp_a_2_address, '202.50009') accounts_builder.sign() response = accounts_builder.submit() assert 'hash' in response print('Initializing transactions') address_a_1 = Address(address=kp_a_1_address) address_a_2 = Address(address=kp_a_2_address) address_a_1.get() address_a_2.get() starting_sequence_a_1 = int(address_a_1.sequence) starting_sequence_a_2 = int(address_a_2.sequence)
class Interface: """ Interface to handle all API calls to third-party account. """ def __init__(self, account): self.account = account if account.secret: self.builder = Builder(secret=account.secret, network=account.network) self.address = Address(address=account.account_id, network=account.network) def _get_new_receives(self): # Get all stored transactions for account transactions = ReceiveTransaction.filter(admin_account=self.account) # Set the cursor according to latest stored transaction: if not transactions: cursor = None else: cursor = int(transactions.latest().data['paging_token']) + 1 # Get new transactions from the stellar network: new_transactions = self._get_receives(cursor=cursor) return new_transactions def _get_receives(self, cursor=None): # If cursor was specified, get all transactions after the cursor: if cursor: transactions = self.address.payments(cursor=cursor)['_embedded']['records'] print(transactions) for i, tx in enumerate(transactions): if tx.get('to') != self.account.account_id: transactions.pop(i) # remove sends # else just get all the transactions: else: transactions = self.address.payments()['_embedded']['records'] for i, tx in enumerate(transactions): if tx.get('from') == self.account.account_id: transactions.pop(i) # remove sends return transactions def _process_receive(self, tx): # Get memo: details = requests.get(url=tx['_links']['transaction']['href']).json() memo = details.get('memo') print('memo: ' + str(memo)) if memo: account_id = memo + '*rehive.com' user_account = UserAccount.objects.get(account_id=account_id) user_email = user_account.user_id # for this implementation, user_id is the user's email amount = to_cents(Decimal(tx['amount']), 7) if tx['asset_type'] == 'native': currency = 'XLM' issuer = '' else: currency = tx['asset_code'] issuer_address = tx['asset_issuer'] issuer = Asset.objects.get(account_id=issuer_address, code=currency).issuer # Create Transaction: tx = ReceiveTransaction.objects.create(user_account=user_account, external_id=tx['hash'], recipient=user_email, amount=amount, currency=currency, issuer=issuer, status='Waiting', data=tx, metadata={'type': 'stellar'} ) # TODO: Move tx.upload_to_rehive() to a signal to auto-run after Transaction creation. tx.upload_to_rehive() return True @staticmethod def _is_valid_address(address: str) -> bool: # TODO: Replace with real address check. if len(address) == 56 and '*' not in address: return True else: return False # This function should always be included if transactions are received to admin account and not added via webhooks: def process_receives(self): # Get new receive transactions new_transactions = self._get_new_receives() # Add each transaction to Rehive and log in transaction table: for tx in new_transactions: self._process_receive(tx) # This function should always be included. def process_send(self, tx): if self._is_valid_address(tx.recipient): address = tx.recipient else: federation = get_federation_details(tx.recipient) if federation['memo_type'] == 'text': self.builder.add_text_memo(federation['memo']) elif federation['memo_type'] == 'id': self.builder.add_id_memo(federation['memo']) elif federation['memo_type'] == 'hash': self.builder.add_hash_memo(federation['memo']) else: raise NotImplementedAPIError('Invalid memo type specified.') address = federation['account_id'] # Create account or create payment: if tx.currency == 'XLM': try: address_obj = self.address address_obj.get() self.builder.append_payment_op(address, tx.amount, 'XLM') except APIException as exc: if exc.status_code == 404: self.builder.append_create_account_op(address, tx.amount) else: # Get issuer address details: issuer_address = get_issuer_address(tx.issuer, tx.currency) address_obj = self.address address_obj.get() self.builder.append_payment_op(address, tx.amount, tx.currency, issuer_address) try: self.builder.sign() self.builder.submit() except Exception as exc: print(exc.payload) def get_balance(self): address = self.address address.get() for balance in address.balances: if balance['asset_type'] == 'native': return to_cents(Decimal(balance['balance']), 7) def get_issuer_address(self, issuer, asset_code): if self._is_valid_address(issuer): address = issuer else: if '*' in issuer: address = get_federation_details(issuer)['account_id'] else: # assume it is an anchor domain address = address_from_domain(issuer, asset_code) return address def trust_issuer(self, asset_code, issuer): logger.info('Trusting issuer: %s %s' % (issuer, asset_code)) address = self.get_issuer_address(issuer, asset_code) self.builder.append_trust_op(address, asset_code) try: self.builder.sign() self.builder.submit() except Exception as exc: print(exc.payload) # Generate new crypto address/ account id @staticmethod def new_account_id(**kwargs): metadata = kwargs.get('metadata') account_id = metadata['username'] + '*' + getattr(settings, 'STELLAR_WALLET_DOMAIN') return account_id def get_account_details(self): address = self.account.account_id qr_code = create_qr_code_url('stellar:' + str(address)) return {'account_id': address, 'metadata': {'qr_code': qr_code}}
async def build_joint_wallet( transaction_source_address: str, deal_address: str, parties: List, creator: str, starting_xlm: Decimal, meta: str = None, sequence: str = None, ): """Build transaction for create joint wallet, trust HOT and set option signer.""" def _add_signer(builder: Builder, deal_address: str, party: str, amount: Decimal): """Set permission of parties can signed transaction that generate from joint account""" builder.append_set_options_op(source=deal_address, signer_address=party, signer_type='ed25519PublicKey', signer_weight=1) builder.append_payment_op( source=party, destination=deal_address, asset_code=settings['ASSET_CODE'], asset_issuer=settings['ISSUER'], amount=amount, ) builder = Builder( address=transaction_source_address, horizon=settings['HORIZON_URL'], network=settings['PASSPHRASE'], sequence=sequence, ) builder.append_create_account_op(source=creator, destination=deal_address, starting_balance=starting_xlm) builder.append_trust_op(source=deal_address, destination=settings['ISSUER'], code=settings['ASSET_CODE'], limit=settings['LIMIT_ASSET']) builder.append_set_options_op(source=deal_address, signer_address=creator, signer_type='ed25519PublicKey', signer_weight=1) for party in parties: _add_signer(builder, deal_address, party['address'], party['amount']) if meta and isinstance(meta, dict): for key, value in meta.items(): builder.append_manage_data_op(source=deal_address, data_name=key, data_value=value) builder.append_manage_data_op(source=deal_address, data_name='creator_address', data_value=creator) weight = len(parties) + 1 builder.append_set_options_op(source=deal_address, master_weight=0, low_threshold=weight, med_threshold=weight, high_threshold=weight) xdr = builder.gen_xdr() tx_hash = builder.te.hash_meta() return xdr.decode(), binascii.hexlify(tx_hash).decode()