Exemple #1
0
    def handle(self, *args, **options):
        for task in TASKS:
            _, created = task.create_task()
            if created:
                self.stdout.write(
                    self.style.SUCCESS('Created Periodic Task %s' % task.name))
            else:
                self.stdout.write(
                    self.style.SUCCESS('Task %s was already created' %
                                       task.name))

        self.stdout.write(
            self.style.SUCCESS('Setting up Safe Contract Addresses'))
        ethereum_client = EthereumClientProvider()
        ethereum_network = ethereum_client.get_network()
        if ethereum_network in MASTER_COPIES:
            self.stdout.write(
                self.style.SUCCESS(
                    f'Setting up {ethereum_network.name} safe addresses'))
            self._setup_safe_master_copies(MASTER_COPIES[ethereum_network])
        if ethereum_network in PROXY_FACTORIES:
            self.stdout.write(
                self.style.SUCCESS(
                    f'Setting up {ethereum_network.name} proxy factory addresses'
                ))
            self._setup_safe_proxy_factories(PROXY_FACTORIES[ethereum_network])

        if not (ethereum_network in MASTER_COPIES
                and ethereum_network in PROXY_FACTORIES):
            self.stdout.write(
                self.style.WARNING('Cannot detect a valid ethereum-network'))
    def handle(self, *args, **options):
        ethereum_client = EthereumClientProvider()
        proxy_factory_address = settings.SAFE_PROXY_FACTORY_ADDRESS
        deployer_key = options['deployer_key']
        deployer_account = Account.privateKeyToAccount(
            deployer_key) if deployer_key else self.DEFAULT_ACCOUNT

        self.stdout.write(
            self.style.SUCCESS(
                'Checking if proxy factory was already deployed on %s' %
                proxy_factory_address))
        if ethereum_client.is_contract(proxy_factory_address):
            self.stdout.write(
                self.style.NOTICE('Proxy factory was already deployed on %s' %
                                  proxy_factory_address))
        else:
            self.stdout.write(
                self.style.SUCCESS(
                    'Deploying proxy factory using deployer account, '
                    'proxy factory %s not found' % proxy_factory_address))
            proxy_factory_address = ProxyFactory.deploy_proxy_factory_contract(
                ethereum_client,
                deployer_account=deployer_account).contract_address
            self.stdout.write(
                self.style.SUCCESS('Proxy factory has been deployed on %s' %
                                   proxy_factory_address))
def fix_ethereum_logs(apps, schema_editor):
    EthereumTx = apps.get_model('history', 'EthereumTx')
    ethereum_client = EthereumClientProvider()

    # We need to add `address` to the logs, so we exclude empty logs and logs already containing `address`
    queryset = EthereumTx.objects.exclude(logs__0__has_key='address').exclude(
        logs=[])
    total = queryset.count()
    processed = 200
    logger.info('Fixing ethereum logs. %d remaining to be fixed', total)
    while True:
        ethereum_txs = queryset[:processed]
        if not ethereum_txs:
            break

        tx_hashes = [ethereum_tx.tx_hash for ethereum_tx in ethereum_txs]
        try:
            tx_receipts = ethereum_client.get_transaction_receipts(tx_hashes)
            for ethereum_tx, tx_receipt in zip(ethereum_txs, tx_receipts):
                ethereum_tx.logs = [
                    clean_receipt_log(log) for log in tx_receipt['logs']
                ]
                ethereum_tx.save(update_fields=['logs'])
                total -= 1

            logger.info('Fixed %d ethereum logs. %d remaining to be fixed',
                        processed, total)
        except IOError:
            logger.warning('Node connection error when retrieving tx receipts')
    def handle(self, *args, **options):
        fix = options['fix']

        queryset = SafeStatus.objects.last_for_every_address()
        count = queryset.count()
        batch = 200
        ethereum_client = EthereumClientProvider()
        index_service = IndexServiceProvider()

        for i in range(0, count, batch):
            self.stdout.write(self.style.SUCCESS(f'Processed {i}/{count}'))
            safe_statuses = queryset[i:i + batch]
            addresses = []
            nonces = []
            for result in safe_statuses.values('address', 'nonce'):
                addresses.append(result['address'])
                nonces.append(result['nonce'])

            blockchain_nonce_payloads = self.build_nonce_payload(addresses)
            blockchain_nonces = ethereum_client.batch_call_custom(blockchain_nonce_payloads)

            addresses_to_reindex = []
            for address, nonce, blockchain_nonce in zip(addresses, nonces, blockchain_nonces):
                if nonce != blockchain_nonce:
                    self.stdout.write(self.style.WARNING(f'Safe={address} stored nonce={nonce} is '
                                                         f'different from blockchain-nonce={blockchain_nonce}'))
                    addresses_to_reindex.append(address)

            if fix and addresses_to_reindex:
                self.stdout.write(self.style.SUCCESS(f'Fixing Safes={addresses_to_reindex}'))
                index_service.reindex_addresses(addresses_to_reindex)
    def handle(self, *args, **options):
        for task in self.tasks:
            _, created = task.create_task()
            if created:
                self.stdout.write(self.style.SUCCESS('Created Periodic Task %s' % task.name))
            else:
                self.stdout.write(self.style.SUCCESS('Task %s was already created' % task.name))

        self.stdout.write(self.style.SUCCESS('Setting up Safe Contract Addresses'))
        ethereum_client = EthereumClientProvider()
        ethereum_network = ethereum_client.get_network()
        self.stdout.write(self.style.SUCCESS('Network %s was identified' % ethereum_network))
        if ethereum_network == EthereumNetwork.MAINNET:
            self.stdout.write(self.style.SUCCESS('Setting up Mainnet addresses'))
            self.setup_mainnet()
        elif ethereum_network == EthereumNetwork.RINKEBY:
            self.stdout.write(self.style.SUCCESS('Setting up Rinkeby addresses'))
            self.setup_rinkeby()
        elif ethereum_network == EthereumNetwork.GOERLI:
            self.stdout.write(self.style.SUCCESS('Setting up Goerli addresses'))
            self.setup_goerli()
        elif ethereum_network == EthereumNetwork.KOVAN:
            self.stdout.write(self.style.SUCCESS('Setting up Kovan addresses'))
            self.setup_kovan()
        elif ethereum_network == EthereumNetwork.UNKNOWN:
            self.stdout.write(self.style.SUCCESS('Setting up RSK addresses'))
            self.setup_rsk()
        else:
            self.stdout.write(self.style.WARNING(f'Cannot detect a valid ethereum-network'))
Exemple #6
0
def check_create2_deployed_safes_task() -> None:
    """
    Check if create2 safes were deployed and store the `blockNumber` if there are enough confirmations
    """
    try:
        redis = RedisRepository().redis
        with redis.lock('tasks:check_create2_deployed_safes_task', blocking_timeout=1, timeout=LOCK_TIMEOUT):
            ethereum_client = EthereumClientProvider()
            confirmations = 6
            current_block_number = ethereum_client.current_block_number
            for safe_creation2 in SafeCreation2.objects.pending_to_check():
                tx_receipt = ethereum_client.get_transaction_receipt(safe_creation2.tx_hash)
                safe_address = safe_creation2.safe.address
                if tx_receipt:
                    block_number = tx_receipt.blockNumber
                    if (current_block_number - block_number) >= confirmations:
                        logger.info('Safe=%s with tx-hash=%s was confirmed in block-number=%d',
                                    safe_address, safe_creation2.tx_hash, block_number)
                        send_create_notification.delay(safe_address, safe_creation2.owners)
                        safe_creation2.block_number = block_number
                        safe_creation2.save()
                else:
                    # If safe was not included in any block after 35 minutes (mempool limit is 30), we try to deploy it again
                    if safe_creation2.modified + timedelta(minutes=35) < timezone.now():
                        logger.info('Safe=%s with tx-hash=%s was not deployed after 10 minutes',
                                    safe_address, safe_creation2.tx_hash)
                        safe_creation2.tx_hash = None
                        safe_creation2.save()
                        deploy_create2_safe_task.delay(safe_address, retry=False)

            for safe_creation2 in SafeCreation2.objects.not_deployed():
                deploy_create2_safe_task.delay(safe_creation2.safe.address, retry=False)
    except LockError:
        pass
Exemple #7
0
    def handle(self, *args, **options):
        ethereum_client = EthereumClientProvider()
        app_name = apps.get_app_config('history').verbose_name
        network_name = ethereum_client.get_network().name.capitalize()
        startup_message = f'Starting {app_name} version {__version__} on {network_name}'
        self.stdout.write(self.style.SUCCESS(startup_message))

        if settings.SLACK_API_WEBHOOK:
            try:
                r = requests.post(settings.SLACK_API_WEBHOOK,
                                  json={'text': startup_message})
                if r.ok:
                    self.stdout.write(
                        self.style.SUCCESS(
                            f'Slack configured, "{startup_message}" sent'))
                else:
                    raise RequestException()
            except RequestException as e:
                self.stdout.write(
                    self.style.ERROR(
                        f'Cannot send slack notification to webhook '
                        f'({settings.SLACK_API_WEBHOOK}): "{e}"'))
        else:
            self.stdout.write(
                self.style.SUCCESS('Slack not configured, ignoring'))
Exemple #8
0
    def handle(self, *args, **options):
        ethereum_client = EthereumClientProvider()
        total = EthereumTx.objects.filter(logs=None).count()
        processed = 200
        self.stdout.write(
            self.style.SUCCESS(
                f'Fixing ethereum logs. {total} remaining to be fixed'))
        while True:
            ethereum_txs = EthereumTx.objects.filter(logs=None)[:processed]
            if not ethereum_txs:
                break

            tx_hashes = [ethereum_tx.tx_hash for ethereum_tx in ethereum_txs]
            tx_receipts = ethereum_client.get_transaction_receipts(tx_hashes)
            for ethereum_tx, tx_receipt in zip(ethereum_txs, tx_receipts):
                ethereum_tx.logs = [
                    clean_receipt_log(log) for log in tx_receipt['logs']
                ]
                ethereum_tx.save(update_fields=['logs'])
                total -= 1

            self.stdout.write(
                self.style.SUCCESS(
                    f'Fixed {processed} ethereum logs. {total} remaining to be fixed'
                ))
def add_status_and_index_to_txs(apps, schema_editor):
    EthereumTx = apps.get_model("relay", "EthereumTx")
    ethereum_client = EthereumClientProvider()
    for ethereum_tx in EthereumTx.objects.filter(status=None):
        tx_receipt = ethereum_client.get_transaction_receipt(ethereum_tx.tx_hash)
        if tx_receipt:
            ethereum_tx.status = tx_receipt.get("status")
            ethereum_tx.transaction_index = tx_receipt["transactionIndex"]
            ethereum_tx.save(update_fields=["status", "transaction_index"])
def add_status_and_index_to_txs(apps, schema_editor):
    EthereumTx = apps.get_model('relay', 'EthereumTx')
    ethereum_client = EthereumClientProvider()
    for ethereum_tx in EthereumTx.objects.filter(status=None):
        tx_receipt = ethereum_client.get_transaction_receipt(ethereum_tx.tx_hash)
        if tx_receipt:
            ethereum_tx.status = tx_receipt.get('status')
            ethereum_tx.transaction_index = tx_receipt['transactionIndex']
            ethereum_tx.save(update_fields=['status', 'transaction_index'])
 def get_or_create_from_block_number(self, block_number: int):
     try:
         return self.get(number=block_number)
     except self.model.DoesNotExist:
         ethereum_client = EthereumClientProvider()
         current_block_number = ethereum_client.current_block_number  # For reorgs
         block = ethereum_client.get_block(block_number)
         return self.create_from_block(
             block, current_block_number=current_block_number)
    def handle(self, *args, **options):
        fix = options['fix']

        queryset = SafeStatus.objects.last_for_every_address()
        count = queryset.count()
        batch = 100
        ethereum_client = EthereumClientProvider()
        index_service = IndexServiceProvider()

        for i in range(0, count, batch):
            self.stdout.write(self.style.SUCCESS(f'Processed {i}/{count}'))
            safe_statuses = queryset[i:i + batch]
            safe_statuses_list = list(
                safe_statuses)  # Force retrieve queryset from DB
            blockchain_nonce_payloads = self.build_nonce_payload(
                [safe_status.address for safe_status in safe_statuses_list])
            blockchain_nonces = ethereum_client.batch_call_custom(
                blockchain_nonce_payloads, raise_exception=False)

            addresses_to_reindex = set()
            for safe_status, blockchain_nonce in zip(safe_statuses_list,
                                                     blockchain_nonces):
                address = safe_status.address
                nonce = safe_status.nonce
                if safe_status.is_corrupted():
                    self.stdout.write(
                        self.style.WARNING(
                            f'Safe={address} is corrupted, has some old '
                            f'transactions missing'))
                    addresses_to_reindex.add(address)

                if blockchain_nonce is None:
                    self.stdout.write(
                        self.style.WARNING(
                            f'Safe={address} looks problematic, '
                            f'cannot retrieve blockchain-nonce'))
                if nonce != blockchain_nonce:
                    self.stdout.write(
                        self.style.WARNING(
                            f'Safe={address} stored nonce={nonce} is '
                            f'different from blockchain-nonce={blockchain_nonce}'
                        ))
                    if last_valid_transaction := MultisigTransaction.objects.last_valid_transaction(
                            address):
                        self.stdout.write(
                            self.style.WARNING(
                                f'Last valid transaction for Safe={address} has safe-nonce={last_valid_transaction.nonce} '
                                f'safe-transaction-hash={last_valid_transaction.safe_tx_hash} and '
                                f'ethereum-tx-hash={last_valid_transaction.ethereum_tx_id}'
                            ))
                    addresses_to_reindex.add(address)

            if fix and addresses_to_reindex:
                self.stdout.write(
                    self.style.SUCCESS(f'Fixing Safes={addresses_to_reindex}'))
                index_service.reindex_addresses(addresses_to_reindex)
Exemple #13
0
def check_deployer_funded_task(self, safe_address: str, retry: bool = True) -> None:
    """
    Check the `deployer_funded_tx_hash`. If receipt can be retrieved, in SafeFunding `deployer_funded=True`.
    If not, after the number of retries `deployer_funded_tx_hash=None`
    :param safe_address: safe account
    :param retry: if True, retries are allowed, otherwise don't retry
    """
    try:
        redis = RedisRepository().redis
        with redis.lock(f"tasks:check_deployer_funded_task:{safe_address}", blocking_timeout=1, timeout=LOCK_TIMEOUT):
            ethereum_client = EthereumClientProvider()
            logger.debug('Starting check deployer funded task for safe=%s', safe_address)
            safe_funding = SafeFunding.objects.get(safe=safe_address)
            deployer_funded_tx_hash = safe_funding.deployer_funded_tx_hash

            if safe_funding.deployer_funded:
                logger.warning('Tx-hash=%s for safe %s is already checked', deployer_funded_tx_hash, safe_address)
                return
            elif not deployer_funded_tx_hash:
                logger.error('No deployer_funded_tx_hash for safe=%s', safe_address)
                return

            logger.debug('Checking safe=%s deployer tx-hash=%s', safe_address, deployer_funded_tx_hash)
            if ethereum_client.get_transaction_receipt(deployer_funded_tx_hash):
                logger.info('Found transaction to deployer of safe=%s with receipt=%s', safe_address,
                            deployer_funded_tx_hash)
                safe_funding.deployer_funded = True
                safe_funding.save()
            else:
                logger.debug('Not found transaction receipt for tx-hash=%s', deployer_funded_tx_hash)
                # If no more retries
                if not retry or (self.request.retries == self.max_retries):
                    safe_creation = SafeCreation.objects.get(safe=safe_address)
                    balance = ethereum_client.get_balance(safe_creation.deployer)
                    if balance >= safe_creation.wei_deploy_cost():
                        logger.warning('Safe=%s. Deployer=%s. Cannot find transaction receipt with tx-hash=%s, '
                                       'but balance is there. This should never happen',
                                       safe_address, safe_creation.deployer, deployer_funded_tx_hash)
                        safe_funding.deployer_funded = True
                        safe_funding.save()
                    else:
                        logger.error('Safe=%s. Deployer=%s. Transaction receipt with tx-hash=%s not mined after %d '
                                     'retries. Setting `deployer_funded_tx_hash` back to `None`',
                                     safe_address,
                                     safe_creation.deployer,
                                     deployer_funded_tx_hash,
                                     self.request.retries)
                        safe_funding.deployer_funded_tx_hash = None
                        safe_funding.save()
                else:
                    logger.debug('Retry finding transaction receipt %s', deployer_funded_tx_hash)
                    if retry:
                        raise self.retry(countdown=self.request.retries * 10 + 15)  # More countdown every retry
    except LockError:
        logger.info('check_deployer_funded_task is locked for safe=%s', safe_address)
def fix_uniswap_pool_tokens_task() -> Optional[int]:
    ethereum_client = EthereumClientProvider()
    ethereum_network = ethereum_client.get_network()
    if ethereum_network == EthereumNetwork.MAINNET:
        try:
            number = Token.objects.fix_uniswap_pool_tokens()
            if number:
                logger.info('%d uniswap pool token names were fixed', number)
            return number
        finally:
            close_gevent_db_connection()
Exemple #15
0
def circles_onboarding_organization_signup_task(safe_address: str) -> None:
    """
    Check if Organization Safe is already registered in the Hub, if not, fund it
    :param safe_address: Address of the created safe
    """

    assert check_checksum(safe_address)

    # Additional funds for organization deployments (it should at least cover
    # one `trust` method call) next to the `organizationSignup` method
    ADDITIONAL_START_FUNDS = 100000000000000

    try:
        redis = RedisRepository().redis
        lock_name = f'locks:circles_onboarding_organization_signup_task:{safe_address}'
        with redis.lock(lock_name, blocking_timeout=1, timeout=LOCK_TIMEOUT):
            logger.info(
                'Fund organizationSignup task for {}'.format(safe_address))

            ethereum_client = EthereumClientProvider()

            # Do nothing if account already exists in Hub
            if CirclesService(ethereum_client).is_organization_deployed(
                    safe_address):
                logger.info('Organization is already deployed for {}'.format(
                    safe_address))
                return

            # Do nothing if the signup is already funded
            transaction_service = TransactionServiceProvider()

            # Sum `organizationSignup` and additional `trust` transactions
            # costs as the organization needs to trust at least one user in the
            # beginning to receive more funds
            payment = transaction_service.estimate_circles_organization_signup_tx(
                safe_address) + ADDITIONAL_START_FUNDS
            safe_balance = ethereum_client.get_balance(safe_address)
            logger.info(
                'Found %d balance for organization deployment of safe=%s. Required=%d',
                safe_balance, safe_address, payment)
            if safe_balance >= payment:
                logger.info(
                    'Organization is already funded {}'.format(safe_address))
                return

            # Otherwise fund deployment
            logger.info('Fund Organization {}'.format(safe_address))
            FundingServiceProvider().send_eth_to(safe_address,
                                                 payment - safe_balance,
                                                 gas=30000,
                                                 retry=True)
    except LockError:
        pass
def fix_pool_tokens_task() -> Optional[int]:
    """
    Fix names for generic pool tokens, like Balancer or Uniswap
    :return: Number of pool token names updated
    """
    ethereum_client = EthereumClientProvider()
    ethereum_network = ethereum_client.get_network()
    if ethereum_network == EthereumNetwork.MAINNET:
        try:
            number = Token.pool_tokens.fix_all_pool_tokens()
            if number:
                logger.info('%d pool token names were fixed', number)
            return number
        finally:
            close_gevent_db_connection()
Exemple #17
0
    def handle(self, *args, **options):
        tokens = options['tokens']
        no_prompt = options['no_prompt']
        ethereum_client = EthereumClientProvider()

        for token_address in tokens:
            token_address = ethereum_client.w3.toChecksumAddress(token_address)
            try:
                token = Token.objects.get(address=token_address)
                self.stdout.write(
                    self.style.SUCCESS(
                        f'Token {token.name} - {token.symbol} with address '
                        f'{token_address} already exists'))
                continue
            except Token.DoesNotExist:
                pass
            info = ethereum_client.erc20.get_info(token_address)
            if no_prompt:
                response = 'y'
            else:
                response = input(f'Do you want to create a token {info} (y/n) '
                                 ).strip().lower()
            if response == 'y':
                Token.objects.create(address=token_address,
                                     name=info.name,
                                     symbol=info.symbol,
                                     decimals=info.decimals)
                self.stdout.write(
                    self.style.SUCCESS(f'Created token {info.name}'))
    def validate(self, data):
        super().validate(data)

        safe_address = data["safe"]
        if not SafeContract.objects.filter(address=safe_address).exists():
            raise ValidationError(
                f"Safe={safe_address} does not exist or it's still not indexed"
            )

        signature = data["signature"]
        delegate = data["delegate"]  # Delegate address to be added/removed

        ethereum_client = EthereumClientProvider()
        valid_delegators = self.get_valid_delegators(ethereum_client,
                                                     safe_address, delegate)

        # Tries to find a valid delegator using multiple strategies
        for operation_hash in DelegateSignatureHelper.calculate_all_possible_hashes(
                delegate):
            delegator = self.check_signature(
                ethereum_client,
                safe_address,
                signature,
                operation_hash,
                valid_delegators,
            )
            if delegator:
                break

        if not delegator:
            raise ValidationError("Signing owner is not an owner of the Safe")

        data["delegator"] = delegator
        return data
    def handle(self, *args, **options):
        tokens = options['tokens']
        no_prompt = options['no_prompt']
        ethereum_client = EthereumClientProvider()

        for token_address in tokens:
            token_address = ethereum_client.w3.toChecksumAddress(token_address)
            try:
                token = Token.objects.get(address=token_address)
                self.stdout.write(self.style.SUCCESS(f'Token {token.name} - {token.symbol} with address '
                                                     f'{token_address} already exists'))
                if not token.trusted:  # Mark token as trusted if it's not
                    token.set_trusted()
                    self.stdout.write(self.style.SUCCESS(f'Marking token {token_address} as trusted'))
                continue
            except Token.DoesNotExist:
                pass

            try:
                info = ethereum_client.erc20.get_info(token_address)
                decimals = info.decimals
            except InvalidERC20Info:  # Try with a ERC721
                info = ethereum_client.erc721.get_info(token_address)
                self.stdout.write(self.style.SUCCESS('Detected ERC721 token'))
                decimals = 0

            if no_prompt:
                response = 'y'
            else:
                response = input(f'Do you want to create a token {info} (y/n) ').strip().lower()
            if response == 'y':
                Token.objects.create(address=token_address, name=info.name, symbol=info.symbol, decimals=decimals,
                                     trusted=True)
                self.stdout.write(self.style.SUCCESS(f'Created token {info.name} on address {token_address}'))
    def __new__(cls):
        if not hasattr(cls, "instance"):
            from django.conf import settings

            cls.instance = ReorgService(EthereumClientProvider(),
                                        settings.ETH_REORG_BLOCKS)
        return cls.instance
    def create_from_blockchain(
            self, token_address: ChecksumAddress) -> Optional['Token']:
        ethereum_client = EthereumClientProvider()
        if token_address in ENS_CONTRACTS_WITH_TLD:  # Special case for ENS
            return self.create(address=token_address,
                               name='Ethereum Name Service',
                               symbol='ENS',
                               logo_uri='ENS.png',
                               decimals=None,
                               trusted=True)
        try:
            logger.debug('Querying blockchain for info for erc20 token=%s',
                         token_address)
            erc_info = ethereum_client.erc20.get_info(token_address)
            decimals = erc_info.decimals
        except InvalidERC20Info:
            logger.debug(
                'Erc20 token not found, querying blockchain for info for erc721 token=%s',
                token_address)
            try:
                erc_info = ethereum_client.erc721.get_info(token_address)
                decimals = None
            except InvalidERC721Info:
                logger.debug('Cannot find anything on blockchain for token=%s',
                             token_address)
                return None

        # If symbol is way bigger than name (by 5 characters), swap them (e.g. POAP)
        name, symbol = erc_info.name, erc_info.symbol
        if (len(name) - len(symbol)) < -5:
            name, symbol = symbol, name
        return self.create(address=token_address,
                           name=name,
                           symbol=symbol,
                           decimals=decimals)
 def fix_balancer_pool_tokens(self) -> int:
     """
     All Uniswap V2 tokens have the same name: "Uniswap V2". This method will return better names
     :return: Number of pool tokens fixed
     """
     zerion_client = BalancerTokenAdapterClient(EthereumClientProvider())
     return self._fix_pool_tokens('Balancer Pool Token', zerion_client)
    def validate(self, data):
        super().validate(data)

        if not SafeContract.objects.filter(address=data['safe']).exists():
            raise ValidationError(f"Safe={data['safe']} does not exist or it's still not indexed")

        ethereum_client = EthereumClientProvider()
        safe = Safe(data['safe'], ethereum_client)

        # Check owners and pending owners
        try:
            safe_owners = safe.retrieve_owners(block_identifier='pending')
        except BadFunctionCallOutput:  # Error using pending block identifier
            safe_owners = safe.retrieve_owners(block_identifier='latest')

        signature = data['signature']
        delegate = data['delegate']  # Delegate address to be added

        # Tries to find a valid delegator using multiple strategies
        for operation_hash in (DelegateSignatureHelper.calculate_hash(delegate),
                               DelegateSignatureHelper.calculate_hash(delegate, eth_sign=True),
                               DelegateSignatureHelper.calculate_hash(delegate, previous_topt=True),
                               DelegateSignatureHelper.calculate_hash(delegate, eth_sign=True, previous_topt=True)):
            delegator = self.check_signature(signature, operation_hash, safe_owners)
            if delegator:
                break

        if not delegator:
            raise ValidationError('Signing owner is not an owner of the Safe')

        data['delegator'] = delegator
        return data
Exemple #24
0
    def reindex_master_copies(
        self,
        from_block_number: int,
        to_block_number: Optional[int] = None,
        block_process_limit: int = 100,
        addresses: Optional[ChecksumAddress] = None,
    ):
        """
        Reindexes master copies in parallel with the current running indexer, so service will have no missing txs
        while reindexing

        :param from_block_number: Block number to start indexing from
        :param to_block_number: Block number to stop indexing on
        :param block_process_limit: Number of blocks to process each time
        :param addresses: Master Copy or Safes(for L2 event processing) addresses. If not provided,
            all master copies will be used
        """
        assert (not to_block_number) or to_block_number > from_block_number

        from ..indexers import (
            EthereumIndexer,
            InternalTxIndexerProvider,
            SafeEventsIndexerProvider,
        )

        indexer_provider = (SafeEventsIndexerProvider if self.eth_l2_network
                            else InternalTxIndexerProvider)
        indexer: EthereumIndexer = indexer_provider()
        ethereum_client = EthereumClientProvider()

        if addresses:
            indexer.IGNORE_ADDRESSES_ON_LOG_FILTER = (
                False  # Just process addresses provided
            )
        else:
            addresses = list(
                indexer.database_queryset.values_list("address", flat=True))

        if not addresses:
            logger.warning("No addresses to process")
        else:
            logger.info("Start reindexing Safe Master Copy addresses %s",
                        addresses)
            current_block_number = ethereum_client.current_block_number
            stop_block_number = (min(current_block_number, to_block_number)
                                 if to_block_number else current_block_number)
            block_number = from_block_number
            while block_number < stop_block_number:
                elements = indexer.find_relevant_elements(
                    addresses, block_number,
                    block_number + block_process_limit)
                indexer.process_elements(elements)
                block_number += block_process_limit
                logger.info(
                    "Current block number %d, found %d traces/events",
                    block_number,
                    len(elements),
                )

            logger.info("End reindexing addresses %s", addresses)
Exemple #25
0
 def save(self, **kwargs):
     safe_address = self.context['safe_address']
     ethereum_client = EthereumClientProvider()
     safe = Safe(safe_address, ethereum_client)
     safe_tx_gas = safe.estimate_tx_gas(self.validated_data['to'], self.validated_data['value'],
                                        self.validated_data['data'], self.validated_data['operation'])
     return {'safe_tx_gas': safe_tx_gas}
    def validate(self, data):
        super().validate(data)

        safe_address: Optional[ChecksumAddress] = data.get("safe")
        if (safe_address and not SafeContract.objects.filter(
                address=safe_address).exists()):
            raise ValidationError(
                f"Safe={safe_address} does not exist or it's still not indexed"
            )

        signature = data["signature"]
        delegate = data["delegate"]  # Delegate address to be added/removed
        delegator = data[
            "delegator"]  # Delegator giving permissions to delegate (signer)

        ethereum_client = EthereumClientProvider()
        if safe_address:
            # Valid delegators must be owners
            valid_delegators = self.get_safe_owners(ethereum_client,
                                                    safe_address)
            if delegator not in valid_delegators:
                raise ValidationError(
                    f"Provided delegator={delegator} is not an owner of Safe={safe_address}"
                )

        # Tries to find a valid delegator using multiple strategies
        for operation_hash in DelegateSignatureHelper.calculate_all_possible_hashes(
                delegate):
            if self.check_delegate_signature(ethereum_client, signature,
                                             operation_hash, delegator):
                return data

        raise ValidationError(
            f"Signature does not match provided delegator={delegator}")
Exemple #27
0
    def validate_signature(self, signature: bytes):
        safe_tx_hash = self.context['safe_tx_hash']
        try:
            multisig_transaction = MultisigTransaction.objects.select_related(
                'ethereum_tx'
            ).get(safe_tx_hash=safe_tx_hash)
        except MultisigTransaction.DoesNotExist:
            raise NotFound(f'Multisig transaction with safe-tx-hash={safe_tx_hash} was not found')

        safe_address = multisig_transaction.safe
        if multisig_transaction.executed:
            raise ValidationError(f'Transaction with safe-tx-hash={safe_tx_hash} was already executed')

        ethereum_client = EthereumClientProvider()
        safe = Safe(safe_address, ethereum_client)
        try:
            safe_owners = safe.retrieve_owners(block_identifier='pending')
        except BadFunctionCallOutput:  # Error using pending block identifier
            safe_owners = safe.retrieve_owners(block_identifier='latest')

        parsed_signatures = SafeSignature.parse_signature(signature, safe_tx_hash)
        signature_owners = []
        for safe_signature in parsed_signatures:
            owner = safe_signature.owner
            if owner not in safe_owners:
                raise ValidationError(f'Signer={owner} is not an owner. Current owners={safe_owners}')
            if not safe_signature.is_valid(ethereum_client, safe.address):
                raise ValidationError(f'Signature={safe_signature.signature.hex()} for owner={owner} is not valid')
            if owner in signature_owners:
                raise ValidationError(f'Signature for owner={owner} is duplicated')

            signature_owners.append(owner)
        return signature
 def queryset(self, request, queryset):
     current_block_number = EthereumClientProvider().current_block_number
     condition = {'erc20_block_number__gte': current_block_number - 200}
     if self.value() == 'YES':
         return queryset.filter(**condition)
     elif self.value() == 'NO':
         return queryset.exclude(**condition)
Exemple #29
0
 def __new__(cls):
     if not hasattr(cls, 'instance'):
         from django.conf import settings
         cls.instance = BalanceService(EthereumClientProvider(),
                                       settings.ETH_UNISWAP_FACTORY_ADDRESS,
                                       settings.ETH_KYBER_NETWORK_PROXY_ADDRESS)
     return cls.instance
Exemple #30
0
def check_balance_of_accounts_task() -> bool:
    """
    Checks if balance of relayer accounts (tx sender, safe funder) are less than the configured threshold
    :return: True if every account have enough ether, False otherwise
    """
    balance_warning_wei = settings.SAFE_ACCOUNTS_BALANCE_WARNING
    addresses = FundingServiceProvider().funder_account.address, TransactionServiceProvider().tx_sender_account.address

    ethereum_client = EthereumClientProvider()
    result = True
    for address in addresses:
        balance_wei = ethereum_client.get_balance(address)
        if balance_wei <= balance_warning_wei:
            logger.error('Relayer account=%s current balance=%d . Balance must be greater than %d',
                         address, balance_wei, balance_warning_wei)
            result = False
    return result