Ejemplo n.º 1
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)
Ejemplo n.º 2
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
Ejemplo n.º 3
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
 def handle(self, *args, **options):
     ethereum_client = EthereumClientProvider()
     mismatchs = 0
     for safe_contract in SafeContract.objects.deployed():
         blockchain_balance = ethereum_client.get_balance(
             safe_contract.address)
         internal_tx_balance = safe_contract.get_balance()
         if blockchain_balance != internal_tx_balance:
             mismatchs += 1
             self.stdout.write(
                 self.style.NOTICE(
                     f"safe={safe_contract.address} "
                     f"blockchain-balance={blockchain_balance} does not match "
                     f"internal-tx-balance={internal_tx_balance}"))
     if mismatchs:
         self.stdout.write(
             self.style.NOTICE(
                 f"{mismatchs} Safes don't match blockchain balance"))
     else:
         self.stdout.write(
             self.style.SUCCESS("All Safes match blockchain balance"))
Ejemplo n.º 5
0
def fund_deployer_task(self, safe_address: str, retry: bool = True) -> None:
    """
    Check if user has sent enough ether or tokens to the safe account
    If every condition is met ether is sent to the deployer address and `check_deployer_funded_task`
    is called to check that that tx is mined
    If everything goes well in SafeFunding `safe_funded=True` and `deployer_funded_tx_hash=tx_hash` are set
    :param safe_address: safe account
    :param retry: if True, retries are allowed, otherwise don't retry
    """

    safe_contract = SafeContract.objects.get(address=safe_address)
    try:
        safe_creation = SafeCreation.objects.get(safe=safe_address)
    except SafeCreation.DoesNotExist:
        deploy_create2_safe_task.delay(safe_address)
        return

    deployer_address = safe_creation.deployer
    payment = safe_creation.payment

    # These asserts just to make sure we are not wasting money
    assert check_checksum(safe_address)
    assert check_checksum(deployer_address)
    assert checksum_encode(mk_contract_address(sender=deployer_address, nonce=0)) == safe_address
    assert payment > 0

    redis = RedisRepository().redis
    with redis.lock('locks:fund_deployer_task', timeout=LOCK_TIMEOUT):
        ethereum_client = EthereumClientProvider()
        safe_funding, _ = SafeFunding.objects.get_or_create(safe=safe_contract)

        # Nothing to do if everything is funded and mined
        if safe_funding.is_all_funded():
            logger.debug('Nothing to do here for safe %s. Is all funded', safe_address)
            return

        # If receipt exists already, let's check
        if safe_funding.deployer_funded_tx_hash and not safe_funding.deployer_funded:
            logger.debug('Safe %s deployer has already been funded. Checking tx_hash %s',
                         safe_address,
                         safe_funding.deployer_funded_tx_hash)
            check_deployer_funded_task.delay(safe_address)
        elif not safe_funding.deployer_funded:
            confirmations = settings.SAFE_FUNDING_CONFIRMATIONS
            last_block_number = ethereum_client.current_block_number

            assert (last_block_number - confirmations) > 0

            if safe_creation.payment_token and safe_creation.payment_token != NULL_ADDRESS:
                safe_balance = ethereum_client.erc20.get_balance(safe_address, safe_creation.payment_token)
            else:
                safe_balance = ethereum_client.get_balance(safe_address, last_block_number - confirmations)

            if safe_balance >= payment:
                logger.info('Found %d balance for safe=%s', safe_balance, safe_address)
                safe_funding.safe_funded = True
                safe_funding.save()

                # Check deployer has no eth. This should never happen
                balance = ethereum_client.get_balance(deployer_address)
                if balance:
                    logger.error('Deployer=%s for safe=%s has eth already (%d wei)',
                                 deployer_address, safe_address, balance)
                else:
                    logger.info('Safe=%s. Transferring deployment-cost=%d to deployer=%s',
                                safe_address, safe_creation.wei_deploy_cost(), deployer_address)
                    tx_hash = FundingServiceProvider().send_eth_to(deployer_address,
                                                                   safe_creation.wei_deploy_cost(),
                                                                   gas_price=safe_creation.gas_price,
                                                                   retry=True)
                    if tx_hash:
                        tx_hash = tx_hash.hex()
                        logger.info('Safe=%s. Transferred deployment-cost=%d to deployer=%s with tx-hash=%s',
                                    safe_address, safe_creation.wei_deploy_cost(), deployer_address, tx_hash)
                        safe_funding.deployer_funded_tx_hash = tx_hash
                        safe_funding.save()
                        logger.debug('Safe=%s deployer has just been funded. tx_hash=%s', safe_address, tx_hash)
                        check_deployer_funded_task.apply_async((safe_address,), countdown=20)
                    else:
                        logger.error('Cannot send payment=%d to deployer safe=%s', payment, deployer_address)
                        if retry:
                            raise self.retry(countdown=30)
            else:
                logger.info('Not found required balance=%d for safe=%s', payment, safe_address)
                if retry:
                    raise self.retry(countdown=30)
Ejemplo n.º 6
0
def circles_onboarding_safe_task(self, safe_address: str) -> None:
    """
    Check if create2 Safe has enough incoming trust connections to fund and
    deploy it
    :param safe_address: Address of the safe to-be-created
    """

    assert check_checksum(safe_address)

    try:
        redis = RedisRepository().redis
        lock_name = f'locks:circles_onboarding_safe_task:{safe_address}'
        with redis.lock(lock_name, blocking_timeout=1, timeout=LOCK_TIMEOUT):
            logger.info('Check deploying Safe .. {}'.format(safe_address))
            try:
                SafeCreationServiceProvider().deploy_create2_safe_tx(
                    safe_address)
            except SafeCreation2.DoesNotExist:
                pass
            except NotEnoughFundingForCreation:
                logger.info('Safe does not have enough fund for deployment, '
                            'check trust connections {}'.format(safe_address))
                # If we have enough trust connections, fund safe
                if GraphQLService().check_trust_connections(safe_address):
                    logger.info(
                        'Fund Safe deployment for {}'.format(safe_address))
                    ethereum_client = EthereumClientProvider()
                    safe_creation = SafeCreation2.objects.get(
                        safe=safe_address)
                    # Estimate costs of safe creation
                    safe_deploy_cost = safe_creation.wei_estimated_deploy_cost(
                    )
                    logger.info('Estimating %d for safe creation',
                                safe_deploy_cost)
                    # Estimate costs of token creation
                    transaction_service = TransactionServiceProvider()
                    token_deploy_cost = transaction_service.estimate_circles_signup_tx(
                        safe_address)
                    logger.info('Estimating %d for token deployment',
                                token_deploy_cost)
                    # Find total onboarding costs
                    payment = safe_deploy_cost + token_deploy_cost
                    # Get current safe balance
                    safe_balance = ethereum_client.get_balance(safe_address)
                    logger.info(
                        'Found %d balance for token deployment of safe=%s. Required=%d',
                        safe_balance, safe_address, payment)
                    if safe_balance >= payment:
                        logger.info('Onboarding is already funded {}'.format(
                            safe_address))
                        return

                    FundingServiceProvider().send_eth_to(safe_address,
                                                         payment,
                                                         gas=24000)
                    # Retry later to check for enough funding and successful deployment
                    raise self.retry(countdown=30)
                else:
                    logger.info(
                        'Not enough trust connections for funding deployment {}'
                        .format(safe_address))
    except LockError:
        pass