class TransactionService:
    def __init__(self, gas_station: GasStation, ethereum_client: EthereumClient, redis: Redis,
                 safe_valid_contract_addresses: Set[str], proxy_factory_address: str, tx_sender_private_key: str):
        self.gas_station = gas_station
        self.ethereum_client = ethereum_client
        self.redis = redis
        self.safe_valid_contract_addresses = safe_valid_contract_addresses
        self.proxy_factory = ProxyFactory(proxy_factory_address, self.ethereum_client)
        self.tx_sender_account = Account.from_key(tx_sender_private_key)

    def _check_refund_receiver(self, refund_receiver: str) -> bool:
        """
        Support tx.origin or relay tx sender as refund receiver.
        This would prevent that anybody can front-run our service
        :param refund_receiver: Payment refund receiver as Ethereum checksummed address
        :return: True if refund_receiver is ok, False otherwise
        """
        return refund_receiver in (NULL_ADDRESS, self.tx_sender_account.address)

    @staticmethod
    def _is_valid_gas_token(address: Optional[str]) -> float:
        """
        :param address: Token address
        :return: bool if gas token, false otherwise
        """
        address = address or NULL_ADDRESS
        if address == NULL_ADDRESS:
            return True
        try:
            Token.objects.get(address=address, gas=True)
            return True
        except Token.DoesNotExist:
            logger.warning('Cannot retrieve gas token from db: Gas token %s not valid', address)
            return False

    def _check_safe_gas_price(self, gas_token: Optional[str], safe_gas_price: int) -> bool:
        """
        Check that `safe_gas_price` is not too low, so that the relay gets a full refund
        for the tx. Gas_price must be always > 0, if not refunding would be disabled
        If a `gas_token` is used we need to calculate the `gas_price` in Eth
        Gas price must be at least >= _minimum_gas_price_ > 0
        :param gas_token: Address of token is used, `NULL_ADDRESS` or `None` if it's ETH
        :return:
        :exception GasPriceTooLow
        :exception InvalidGasToken
        """
        if safe_gas_price < 1:
            raise RefundMustBeEnabled('Tx internal gas price cannot be 0 or less, it was %d' % safe_gas_price)

        minimum_accepted_gas_price = self._get_minimum_gas_price()
        estimated_gas_price = self._estimate_tx_gas_price(minimum_accepted_gas_price, gas_token)
        if safe_gas_price < estimated_gas_price:
            raise GasPriceTooLow('Required gas-price>=%d with gas-token=%s' % (estimated_gas_price, gas_token))
        return True

    def _estimate_tx_gas_price(self, base_gas_price: int, gas_token: Optional[str] = None) -> int:
        if gas_token and gas_token != NULL_ADDRESS:
            try:
                gas_token_model = Token.objects.get(address=gas_token, gas=True)
                estimated_gas_price = gas_token_model.calculate_gas_price(base_gas_price)
            except Token.DoesNotExist:
                raise InvalidGasToken('Gas token %s not found' % gas_token)
        else:
            estimated_gas_price = base_gas_price

        # FIXME Remove 2 / 3, workaround to prevent frontrunning
        return int(estimated_gas_price * 2 / 3)

    def _get_configured_gas_price(self) -> int:
        """
        :return: Gas price for txs
        """
        return self.gas_station.get_gas_prices().fast

    def _get_minimum_gas_price(self) -> int:
        """
        :return: Minimum gas price accepted for txs set by the user
        """
        return self.gas_station.get_gas_prices().standard

    def get_last_used_nonce(self, safe_address: str) -> Optional[int]:
        safe = Safe(safe_address, self.ethereum_client)
        last_used_nonce = SafeMultisigTx.objects.get_last_nonce_for_safe(safe_address)
        last_used_nonce = last_used_nonce if last_used_nonce is not None else -1
        try:
            blockchain_nonce = safe.retrieve_nonce()
            last_used_nonce = max(last_used_nonce, blockchain_nonce - 1)
            if last_used_nonce < 0:  # There's no last_used_nonce
                last_used_nonce = None
            return last_used_nonce
        except BadFunctionCallOutput:  # If Safe does not exist
            raise SafeDoesNotExist(f'Safe={safe_address} does not exist')

    def estimate_tx(self, safe_address: str, to: str, value: int, data: bytes, operation: int,
                    gas_token: Optional[str]) -> TransactionEstimationWithNonce:
        """
        :return: TransactionEstimation with costs using the provided gas token and last used nonce of the Safe
        :raises: InvalidGasToken: If Gas Token is not valid
        """
        if not self._is_valid_gas_token(gas_token):
            raise InvalidGasToken(gas_token)

        last_used_nonce = self.get_last_used_nonce(safe_address)
        safe = Safe(safe_address, self.ethereum_client)
        safe_tx_gas = safe.estimate_tx_gas(to, value, data, operation)
        safe_tx_base_gas = safe.estimate_tx_base_gas(to, value, data, operation, gas_token, safe_tx_gas)

        # For Safe contracts v1.0.0 operational gas is not used (`base_gas` has all the related costs already)
        safe_version = safe.retrieve_version()
        if Version(safe_version) >= Version('1.0.0'):
            safe_tx_operational_gas = 0
        else:
            safe_tx_operational_gas = safe.estimate_tx_operational_gas(len(data) if data else 0)

        # Can throw RelayServiceException
        gas_price = self._estimate_tx_gas_price(self._get_configured_gas_price(), gas_token)
        return TransactionEstimationWithNonce(safe_tx_gas, safe_tx_base_gas, safe_tx_base_gas, safe_tx_operational_gas,
                                              gas_price, gas_token or NULL_ADDRESS, last_used_nonce,
                                              self.tx_sender_account.address)

    def estimate_tx_for_all_tokens(self, safe_address: str, to: str, value: int, data: bytes,
                                   operation: int) -> TransactionEstimationWithNonceAndGasTokens:
        """
        :return: TransactionEstimation with costs using ether and every gas token supported by the service,
        with the last used nonce of the Safe
        :raises: InvalidGasToken: If Gas Token is not valid
        """
        safe = Safe(safe_address, self.ethereum_client)
        last_used_nonce = self.get_last_used_nonce(safe_address)
        safe_tx_gas = safe.estimate_tx_gas(to, value, data, operation)

        safe_version = safe.retrieve_version()
        if Version(safe_version) >= Version('1.0.0'):
            safe_tx_operational_gas = 0
        else:
            safe_tx_operational_gas = safe.estimate_tx_operational_gas(len(data) if data else 0)

        # Calculate `base_gas` for ether and calculate for tokens using the ether token price
        ether_safe_tx_base_gas = safe.estimate_tx_base_gas(to, value, data, operation, NULL_ADDRESS, safe_tx_gas)
        base_gas_price = self._get_configured_gas_price()
        gas_price = self._estimate_tx_gas_price(base_gas_price, NULL_ADDRESS)
        gas_token_estimations = [TransactionGasTokenEstimation(ether_safe_tx_base_gas, gas_price, NULL_ADDRESS)]
        token_gas_difference = 50000  # 50K gas more expensive than ether
        for token in Token.objects.gas_tokens():
            try:
                gas_price = self._estimate_tx_gas_price(base_gas_price, token.address)
                gas_token_estimations.append(
                    TransactionGasTokenEstimation(ether_safe_tx_base_gas + token_gas_difference,
                                                  gas_price, token.address)
                )
            except CannotGetTokenPriceFromApi:
                logger.error('Cannot get price for token=%s', token.address)

        return TransactionEstimationWithNonceAndGasTokens(last_used_nonce, safe_tx_gas, safe_tx_operational_gas,
                                                          gas_token_estimations)

    def create_multisig_tx(self,
                           safe_address: str,
                           to: str,
                           value: int,
                           data: bytes,
                           operation: int,
                           safe_tx_gas: int,
                           base_gas: int,
                           gas_price: int,
                           gas_token: str,
                           refund_receiver: str,
                           safe_nonce: int,
                           signatures: List[Dict[str, int]]) -> SafeMultisigTx:
        """
        :return: Database model of SafeMultisigTx
        :raises: SafeMultisigTxExists: If Safe Multisig Tx with nonce already exists
        :raises: InvalidGasToken: If Gas Token is not valid
        :raises: TransactionServiceException: If Safe Tx is not valid (not sorted owners, bad signature, bad nonce...)
        """

        safe_contract, _ = SafeContract.objects.get_or_create(address=safe_address,
                                                              defaults={'master_copy': NULL_ADDRESS})
        created = timezone.now()

        if SafeMultisigTx.objects.not_failed().filter(safe=safe_contract, nonce=safe_nonce).exists():
            raise SafeMultisigTxExists(f'Tx with safe-nonce={safe_nonce} for safe={safe_address} already exists in DB')

        signature_pairs = [(s['v'], s['r'], s['s']) for s in signatures]
        signatures_packed = signatures_to_bytes(signature_pairs)

        try:
            tx_hash, safe_tx_hash, tx = self._send_multisig_tx(
                safe_address,
                to,
                value,
                data,
                operation,
                safe_tx_gas,
                base_gas,
                gas_price,
                gas_token,
                refund_receiver,
                safe_nonce,
                signatures_packed
            )
        except SafeServiceException as exc:
            raise TransactionServiceException(str(exc)) from exc

        ethereum_tx = EthereumTx.objects.create_from_tx_dict(tx, tx_hash)

        try:
            return SafeMultisigTx.objects.create(
                created=created,
                safe=safe_contract,
                ethereum_tx=ethereum_tx,
                to=to,
                value=value,
                data=data,
                operation=operation,
                safe_tx_gas=safe_tx_gas,
                data_gas=base_gas,
                gas_price=gas_price,
                gas_token=None if gas_token == NULL_ADDRESS else gas_token,
                refund_receiver=refund_receiver,
                nonce=safe_nonce,
                signatures=signatures_packed,
                safe_tx_hash=safe_tx_hash,
            )
        except IntegrityError as exc:
            raise SafeMultisigTxExists(f'Tx with safe_tx_hash={safe_tx_hash.hex()} already exists in DB') from exc

    def _send_multisig_tx(self,
                          safe_address: str,
                          to: str,
                          value: int,
                          data: bytes,
                          operation: int,
                          safe_tx_gas: int,
                          base_gas: int,
                          gas_price: int,
                          gas_token: str,
                          refund_receiver: str,
                          safe_nonce: int,
                          signatures: bytes,
                          block_identifier='latest') -> Tuple[bytes, bytes, Dict[str, Any]]:
        """
        This function calls the `send_multisig_tx` of the Safe, but has some limitations to prevent abusing
        the relay
        :return: Tuple(tx_hash, safe_tx_hash, tx)
        :raises: InvalidMultisigTx: If user tx cannot go through the Safe
        """

        safe = Safe(safe_address, self.ethereum_client)
        data = data or b''
        gas_token = gas_token or NULL_ADDRESS
        refund_receiver = refund_receiver or NULL_ADDRESS
        to = to or NULL_ADDRESS

        # Make sure refund receiver is set to 0x0 so that the contract refunds the gas costs to tx.origin
        if not self._check_refund_receiver(refund_receiver):
            raise InvalidRefundReceiver(refund_receiver)

        self._check_safe_gas_price(gas_token, gas_price)

        # Make sure proxy contract is ours
        if not self.proxy_factory.check_proxy_code(safe_address):
            raise InvalidProxyContract(safe_address)

        # Make sure master copy is valid
        safe_master_copy_address = safe.retrieve_master_copy_address()
        if safe_master_copy_address not in self.safe_valid_contract_addresses:
            raise InvalidMasterCopyAddress(safe_master_copy_address)

        # Check enough funds to pay for the gas
        if not safe.check_funds_for_tx_gas(safe_tx_gas, base_gas, gas_price, gas_token):
            raise NotEnoughFundsForMultisigTx

        threshold = safe.retrieve_threshold()
        number_signatures = len(signatures) // 65  # One signature = 65 bytes
        if number_signatures < threshold:
            raise SignaturesNotFound('Need at least %d signatures' % threshold)

        safe_tx_gas_estimation = safe.estimate_tx_gas(to, value, data, operation)
        safe_base_gas_estimation = safe.estimate_tx_base_gas(to, value, data, operation, gas_token,
                                                             safe_tx_gas_estimation)
        if safe_tx_gas < safe_tx_gas_estimation or base_gas < safe_base_gas_estimation:
            raise InvalidGasEstimation("Gas should be at least equal to safe-tx-gas=%d and base-gas=%d. Current is "
                                       "safe-tx-gas=%d and base-gas=%d" %
                                       (safe_tx_gas_estimation, safe_base_gas_estimation, safe_tx_gas, base_gas))

        # We use fast tx gas price, if not txs could be stuck
        tx_gas_price = self._get_configured_gas_price()
        tx_sender_private_key = self.tx_sender_account.key
        tx_sender_address = Account.from_key(tx_sender_private_key).address

        safe_tx = safe.build_multisig_tx(
            to,
            value,
            data,
            operation,
            safe_tx_gas,
            base_gas,
            gas_price,
            gas_token,
            refund_receiver,
            signatures,
            safe_nonce=safe_nonce,
            safe_version=safe.retrieve_version()
        )

        owners = safe.retrieve_owners()
        signers = safe_tx.signers
        if set(signers) - set(owners):  # All the signers must be owners
            raise InvalidOwners('Signers=%s are not valid owners of the safe. Owners=%s', safe_tx.signers, owners)

        if signers != safe_tx.sorted_signers:
            raise SignaturesNotSorted('Safe-tx-hash=%s - Signatures are not sorted by owner: %s' %
                                      (safe_tx.safe_tx_hash.hex(), safe_tx.signers))

        if banned_signers := BannedSigner.objects.filter(address__in=signers):
            raise SignerIsBanned(f'Signers {list(banned_signers)} are banned')

        logger.info('Safe=%s safe-nonce=%d Check `call()` before sending transaction', safe_address, safe_nonce)
        # Set `gasLimit` for `call()`. It will use the same that it will be used later for execution
        tx_gas = safe_tx.recommended_gas()
        safe_tx.call(tx_sender_address=tx_sender_address, tx_gas=tx_gas, block_identifier=block_identifier)
        with EthereumNonceLock(self.redis, self.ethereum_client, self.tx_sender_account.address,
                               lock_timeout=60 * 2) as tx_nonce:
            logger.info('Safe=%s safe-nonce=%d `call()` was successful', safe_address, safe_nonce)
            tx_hash, tx = safe_tx.execute(tx_sender_private_key, tx_gas=tx_gas, tx_gas_price=tx_gas_price,
                                          tx_nonce=tx_nonce, block_identifier=block_identifier)
            logger.info('Safe=%s, Sent transaction with nonce=%d tx-hash=%s for safe-tx-hash=%s safe-nonce=%d',
                        safe_address, tx_nonce, tx_hash.hex(), safe_tx.safe_tx_hash.hex(), safe_tx.safe_nonce)
            return tx_hash, safe_tx.safe_tx_hash, tx
class TransactionService:
    def __init__(self, gas_station: GasStation,
                 ethereum_client: EthereumClient, redis: Redis,
                 safe_valid_contract_addresses: Set[str],
                 proxy_factory_address: str, tx_sender_private_key: str):
        self.gas_station = gas_station
        self.ethereum_client = ethereum_client
        self.redis = redis
        self.safe_valid_contract_addresses = safe_valid_contract_addresses
        self.proxy_factory = ProxyFactory(proxy_factory_address,
                                          self.ethereum_client)
        self.tx_sender_account = Account.privateKeyToAccount(
            tx_sender_private_key)

    @staticmethod
    def _check_refund_receiver(refund_receiver: str) -> bool:
        """
        We only support tx.origin as refund receiver right now
        In the future we can also accept transactions where it is set to our service account to receive the payments.
        This would prevent that anybody can front-run our service
        """
        return refund_receiver == NULL_ADDRESS

    @staticmethod
    def _is_valid_gas_token(address: Optional[str]) -> float:
        """
        :param address: Token address
        :return: bool if gas token, false otherwise
        """
        address = address or NULL_ADDRESS
        if address == NULL_ADDRESS:
            return True
        try:
            Token.objects.get(address=address, gas=True)
            return True
        except Token.DoesNotExist:
            logger.warning(
                'Cannot retrieve gas token from db: Gas token %s not valid',
                address)
            return False

    def _check_safe_gas_price(self, gas_token: Optional[str],
                              safe_gas_price: int) -> bool:
        """
        Check that `safe_gas_price` is not too low, so that the relay gets a full refund
        for the tx. Gas_price must be always > 0, if not refunding would be disabled
        If a `gas_token` is used we need to calculate the `gas_price` in Eth
        Gas price must be at least >= _current standard gas price_ > 0
        :param gas_token: Address of token is used, `NULL_ADDRESS` or `None` if it's ETH
        :return:
        :exception GasPriceTooLow
        :exception InvalidGasToken
        """
        if safe_gas_price < 1:
            raise RefundMustBeEnabled(
                'Tx internal gas price cannot be 0 or less, it was %d' %
                safe_gas_price)

        minimum_accepted_gas_price = self._get_minimum_gas_price()
        if gas_token and gas_token != NULL_ADDRESS:
            try:
                gas_token_model = Token.objects.get(address=gas_token,
                                                    gas=True)
                estimated_gas_price = gas_token_model.calculate_gas_price(
                    minimum_accepted_gas_price)
                if safe_gas_price < estimated_gas_price:
                    raise GasPriceTooLow(
                        'Required gas-price>=%d to use gas-token' %
                        estimated_gas_price)
                # We use gas station tx gas price. We cannot use internal tx's because is calculated
                # based on the gas token
            except Token.DoesNotExist:
                logger.warning(
                    'Cannot retrieve gas token from db: Gas token %s not valid',
                    gas_token)
                raise InvalidGasToken('Gas token %s not valid' % gas_token)
        else:
            if safe_gas_price < minimum_accepted_gas_price:
                raise GasPriceTooLow('Required gas-price>=%d' %
                                     minimum_accepted_gas_price)
        return True

    def _estimate_tx_gas_price(self, gas_token: Optional[str] = None):
        gas_price_fast = self._get_configured_gas_price()
        if gas_token and gas_token != NULL_ADDRESS:
            try:
                gas_token_model = Token.objects.get(address=gas_token,
                                                    gas=True)
                return gas_token_model.calculate_gas_price(gas_price_fast)
            except Token.DoesNotExist:
                raise InvalidGasToken('Gas token %s not found' % gas_token)
        else:
            return gas_price_fast

    def _get_configured_gas_price(self) -> int:
        """
        :return: Gas price for txs
        """
        return self.gas_station.get_gas_prices().standard

    def _get_minimum_gas_price(self) -> int:
        """
        :return: Minimum gas price accepted for txs set by the user
        """
        return self.gas_station.get_gas_prices().safe_low

    def estimate_tx(
            self, safe_address: str, to: str, value: int, data: str,
            operation: int,
            gas_token: Optional[str]) -> TransactionEstimationWithNonce:
        """
        :return: TransactionEstimation with costs and last used nonce of safe
        :raises: InvalidGasToken: If Gas Token is not valid
        """
        if not self._is_valid_gas_token(gas_token):
            raise InvalidGasToken(gas_token)
        last_used_nonce = SafeMultisigTx.objects.get_last_nonce_for_safe(
            safe_address)
        safe = Safe(safe_address, self.ethereum_client)
        safe_tx_gas = safe.estimate_tx_gas(to, value, data, operation)
        safe_tx_base_gas = safe.estimate_tx_base_gas(to, value, data,
                                                     operation, gas_token,
                                                     safe_tx_gas)

        # For Safe contracts v1.0.0 operational gas is not used (`base_gas` has all the related costs already)
        safe_version = safe.retrieve_version()
        if Version(safe_version) >= Version('1.0.0'):
            safe_tx_operational_gas = 0
        else:
            safe_tx_operational_gas = safe.estimate_tx_operational_gas(
                len(data) if data else 0)

        # Can throw RelayServiceException
        gas_price = self._estimate_tx_gas_price(gas_token)
        return TransactionEstimationWithNonce(safe_tx_gas, safe_tx_base_gas,
                                              safe_tx_base_gas,
                                              safe_tx_operational_gas,
                                              gas_price, gas_token
                                              or NULL_ADDRESS, last_used_nonce)

    def estimate_tx_for_all_tokens(
            self, safe_address: str, to: str, value: int, data: str,
            operation: int) -> TransactionEstimationWithNonceAndGasTokens:
        last_used_nonce = SafeMultisigTx.objects.get_last_nonce_for_safe(
            safe_address)
        safe = Safe(safe_address, self.ethereum_client)
        safe_tx_gas = safe.estimate_tx_gas(to, value, data, operation)

        safe_version = safe.retrieve_version()
        if Version(safe_version) >= Version('1.0.0'):
            safe_tx_operational_gas = 0
        else:
            safe_tx_operational_gas = safe.estimate_tx_operational_gas(
                len(data) if data else 0)

        # Calculate `base_gas` for ether and calculate for tokens using the ether token price
        ether_safe_tx_base_gas = safe.estimate_tx_base_gas(
            to, value, data, operation, NULL_ADDRESS, safe_tx_gas)
        gas_price = self._estimate_tx_gas_price(NULL_ADDRESS)
        gas_token_estimations = [
            TransactionGasTokenEstimation(ether_safe_tx_base_gas, gas_price,
                                          NULL_ADDRESS)
        ]
        token_gas_difference = 50000  # 50K gas more expensive than ether
        for token in Token.objects.gas_tokens():
            try:
                gas_price = self._estimate_tx_gas_price(token.address)
                gas_token_estimations.append(
                    TransactionGasTokenEstimation(
                        ether_safe_tx_base_gas + token_gas_difference,
                        gas_price, token.address))
            except CannotGetTokenPriceFromApi:
                logger.error('Cannot get price for token=%s', token.address)

        return TransactionEstimationWithNonceAndGasTokens(
            last_used_nonce, safe_tx_gas, safe_tx_operational_gas,
            gas_token_estimations)

    def create_multisig_tx(self, safe_address: str, to: str, value: int,
                           data: bytes, operation: int, safe_tx_gas: int,
                           base_gas: int, gas_price: int, gas_token: str,
                           refund_receiver: str, nonce: int,
                           signatures: List[Dict[str, int]]) -> SafeMultisigTx:
        """
        :return: Database model of SafeMultisigTx
        :raises: SafeMultisigTxExists: If Safe Multisig Tx with nonce already exists
        :raises: InvalidGasToken: If Gas Token is not valid
        :raises: TransactionServiceException: If Safe Tx is not valid (not sorted owners, bad signature, bad nonce...)
        """

        safe_contract = SafeContract.objects.get(address=safe_address)
        created = timezone.now()

        if SafeMultisigTx.objects.filter(safe=safe_contract,
                                         nonce=nonce).exists():
            raise SafeMultisigTxExists(
                f'Tx with nonce={nonce} for safe={safe_address} already exists in DB'
            )

        signature_pairs = [(s['v'], s['r'], s['s']) for s in signatures]
        signatures_packed = signatures_to_bytes(signature_pairs)

        try:
            tx_hash, safe_tx_hash, tx = self._send_multisig_tx(
                safe_address, to, value, data, operation, safe_tx_gas,
                base_gas, gas_price, gas_token, refund_receiver, nonce,
                signatures_packed)
        except SafeServiceException as exc:
            raise TransactionServiceException(str(exc)) from exc

        ethereum_tx = EthereumTx.objects.create_from_tx(tx, tx_hash)

        # Fix race conditions for tx being created at the same time
        try:
            return SafeMultisigTx.objects.create(
                created=created,
                safe=safe_contract,
                ethereum_tx=ethereum_tx,
                to=to,
                value=value,
                data=data,
                operation=operation,
                safe_tx_gas=safe_tx_gas,
                data_gas=base_gas,
                gas_price=gas_price,
                gas_token=None if gas_token == NULL_ADDRESS else gas_token,
                refund_receiver=refund_receiver,
                nonce=nonce,
                signatures=signatures_packed,
                safe_tx_hash=safe_tx_hash,
            )
        except IntegrityError as exc:
            raise SafeMultisigTxExists(
                f'Tx with nonce={nonce} for safe={safe_address} already exists in DB'
            ) from exc

    def _send_multisig_tx(
            self,
            safe_address: str,
            to: str,
            value: int,
            data: bytes,
            operation: int,
            safe_tx_gas: int,
            base_gas: int,
            gas_price: int,
            gas_token: str,
            refund_receiver: str,
            safe_nonce: int,
            signatures: bytes,
            tx_gas=None,
            block_identifier='latest') -> Tuple[bytes, bytes, Dict[str, Any]]:
        """
        This function calls the `send_multisig_tx` of the Safe, but has some limitations to prevent abusing
        the relay
        :return: Tuple(tx_hash, safe_tx_hash, tx)
        :raises: InvalidMultisigTx: If user tx cannot go through the Safe
        """

        safe = Safe(safe_address, self.ethereum_client)
        data = data or b''
        gas_token = gas_token or NULL_ADDRESS
        refund_receiver = refund_receiver or NULL_ADDRESS
        to = to or NULL_ADDRESS

        # Make sure refund receiver is set to 0x0 so that the contract refunds the gas costs to tx.origin
        if not self._check_refund_receiver(refund_receiver):
            raise InvalidRefundReceiver(refund_receiver)

        self._check_safe_gas_price(gas_token, gas_price)

        # Make sure proxy contract is ours
        if not self.proxy_factory.check_proxy_code(safe_address):
            raise InvalidProxyContract(safe_address)

        # Make sure master copy is valid
        safe_master_copy_address = safe.retrieve_master_copy_address()
        if safe_master_copy_address not in self.safe_valid_contract_addresses:
            raise InvalidMasterCopyAddress(safe_master_copy_address)

        # Check enough funds to pay for the gas
        if not safe.check_funds_for_tx_gas(safe_tx_gas, base_gas, gas_price,
                                           gas_token):
            raise NotEnoughFundsForMultisigTx

        threshold = safe.retrieve_threshold()
        number_signatures = len(signatures) // 65  # One signature = 65 bytes
        if number_signatures < threshold:
            raise SignaturesNotFound('Need at least %d signatures' % threshold)

        safe_tx_gas_estimation = safe.estimate_tx_gas(to, value, data,
                                                      operation)
        safe_base_gas_estimation = safe.estimate_tx_base_gas(
            to, value, data, operation, gas_token, safe_tx_gas_estimation)
        if safe_tx_gas < safe_tx_gas_estimation or base_gas < safe_base_gas_estimation:
            raise InvalidGasEstimation(
                "Gas should be at least equal to safe-tx-gas=%d and data-gas=%d. Current is "
                "safe-tx-gas=%d and data-gas=%d" %
                (safe_tx_gas_estimation, safe_base_gas_estimation, safe_tx_gas,
                 base_gas))

        # We use fast tx gas price, if not txs could be stuck
        tx_gas_price = self._get_configured_gas_price()
        tx_sender_private_key = self.tx_sender_account.privateKey
        tx_sender_address = Account.privateKeyToAccount(
            tx_sender_private_key).address

        safe_tx = safe.build_multisig_tx(to,
                                         value,
                                         data,
                                         operation,
                                         safe_tx_gas,
                                         base_gas,
                                         gas_price,
                                         gas_token,
                                         refund_receiver,
                                         signatures,
                                         safe_nonce=safe_nonce,
                                         safe_version=safe.retrieve_version())

        if safe_tx.signers != safe_tx.sorted_signers:
            raise SignaturesNotSorted(
                'Safe-tx-hash=%s - Signatures are not sorted by owner: %s' %
                (safe_tx.safe_tx_hash.hex(), safe_tx.signers))

        safe_tx.call(tx_sender_address=tx_sender_address,
                     block_identifier=block_identifier)

        with EthereumNonceLock(self.redis,
                               self.ethereum_client,
                               self.tx_sender_account.address,
                               timeout=60 * 2) as tx_nonce:
            tx_hash, tx = safe_tx.execute(tx_sender_private_key,
                                          tx_gas=tx_gas,
                                          tx_gas_price=tx_gas_price,
                                          tx_nonce=tx_nonce,
                                          block_identifier=block_identifier)
            return tx_hash, safe_tx.tx_hash, tx

    def get_pending_multisig_transactions(
            self, older_than: int) -> List[SafeMultisigTx]:
        """
        Get multisig txs that have not been mined after X seconds
        :param older_than: Time in seconds for a tx to be considered pending, if 0 all will be returned
        """
        return SafeMultisigTx.objects.filter(
            Q(ethereum_tx__block=None) | Q(ethereum_tx=None)).filter(
                created__lte=timezone.now() - timedelta(seconds=older_than), )