text: import sys
  import signal
  import time
  import datetime
  import asyncio
  from decimal import Decimal
  from typing import Tuple, List, Optional, Dict
  from collections import defaultdict
  
  import django.db.transaction
  from django.core.management import BaseCommand
  from stellar_sdk import (
      Keypair,
      TransactionEnvelope,
      Asset,
      Claimant,
      ServerAsync,
      MuxedAccount,
  )
  from stellar_sdk.client.aiohttp_client import AiohttpClient
  from stellar_sdk.account import Account
  from stellar_sdk.exceptions import (
      BaseHorizonError,
      ConnectionError,
      NotFoundError,
      BaseRequestError,
  )
  from stellar_sdk.transaction_builder import TransactionBuilder
  from stellar_sdk.xdr import TransactionResult, OperationType
  from asgiref.sync import sync_to_async
  
  from polaris import settings
  from polaris.utils import (
      is_pending_trust,
      maybe_make_callback,
      maybe_make_callback_async,
      get_account_obj_async,
  )
  from polaris.integrations import (
      registered_deposit_integration as rdi,
      registered_rails_integration as rri,
      registered_fee_func,
      calculate_fee,
  )
  from polaris.models import Transaction
  from polaris.utils import getLogger, make_memo
  
  logger = getLogger(__name__)
  
  TERMINATE = False
  """
  SIGINT and SIGTERM signals to this process set TERMINATE to True,
  and once all pending tasks complete, the process exits.
  Only relevant if the --loop option is specified.
  """
  
  DEFAULT_INTERVAL = 10
  """
  The default amount of time to sleep before querying for transactions again
  Only used if the --loop option is specified.
  """
  
  
  class Command(BaseCommand):
      """
      The process_pending_deposits command handler.
      """
  
      def __init__(self, *args, **kwargs):
          super().__init__(*args, **kwargs)
          signal.signal(signal.SIGINT, self.exit_gracefully)
          signal.signal(signal.SIGTERM, self.exit_gracefully)
  
      @staticmethod
      def exit_gracefully(*_):  # pragma: no cover
          logger.info("Exiting process_pending_deposits...")
          module = sys.modules[__name__]
          module.TERMINATE = True
  
      @staticmethod
      def sleep(seconds):  # pragma: no cover
          for _ in range(seconds):
              if TERMINATE:
                  break
              time.sleep(1)
  
      def add_arguments(self, parser):  # pragma: no cover
          parser.add_argument(
              "--loop",
              action="store_true",
              help="Continually restart command after a specified number of seconds.",
          )
          parser.add_argument(
              "--interval",
              "-i",
              type=int,
              help="The number of seconds to wait before restarting command. "
              "Defaults to {}.".format(DEFAULT_INTERVAL),
          )
  
      def handle(self, *_args, **options):  # pragma: no cover
          """
          The entrypoint for the functionality implemented in this file.
  
          Calls process_deposits(), and if the --loop option is used, does so
          periodically after sleeping for the number of seconds specified by
          --interval.
          """
          if options.get("loop"):
              while True:
                  if TERMINATE:
                      break
                  asyncio.run(self.process_deposits())
                  self.sleep(options.get("interval") or DEFAULT_INTERVAL)
          else:
              asyncio.run(self.process_deposits())
  
      @classmethod
      async def process_deposits(cls):
          """
          The entry-point for the command's functionality. Queries for transactions in three states:
          - Transactions that have become available in the anchor off-chain account
          - Multi-sig transactions that have all signatures collected
          - Transactions waiting for the client application to establish a trustline for the request asset
  
          All transactions retreived have ``Transaction.pending_execution_attempt`` set to True to ensure
          they are not retreived by other invocation of this command.
  
          Tasks are queued for execution on the event loop for every transaction retreived. All tasks
          ensure that ``Transaction.pending_execution_attempt`` is set to False by the end of execution.
          """
          ready_transactions = await sync_to_async(PendingDeposits.get_ready_deposits)()
          ready_multisig_transactions = await sync_to_async(
              PendingDeposits.get_ready_multisig_deposits
          )()
          pending_trust_transactions = await sync_to_async(
              PendingDeposits.get_pending_trust_transactions
          )()
          locks = {
              "source_accounts": defaultdict(asyncio.Lock),
              "destination_accounts": defaultdict(asyncio.Lock),
          }
          async with ServerAsync(settings.HORIZON_URI, client=AiohttpClient()) as server:
              results = await asyncio.gather(
                  *[
                      PendingDeposits.process_deposit(t, server, locks)
                      for t in ready_transactions
                  ],
                  *[
                      PendingDeposits.handle_submit(t, server, locks)
                      for t in ready_multisig_transactions
                  ],
                  *[
                      PendingDeposits.check_trustline(t, server, locks)
                      for t in pending_trust_transactions
                  ],
                  return_exceptions=True,
              )
          await cls.handle_unexpected_exceptions(
              results,
              ready_transactions
              + ready_multisig_transactions
              + pending_trust_transactions,
          )
  
      @classmethod
      async def handle_unexpected_exceptions(cls, results, processed_transactions):
          for i, transaction in enumerate(processed_transactions):
              if results[i] is None:  # no exeption raised
                  continue
              try:
                  raise results[i]
              except type(results[i]):
                  logger.exception(
                      f"An unexpected exception occured while processing transaction {transaction.id}"
                  )
                  transaction.status_message = str(results[i])
                  transaction.status = Transaction.STATUS.error
                  transaction.pending_execution_attempt = False
                  await sync_to_async(transaction.save)()
                  await maybe_make_callback_async(transaction)
  
  
  class PendingDeposits:
      @classmethod
      async def process_deposit(cls, transaction, server, locks):
          """
          Evaluate `transaction` and determine if it is ready for submission to the
          Stellar Network. The transaction could be in one of the following states:
  
          - The destination account does not exist or does not have a trustline to
          the asset and the initiating client application does not support claimable
          balances.
  
              If the account exists, or after it has been created, the transaction is
              placed in the `pending_trust` status. If the account doesn't exist and
              the distribution account requires multiple signatures, Polaris requests
              a channel account from the Anchor and submits a create account operation
              using the channel account as the source instead of the distribution
              account.
  
          - The distribution account that will submit the transaction requires
          multiple signatures that have not been collected.
  
              In this case Transaction.pending_signatures is set to True, and the
              the anchor is expected collect signatures and set the column back to
              False.
  
          - The transaction is ready to be submitted
  
              In this case, the transaction is submitted to the Stellar Network and
              updated as complete after success.
          """
          logger.info(f"processing transaction {transaction.id}")
          if await cls.requires_trustline(transaction, server, locks):
              return
  
          logger.info(f"transaction {transaction.id} has the appropriate trustline")
          try:
              requires_multisig = await cls.requires_multisig(transaction)
          except NotFoundError:
              await sync_to_async(cls.handle_error)(
                  transaction,
                  f"{transaction.asset.code} distribution account "
                  f"{transaction.asset.distribution_account} does not exist",
              )
              await maybe_make_callback_async(transaction)
              return
          except ConnectionError:
              await sync_to_async(cls.handle_error)(
                  transaction,
                  f"Unable to connect to horizon to fetch {transaction.asset.code} "
                  "distribution account signers",
              )
              await maybe_make_callback_async(transaction)
              return
          if requires_multisig:
              logger.info(f"transaction {transaction.id} requires multiple signatures")
              await cls.save_as_pending_signatures(transaction, server)
              return
          logger.debug(f"transaction {transaction.id} does not require multisig")
  
          await cls.handle_submit(transaction, server, locks)
  
      @classmethod
      def get_ready_deposits(cls) -> List[Transaction]:
          """
          Polaris' API server processes deposit request and places the associated Transaction
          object in the `pending_user_transfer_start` status when all information necessary to
          submit the payment operation on Stellar has been collected.
  
          This function queries for these transaction, in addition to the Transaction objects
          that have been identified as pending external rails, and passes them to the
          DepositIntegration.poll_pending_deposits() integration function. Anchors return the
          transactions that are now available in their off-chain account and therefore ready
          for submission to the Stellar Network. Finally, this function performs various
          validations to ensure the transaction is truly ready and returns them.
          """
          pending_deposits = (
              Transaction.objects.filter(
                  status__in=[
                      Transaction.STATUS.pending_user_transfer_start,
                      Transaction.STATUS.pending_external,
                  ],
                  kind__in=[
                      Transaction.KIND.deposit,
                      getattr(Transaction.KIND, "deposit-exchange"),
                  ],
                  pending_execution_attempt=False,
              )
              .select_related("asset")
              .select_for_update()
          )
          with django.db.transaction.atomic():
              ready_transactions = rri.poll_pending_deposits(pending_deposits)
              Transaction.objects.filter(
                  id__in=[t.id for t in ready_transactions]
              ).update(pending_execution_attempt=True)
          verified_ready_transactions = []
          for transaction in ready_transactions:
              if not transaction.amount_fee or not transaction.amount_out:
                  if transaction.quote:
                      logger.error(
                          f"transaction {transaction.id} uses a quote but was returned "
                          "from poll_pending_deposits() without amount_fee or amount_out "
                          "assigned, skipping"
                      )
                      continue
                  logger.warning(
                      f"transaction {transaction.id} was returned from "
                      f"poll_pending_deposits() without Transaction.amount_fee or "
                      f"Transaction.amount_out assigned. Future Polaris "
                      "releases will not calculate fees and delivered amounts"
                  )
              # refresh from DB to pull pending_execution_attempt value and to ensure invalid
              # values were not assigned to the transaction in rri.poll_pending_deposits()
              asset = transaction.asset
              quote = transaction.quote
              transaction.refresh_from_db()
              transaction.asset = asset
              transaction.quote = quote
              if transaction.kind not in [
                  transaction.KIND.deposit,
                  getattr(transaction.KIND, "deposit-exchange"),
              ]:
                  cls.handle_error(
                      transaction,
                      "poll_pending_deposits() returned a non-deposit transaction",
                  )
                  maybe_make_callback(transaction)
                  continue
              if transaction.amount_in is None:
                  cls.handle_error(
                      transaction,
                      "poll_pending_deposits() did not assign a value to the "
                      "amount_in field of a Transaction object returned",
                  )
                  maybe_make_callback(transaction)
                  continue
              elif transaction.amount_fee is None:
                  if registered_fee_func is calculate_fee:
                      try:
                          transaction.amount_fee = calculate_fee(
                              fee_params={
                                  "amount": transaction.amount_in,
                                  "operation": settings.OPERATION_DEPOSIT,
                                  "asset_code": transaction.asset.code,
                              }
                          )
                      except ValueError:
                          transaction.amount_fee = Decimal(0)
                  else:
                      transaction.amount_fee = Decimal(0)
                  transaction.save()
              verified_ready_transactions.append(transaction)
          return verified_ready_transactions
  
      @staticmethod
      def get_pending_trust_transactions():
          """
          If the destination account does not have a trustline to the requested
          asset and the client application that initiated the request does not
          support claimable balances, Polaris places the transaction in the
          `pending_trust` status.
  
          This function retrieves those transactions and ensure other processes
          do not retreive the same transactions for processing by updating
          Transaction.pending_execution_attempt to True.
  
          The returned transactions will be submitted if their destination
          accounts now have a trustline to the asset.
          """
          with django.db.transaction.atomic():
              transactions = list(
                  Transaction.objects.filter(
                      kind=Transaction.KIND.deposit,
                      status=Transaction.STATUS.pending_trust,
                      pending_execution_attempt=False,
                  )
                  .select_related("asset")
                  .select_for_update()
              )
              Transaction.objects.filter(id__in=[t.id for t in transactions]).update(
                  pending_execution_attempt=True
              )
              return transactions
  
      @staticmethod
      def get_ready_multisig_deposits():
          """
          If the anchor's distribution account requires multiple signatures before
          submitting to Stellar, Polaris generates the envelope and updates
          Transaction.pending_signatures to True.
  
          Polaris then expects the anchor to collect the necessary signatures and
          set Transaction.pending_signatures back to False. This function checks if
          any transaction is in this state and returns it for submission to the
          Stellar Network.
  
          Multisig transactions are therefore identified by a non-null envelope_xdr
          column and a 'pending_anchor' status. The status check is important
          because all successfully submitted transactions have their envelope_xdr
          column set after submission and status set to 'completed'.
          """
          with django.db.transaction.atomic():
              multisig_transactions = list(
                  Transaction.objects.filter(
                      kind=Transaction.KIND.deposit,
                      status=Transaction.STATUS.pending_anchor,
                      pending_signatures=False,
                      envelope_xdr__isnull=False,
                      pending_execution_attempt=False,
                  )
                  .select_related("asset")
                  .select_for_update()
              )
              Transaction.objects.filter(
                  id__in=[t.id for t in multisig_transactions]
              ).update(pending_execution_attempt=True)
              for t in multisig_transactions:
                  logger.debug(
                      f"Detected multisig transaction {t.id} is ready to be submitted"
                  )
              return multisig_transactions
  
      @classmethod
      async def get_or_create_destination_account(
          cls, transaction: Transaction, server: ServerAsync, locks: Dict
      ) -> Tuple[Account, bool]:
          """
          Returns:
              Account: The account found or created for the Transaction
              bool: True if trustline doesn't exist, False otherwise.
  
          If the account doesn't exist, Polaris must create the account using an account provided by the
          anchor. Polaris can use the distribution account of the anchored asset or a channel account if
          the asset's distribution account requires non-master signatures.
  
          If the transacted asset's distribution account does require non-master signatures,
          DepositIntegration.create_channel_account() will be called. See the function docstring for more
          info.
  
          On failure to create the destination account, a RuntimeError exception is raised.
          """
          logger.debug(
              f"requesting lock to get or create destination account for transaction {transaction.id}"
          )
          async with locks["destination_accounts"][transaction.to_address]:
              logger.debug(
                  f"got lock to get or create destination account for transaction {transaction.id}"
              )
              if transaction.to_address.startswith("M"):
                  destination_account = MuxedAccount.from_account(
                      transaction.to_address
                  ).account_id
              else:
                  destination_account = transaction.to_address
              try:
                  account, json_resp = await get_account_obj_async(
                      Keypair.from_public_key(destination_account), server
                  )
                  logger.debug(f"account for transaction {transaction.id} exists")
                  return account, is_pending_trust(transaction, json_resp)
              except RuntimeError:  # account does not exist
                  logger.debug(f"account for transaction {transaction.id} does not exist")
                  try:
                      requires_multisig = await PendingDeposits.requires_multisig(
                          transaction
                      )
                  except NotFoundError:
                      logger.error(
                          f"{transaction.asset.code} distribution account "
                          f"{transaction.asset.distribution_account} does not exist"
                      )
                      raise RuntimeError("the distribution account does not exist")
                  if requires_multisig:
                      source_account_kp = await sync_to_async(cls.get_channel_keypair)(
                          transaction
                      )
                  else:
                      source_account_kp = Keypair.from_secret(
                          transaction.asset.distribution_seed
                      )
  
                  logger.debug(
                      f"requesting lock to create account for transaction {transaction.id}"
                  )
                  async with locks["source_accounts"][
                      transaction.asset.distribution_account
                  ]:
                      logger.debug(
                          f"locked for transaction {transaction.id} to create destination account"
                      )
                      source_account, _ = await get_account_obj_async(
                          source_account_kp, server
                      )
                      builder = TransactionBuilder(
                          source_account=source_account,
                          network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE,
                          # this transaction contains one operation so base_fee will be multiplied by 1
                          base_fee=settings.MAX_TRANSACTION_FEE_STROOPS
                          or await server.fetch_base_fee(),
                      )
                      transaction_envelope = builder.append_create_account_op(
                          destination=transaction.to_address,
                          starting_balance=settings.ACCOUNT_STARTING_BALANCE,
                      ).build()
                      transaction_envelope.sign(source_account_kp)
  
                      try:
                          await server.submit_transaction(transaction_envelope)
                      except BaseHorizonError as e:  # pragma: no cover
                          raise RuntimeError(
                              "Horizon error when submitting create account "
                              f"to horizon: {e.message}"
                          )
                      logger.debug(
                          f"unlocking after creating destination account for transaction {transaction.id}"
                      )
  
                  account, _ = await get_account_obj_async(
                      Keypair.from_public_key(transaction.to_address), server
                  )
                  return account, True
              except BaseHorizonError as e:
                  raise RuntimeError(
                      f"Horizon error when loading stellar account: {e.message}"
                  )
              except ConnectionError:
                  raise RuntimeError("Failed to connect to Horizon")
  
      @classmethod
      async def check_trustline(
          cls, transaction: Transaction, server: ServerAsync, locks: Dict
      ):
          """
          Load the destination account json to determine if a trustline has been
          established. If a trustline for the requested asset is found, a the
          transaction is scheduled for processing. If not, the transaction is
          updated to no longer be pending an execution attempt.
          """
          if transaction.to_address.startswith("M"):
              destination_account = MuxedAccount.from_account(
                  transaction.to_address
              ).account_id
          else:
              destination_account = transaction.to_address
          try:
              _, account = await get_account_obj_async(
                  Keypair.from_public_key(destination_account), server
              )
          except BaseRequestError:
              logger.exception(f"Failed to load account {destination_account}")
              transaction.pending_execution_attempt = False
              await sync_to_async(transaction.save)()
              return
          trustline_found = False
          for balance in account["balances"]:
              if balance.get("asset_type") in ["native", "liquidity_pool_shares"]:
                  continue
              if (
                  balance["asset_code"] == transaction.asset.code
                  and balance["asset_issuer"] == transaction.asset.issuer
              ):
                  trustline_found = True
                  break
          if trustline_found:
              logger.debug(
                  f"detected transaction {transaction.id} is no longer pending trust"
              )
              await cls.process_deposit(transaction, server, locks)
          else:
              transaction.pending_execution_attempt = False
              await sync_to_async(transaction.save)()
  
      @classmethod
      async def submit(cls, transaction: Transaction, server: ServerAsync, locks) -> bool:
          valid_statuses = [
              Transaction.STATUS.pending_user_transfer_start,
              Transaction.STATUS.pending_external,
              Transaction.STATUS.pending_anchor,
              Transaction.STATUS.pending_trust,
          ]
          if transaction.status not in valid_statuses:
              raise ValueError(
                  f"Unexpected transaction status: {transaction.status}, expecting "
                  f"{' or '.join(valid_statuses)}."
              )
  
          logger.info(f"initiating Stellar deposit for {transaction.id}")
          transaction.status = Transaction.STATUS.pending_anchor
          await sync_to_async(transaction.save)()
          await maybe_make_callback_async(transaction)
  
          envelope = None
          if transaction.envelope_xdr:
              try:
                  envelope = TransactionEnvelope.from_xdr(
                      transaction.envelope_xdr, settings.STELLAR_NETWORK_PASSPHRASE
                  )
              except Exception:
                  await sync_to_async(cls.handle_error)(
                      transaction, "Failed to decode transaction envelope"
                  )
                  await maybe_make_callback_async(transaction)
                  return False
  
          logger.debug(f"requesting lock to submit deposit transaction {transaction.id}")
          async with locks["source_accounts"][transaction.asset.distribution_account]:
              logger.debug(
                  f"locked to submit deposit transaction for transaction {transaction.id}"
              )
              if not envelope:
                  distribution_acc, _ = await get_account_obj_async(
                      Keypair.from_public_key(transaction.asset.distribution_account),
                      server,
                  )
                  envelope = await cls.create_deposit_envelope(
                      transaction, distribution_acc, server
                  )
                  envelope.sign(transaction.asset.distribution_seed)
                  transaction.envelope_xdr = envelope.to_xdr()
  
              transaction.status = Transaction.STATUS.pending_stellar
              await sync_to_async(transaction.save)()
              logger.info(
                  f"updating transaction {transaction.id} to pending_stellar status"
              )
              await maybe_make_callback_async(transaction)
  
              try:
                  response = await server.submit_transaction(envelope)
              except (BaseHorizonError, ConnectionError) as e:
                  message = getattr(e, "message", str(e))
                  await sync_to_async(cls.handle_error)(
                      transaction, f"{e.__class__.__name__}: {message}"
                  )
                  await maybe_make_callback_async(transaction)
                  return False
              logger.debug(f"unlocking after submitting transaction {transaction.id}")
  
          if not response.get("successful"):
              await sync_to_async(cls.handle_error)(
                  transaction,
                  "Stellar transaction failed when submitted to horizon: "
                  f"{response['result_xdr']}",
              )
              await maybe_make_callback_async(transaction)
              return False
          elif transaction.claimable_balance_supported:
              transaction.claimable_balance_id = cls.get_balance_id(response)
  
          transaction.paging_token = response["paging_token"]
          transaction.stellar_transaction_id = response["id"]
          transaction.status = Transaction.STATUS.completed
          transaction.completed_at = datetime.datetime.now(datetime.timezone.utc)
          transaction.pending_execution_attempt = False
          if not transaction.quote:
              transaction.amount_out = round(
                  Decimal(transaction.amount_in) - Decimal(transaction.amount_fee),
                  transaction.asset.significant_decimals,
              )
          await sync_to_async(transaction.save)()
          logger.info(f"transaction {transaction.id} completed.")
          await maybe_make_callback_async(transaction)
          return True
  
      @classmethod
      async def handle_submit(cls, transaction: Transaction, server, locks):
          logger.debug(f"calling submit() for transaction {transaction.id}")
          try:
              success = await PendingDeposits.submit(transaction, server, locks)
          except Exception as e:
              logger.exception("submit() threw an unexpected exception")
              await sync_to_async(cls.handle_error)(transaction, str(e))
              await maybe_make_callback_async(transaction)
              return
  
          if success:
              await sync_to_async(transaction.refresh_from_db)()
              try:
                  await sync_to_async(rdi.after_deposit)(transaction=transaction)
              except NotImplementedError:
                  pass
              except Exception:
                  logger.exception("after_deposit() threw an unexpected exception")
  
      @staticmethod
      async def create_deposit_envelope(
          transaction, source_account, server
      ) -> TransactionEnvelope:
          if transaction.amount_out:
              payment_amount = transaction.amount_out
          elif transaction.quote:
              raise RuntimeError(
                  f"transaction {transaction.id} uses a quote but does not have "
                  "amount_out assigned"
              )
          else:
              payment_amount = round(
                  Decimal(transaction.amount_in) - Decimal(transaction.amount_fee),
                  transaction.asset.significant_decimals,
              )
          builder = TransactionBuilder(
              source_account=source_account,
              network_passphrase=settings.STELLAR_NETWORK_PASSPHRASE,
              # only one operation, so base_fee will be multipled by 1
              base_fee=settings.MAX_TRANSACTION_FEE_STROOPS
              or await server.fetch_base_fee(),
          )
          payment_op_kwargs = {
              "destination": transaction.to_address,
              "asset": Asset(
                  code=transaction.asset.code, issuer=transaction.asset.issuer
              ),
              "amount": str(payment_amount),
              "source": transaction.asset.distribution_account,
          }
          if transaction.claimable_balance_supported:
              _, json_resp = await get_account_obj_async(
                  Keypair.from_public_key(transaction.to_address), server
              )
              if is_pending_trust(transaction, json_resp):
                  claimant = Claimant(destination=transaction.to_address)
                  asset = Asset(
                      code=transaction.asset.code, issuer=transaction.asset.issuer
                  )
                  builder.append_create_claimable_balance_op(
                      claimants=[claimant],
                      asset=asset,
                      amount=str(payment_amount),
                      source=transaction.asset.distribution_account,
                  )
              else:
                  builder.append_payment_op(**payment_op_kwargs)
          else:
              builder.append_payment_op(**payment_op_kwargs)
          if transaction.memo:
              builder.add_memo(make_memo(transaction.memo, transaction.memo_type))
          return builder.build()
  
      @staticmethod
      def get_balance_id(response: dict) -> Optional[str]:
          """
          Pulls claimable balance ID from horizon responses if present
  
          When called we decode and read the result_xdr from the horizon response.
          If any of the operations is a createClaimableBalanceResult we
          decode the Base64 representation of the balanceID xdr.
          After the fact we encode the result to hex.
  
          The hex representation of the balanceID is important because its the
          representation required to query and claim claimableBalances.
  
          :param
              response: the response from horizon
  
          :return
              hex representation of the balanceID
              or
              None (if no createClaimableBalanceResult operation is found)
          """
          result_xdr = response["result_xdr"]
          balance_id_hex = None
          for op_result in TransactionResult.from_xdr(result_xdr).result.results:
              if op_result.tr.type == OperationType.CREATE_CLAIMABLE_BALANCE:
                  balance_id_hex = (
                      op_result.tr.create_claimable_balance_result.balance_id.to_xdr_bytes().hex()
                  )
          return balance_id_hex
  
      @classmethod
      async def requires_trustline(
          cls, transaction: Transaction, server: ServerAsync, locks: Dict
      ) -> bool:
          try:
              _, pending_trust = await PendingDeposits.get_or_create_destination_account(
                  transaction, server, locks
              )
          except RuntimeError as e:
              await sync_to_async(cls.handle_error)(transaction, str(e))
              await maybe_make_callback_async(transaction)
              return True
  
          if pending_trust and not transaction.claimable_balance_supported:
              logger.info(
                  f"destination account is pending_trust for transaction {transaction.id}"
              )
              transaction.status = Transaction.STATUS.pending_trust
              transaction.pending_execution_attempt = False
              await sync_to_async(transaction.save)()
              await maybe_make_callback_async(transaction)
              return True
  
          return False
  
      @staticmethod
      async def requires_multisig(transaction: Transaction) -> bool:
          logger.debug(f"checking if transaction {transaction.id} requires multisig")
          master_signer = (
              await transaction.asset.get_distribution_account_master_signer_async()
          )
          thresholds = await transaction.asset.get_distribution_account_thresholds_async()
          return (
              not master_signer
              or master_signer["weight"] == 0
              or master_signer["weight"] < thresholds["med_threshold"]
          )
  
      @classmethod
      async def save_as_pending_signatures(cls, transaction, server):
          try:
              channel_kp = await sync_to_async(cls.get_channel_keypair)(transaction)
              channel_account, _ = await get_account_obj_async(channel_kp, server)
          except (RuntimeError, ConnectionError) as e:
              transaction.status = Transaction.STATUS.error
              transaction.status_message = str(e)
              logger.error(transaction.status_message)
          else:
              # Create the initial envelope XDR with the channel signature
              envelope = await PendingDeposits.create_deposit_envelope(
                  transaction, channel_account, server
              )
              envelope.sign(channel_kp)
              transaction.envelope_xdr = envelope.to_xdr()
              transaction.pending_signatures = True
              transaction.status = Transaction.STATUS.pending_anchor
          transaction.pending_execution_attempt = False
          await sync_to_async(transaction.save)()
          await maybe_make_callback_async(transaction)
  
      @staticmethod
      def get_channel_keypair(transaction) -> Keypair:
          if not transaction.channel_account:
              logger.info(
                  f"calling create_channel_account() for transaction {transaction.id}"
              )
              rdi.create_channel_account(transaction=transaction)
          if not transaction.channel_seed:
              asset = transaction.asset
              quote = transaction.quote
              transaction.refresh_from_db()
              transaction.asset = asset
              transaction.quote = quote
          return Keypair.from_secret(transaction.channel_seed)
  
      @classmethod
      def handle_error(cls, transaction, message):
          transaction.status_message = message
          transaction.status = Transaction.STATUS.error
          transaction.pending_execution_attempt = False
          transaction.save()
          logger.error(message)
  