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))
Ejemplo n.º 2
0
    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))
Ejemplo n.º 3
0
    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)