def _handle_receiver(self, tx: str, receiver: TransactionReceiver, index: int, senders: List[TransactionSender] = None) -> None: # ---------- Pre-Calculation ---------- # fee to be used for the custom currency coin_fee = cast(int, self._fee_service.get_coin_fee()) # fee to be transferred to the Gateway owner gateway_fee = cast(int, self._fee_service.get_gateway_fee()) received_amount = cast(int, receiver.amount) # receiver coin address coin_receiver_address = self._waves_chain_query_service.get_coin_receiver_address_from_transaction( tx) coin_receivers: List[TransactionAttemptReceiver] = [] # only add the gateway receiver if the gateway fee is being collected if gateway_fee > 0: coin_receivers.insert( 0, TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee, )) # the amount of coins to be transferred after applying the fees if self._only_one_transaction_receiver: amount_after_fees = received_amount - ( 1 + len(coin_receivers)) * coin_fee - gateway_fee else: amount_after_fees = received_amount - coin_fee - gateway_fee # add the main receiver before other receivers coin_receivers.insert( 0, TransactionAttemptReceiver( address=coin_receiver_address, amount=amount_after_fees, )) # ---------- Pre-Check ---------- if not self._coin_address_validation_service.validate_address( coin_receiver_address): self._logger.warning( "Received transaction %s with an invalid coin receiver address %s. " "Will be skipped.", tx, coin_receiver_address) return if self._gateway_coin_holder_secret.public == coin_receiver_address: self._logger.warning( "Received transaction %s with the address of the Gateway coin holder." "Will be skipped as an transaction to itself has no effect.", tx) return # transaction must have at least a specific amount to be processed if amount_after_fees < 0: self._logger.warning( "Received transaction %s with an amount of %s, but it is less than the at least " "required amount. Will be skipped.", tx, receiver.amount) return attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=tx, currency="waves", receiver=index)) if attempt_list is None: trigger = AttemptListTrigger(tx=tx, receiver=index, currency="waves", senders=senders) attempts = list() if self._only_one_transaction_receiver: attempts.extend([ TransactionAttempt( sender=self._gateway_coin_holder_secret.public, fee=coin_fee, currency="coin", receivers=[receiver]) for receiver in coin_receivers ]) else: attempts.append( TransactionAttempt( sender=self._gateway_coin_holder_secret.public, fee=coin_fee, currency="coin", receivers=coin_receivers)) attempt_list = TransactionAttemptList( trigger, attempts, last_modified=datetime.datetime.utcnow(), created_at=datetime.datetime.utcnow()) self._attempt_list_storage.safely_save_attempt_list(attempt_list) self._logger.info('Created new attempt list %s', str(attempt_list.attempt_list_id))
def _handle_transaction_receiver(self, tx: str, receiver: TransactionReceiver, index: int, senders: List[TransactionSender] = None): # ---------- Pre-Calculation ---------- # the gateway address that coins were transferred to gateway_managed_address = receiver.address # the custom currency fee to be used coin_fee = cast(int, self._fee_service.get_coin_fee()) # the waves fee to be used; not used in the calculation waves_fee = cast(int, self._fee_service.get_waves_fee()) # the fee for the gateway owner; service costs gateway_fee = cast(int, self._fee_service.get_gateway_fee()) # the amount of coins that were received by this receiver received_amount = cast(int, receiver.amount) # the waves address to receive the amount of coins receiver_waves_address = self._map_storage.get_waves_address_by_coin_address(gateway_managed_address) coin_receivers: List[TransactionAttemptReceiver] = [] # only add the gateway receiver if the gateway fee is being collected if gateway_fee > 0: coin_receivers.insert(0, TransactionAttemptReceiver( address=self._gateway_owner_address, amount=gateway_fee, )) # the amount of coins to be transferred after applying the fees if self._only_one_transaction_receiver: amount_after_fees = received_amount - (1 + len(coin_receivers)) * coin_fee - gateway_fee else: amount_after_fees = received_amount - coin_fee - gateway_fee # add the main receiver before other receivers coin_receivers.insert(0, TransactionAttemptReceiver( address=self._gateway_coin_address, amount=amount_after_fees, )) # ---------- Pre-Check ---------- # transaction must have minimum amount to be handled if amount_after_fees <= 0: self._logger.warn("Received transaction %s with an amount of %s, but it is less than the at least " "required amount. Will be skipped, but marked as processed.", tx, receiver.amount) return # no mapping was found for some reason; this in an internal error as this should # already be prevented by the filter method if receiver_waves_address is None: raise MappingNotFoundForCoinAddress(gateway_managed_address) attempt_list = self._attempt_list_storage.find_by_trigger( AttemptListTrigger(tx=tx, currency="coin", receiver=index)) if attempt_list is None: attempts = list() if self._only_one_transaction_receiver: attempts.extend([ TransactionAttempt( sender=gateway_managed_address, currency="coin", fee=coin_fee, receivers=[receiver]) for receiver in coin_receivers ]) else: attempts.append( TransactionAttempt( sender=gateway_managed_address, currency="coin", fee=coin_fee, receivers=coin_receivers)) attempts.append( TransactionAttempt( sender=self._gateway_pywaves_address.address, fee=waves_fee, currency="waves", receivers=[TransactionAttemptReceiver(address=receiver_waves_address, amount=amount_after_fees)])) trigger = AttemptListTrigger(tx, index, "coin", senders=senders) attempt_list = TransactionAttemptList( trigger, attempts, last_modified=datetime.datetime.utcnow(), created_at=datetime.datetime.utcnow()) self._attempt_list_storage.safely_save_attempt_list(attempt_list) self._logger.info('Created new attempt list %s', str(attempt_list.attempt_list_id))
def test_handle_transaction_only_one_receiver(self, mock_datetime: MagicMock): now = MagicMock() mock_datetime.utcnow.return_value = now gateway_coin_address = self._gateway_managed_receiver.address dst_waves_address = '039769' incoming_transaction = Transaction( tx='723968', receivers=[self._gateway_managed_receiver]) amount_after_fees = self._gateway_managed_receiver.amount - 2 * self._coin_standard_fee - self._gateway_fee attempts = [ TransactionAttempt(sender=gateway_coin_address, currency="coin", fee=self._coin_standard_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_address, amount=amount_after_fees) ]), TransactionAttempt(sender=gateway_coin_address, currency="coin", fee=self._coin_standard_fee, receivers=[ TransactionAttemptReceiver( address=self._gateway_owner_address, amount=self._gateway_fee) ]), TransactionAttempt(sender=self._gateway_waves_address, fee=self._waves_standard_fee, currency="waves", receivers=[ TransactionAttemptReceiver( address=dst_waves_address, amount=amount_after_fees) ]) ] trigger = AttemptListTrigger(tx=incoming_transaction.tx, currency="coin", receiver=0) attempt_list = TransactionAttemptList(trigger=trigger, attempts=attempts, created_at=now, last_modified=now) self._attempt_list_storage.find_by_trigger.return_value = None self._map_storage.get_waves_address_by_coin_address.return_value = dst_waves_address self._coin_transaction_consumer_impl.handle_transaction( incoming_transaction) self._attempt_list_storage.find_by_trigger.assert_any_call( AttemptListTrigger(tx=incoming_transaction.tx, receiver=0, currency="coin")) self._map_storage.get_waves_address_by_coin_address.assert_called_once_with( gateway_coin_address) self._attempt_service.continue_transaction_attempt_list.assert_not_called( ) self._attempt_list_storage.safely_save_attempt_list.assert_called_once_with( attempt_list)