Exemple #1
0
def check_for_replaced_tx(tx_hash, network):
    """
    Get status of the provided transaction hash, and look for a replacement transaction hash. If a
    replacement exists, return the status and hash of the new transaction
    """
    status, timestamp = get_tx_status(tx_hash, network, timezone.now())
    if status in ['pending', 'dropped', 'unknown', '']:
        new_tx = getReplacedTX(tx_hash)
        if new_tx:
            tx_hash = new_tx
            status, timestamp = get_tx_status(tx_hash, network, timezone.now())

    return tx_hash, status, timestamp
Exemple #2
0
 def update_tx_status(self):
     """Updates tx status."""
     from dashboard.utils import get_tx_status
     if self.tx_override:
         return
     tx_status, _ = get_tx_status(self.tx_id, self.subscription.network, self.created_on)
     if self.split_tx_id:
         split_tx_status, _ = get_tx_status(self.split_tx_id, self.subscription.network, self.created_on)
     if tx_status != 'pending':
         self.success = tx_status == 'success'
         self.tx_cleared = True
     if self.split_tx_id and split_tx_status != 'pending':
         self.success = split_tx_status == 'success'
         self.split_tx_confirmed = True
Exemple #3
0
 def update_tx_status(self):
     """Updates tx status."""
     from dashboard.utils import get_tx_status
     tx_status, tx_time = get_tx_status(self.tx_id, self.subscription.network, self.created_on)
     if tx_status != 'pending':
         self.success = tx_status == 'success'
         self.tx_cleared = True
Exemple #4
0
def process_subscription(subscription, live):
    is_ready_to_be_processed_db = subscription.get_is_ready_to_be_processed_from_db()

    logger.info("  - subscription %d", subscription.pk)
    if is_ready_to_be_processed_db:
        logger.info("   -- (ready via db) ")
        are_we_past_next_valid_timestamp = subscription.get_are_we_past_next_valid_timestamp()

        # FOR DEBUGGING
        if not live:
            is_ready_to_be_processed_web3 = subscription.get_is_subscription_ready_from_web3()
            is_active_web3 = subscription.get_is_active_from_web3()
            signer = subscription.get_subscription_signer_from_web3()
            logger.info("    ---  DEBUG INFO")
            logger.info(
                "    --- %s, %s, %s, %s", are_we_past_next_valid_timestamp, is_ready_to_be_processed_web3,
                is_active_web3, signer,
            )

        if not are_we_past_next_valid_timestamp:
            logger.info(f"   -- ( NOT ready via web3, will be ready on {subscription.get_next_valid_timestamp()}) ")
        else:
            logger.info("   -- (ready via web3) ")
            status = 'failure'
            txid = None
            error = ""
            try:
                if live:
                    logger.info("   -- *executing* ")
                    while not has_tx_mined(subscription.new_approve_tx_id, subscription.grant.network):
                        time.sleep(SLEEP_TIME)
                        logger.info(f"   -- *waiting {SLEEP_TIME} seconds*")
                    txid = subscription.do_execute_subscription_via_web3()
                    logger.info("   -- *waiting for mine* (txid %s) ", txid)
                    while not has_tx_mined(txid, subscription.grant.network):
                        time.sleep(SLEEP_TIME)
                        logger.info(f"   -- *waiting {SLEEP_TIME} seconds*")
                    status, __ = get_tx_status(txid, subscription.grant.network, timezone.now())
                    if status != 'success':
                        error = f"tx status from RPC is {status} not success, txid: {txid}"
                else:
                    logger.info("   -- *not live, not executing* ")
            except Exception as e:
                error = str(e)
                logger.info("   -- *not live, not executing* ")

            logger.info("   -- *mined* (status: %s / error: %s) ", status, error)
            was_success = status == 'success'
            if live:
                if not was_success:
                    logger.warning('subscription processing failed')
                    subscription.error = True
                    error_comments = f"{error}\n\ndebug info: {subscription.get_debug_info()}"
                    subscription.subminer_comments = error_comments
                    subscription.save()
                    warn_subscription_failed(subscription)
                else:
                    logger.info('subscription processing successful')
                    subscription.successful_contribution(txid)
                    subscription.save()
Exemple #5
0
    def update_tx_status(self):
        self.tx_status, self.tx_time = get_tx_status(self.txid, self.network, self.created_on)

        # Exit if transaction not mined, otherwise continue
        if self.tx_status != 'success':
            return bool(self.tx_status)

        record_ptoken_activity(self.event, self.ptoken, self.profile, self.metadata)
        self.ptoken.update_token_status()
Exemple #6
0
def check_for_replaced_tx(tx_hash, network, datetime=None, chain='std'):
    """
    Get status of the provided transaction hash, and look for a replacement transaction hash. If a
    replacement exists, return the status and hash of the new transaction
    """
    from dashboard.utils import get_tx_status

    if not datetime:
        datetime = timezone.now()

    status, timestamp = get_tx_status(tx_hash, network, datetime, chain=chain)
    if status in ['pending', 'dropped', 'unknown', '']:
        new_tx = getReplacedTX(tx_hash)
        if new_tx:
            tx_hash = new_tx
            status, timestamp = get_tx_status(tx_hash, network, datetime)

    return tx_hash, status, timestamp
Exemple #7
0
def process_subscription(subscription, live):
    is_ready_to_be_processed_db = subscription.get_is_ready_to_be_processed_from_db(
    )

    print(f"  - subscription {subscription.pk}")
    if is_ready_to_be_processed_db:
        print("   -- (ready via db) ")
        are_we_past_next_valid_timestamp = subscription.get_are_we_past_next_valid_timestamp(
        )

        # FOR DEBUGGING
        if not live:
            is_ready_to_be_processed_web3 = subscription.get_is_subscription_ready_from_web3(
            )
            is_active_web3 = subscription.get_is_active_from_web3()
            signer = subscription.get_subscription_signer_from_web3()
            print("    ---  DEBUG INFO")
            print("    --- ", are_we_past_next_valid_timestamp,
                  is_ready_to_be_processed_web3, is_active_web3, signer)

        if are_we_past_next_valid_timestamp:
            print("   -- (ready via web3) ")
            if not live:
                print("   -- *not live, not executing* ")
            else:
                print("   -- *executing* ")
            status = 'failure'
            txid = None
            error = None
            try:
                if live:
                    txid = subscription.do_execute_subscription_via_web3()
                    print(f"   -- *waiting for mine* (txid {txid}) ")
                    while not has_mined(txid, subscription):
                        time.sleep(10)
                    status, timestamp = get_tx_status(
                        txid, subscription.grant.network, timezone.now())
            except Exception as e:
                error = str(e)

            print(f"   -- *mined* (status: {status} / {error}) ")
            was_success = status == 'success'
            if live:
                if not was_success:
                    warn_subscription_failed(subscription, txid, status, error)
                else:
                    subscription.successful_contribution()
                    subscription.save()
Exemple #8
0
    def update_tx_status(self):
        self.tx_status, self.tx_time = get_tx_status(self.txid, self.network, self.created_on)

        if self.tx_status == 'success':
            metadata = {
                'purchase': self.id,
                'value_in_token': float(self.amount),
                'token_name': self.ptoken.token_symbol,
                'from_user': self.ptoken.token_owner_profile.handle,
                'holder_user': self.ptoken.token_owner_profile.handle
            }

            record_ptoken_activity('buy_ptoken', self.ptoken, self.token_holder_profile, metadata)

        self.ptoken.update_token_status()
        self.ptoken.update_user_balance(self.token_holder_profile, self.token_holder_address)
        return bool(self.tx_status)
Exemple #9
0
    def update_tx_status(self):
        self.tx_status, self.tx_time = get_tx_status(self.txid, self.network, self.created_on)

        if self.redemption_state == 'waiting_complete':
            self.ptoken.update_token_status()
            self.ptoken.update_user_balance(self.redemption_requester, self.redemption_requester_address)
            if self.tx_status == 'success':
                metadata = {'redemption': self.id}
                record_ptoken_activity('complete_redemption_ptoken', self.ptoken, self.redemption_requester, metadata, self)
                send_ptoken_redemption_complete_for_requester(self.redemption_requester, self.ptoken, self)
                send_ptoken_redemption_complete_for_owner(self.redemption_requester, self.ptoken, self)
                self.redemption_state = 'completed'

            elif self.tx_status in ['error', 'unknown', 'dropped']:
                self.redemption_state = 'accepted'

        return bool(self.tx_status)
Exemple #10
0
    def update_tx_status(self):
        self.tx_status, self.tx_time = get_tx_status(self.txid, self.network, self.created_on)

        # Exit if transaction not mined, otherwise continue
        if (self.tx_status != 'success'):
            return bool(self.tx_status)

        # Get token address from event logs
        if (self.token_address == "0x0"):
            web3 = get_web3(self.network)
            receipt = web3.eth.getTransactionReceipt(self.txid)
            contract = web3.eth.contract(Web3.toChecksumAddress(FACTORY_ADDRESS), abi=ptoken_factory_abi)
            logs = contract.events.NewPToken().processReceipt(receipt)
            self.token_address = logs[0].args.token

            record_ptoken_activity('create_ptoken', self, self.token_owner_profile)
            send_personal_token_created(self.token_owner_profile, self)

        self.update_token_status()
Exemple #11
0
    def handle(self, *args, **options):

        # setup
        payment_threshold_usd = 1
        network = 'mainnet' if not settings.DEBUG else 'rinkeby'
        from_address = settings.MINICLR_ADDRESS
        from_pk = settings.MINICLR_PRIVATE_KEY
        DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network == 'mainnet' else '0x8f2e097e79b1c51be9cba42658862f0192c3e487'

        # find a round that has recently expired
        minutes_ago = options['minutes_ago']
        cursor_time = timezone.now() - timezone.timedelta(minutes=minutes_ago)
        mr = MatchRound.objects.filter(valid_from__lt=cursor_time,
                                       valid_to__gt=cursor_time,
                                       valid_to__lt=timezone.now()).first()
        if options['round_number']:
            mr = MatchRound.objects.get(number=options['round_number'])
        if not mr:
            print(
                f'No Match Round Found that ended between {cursor_time} <> {timezone.now()}'
            )
            return
        print(mr)

        # finalize rankings
        if options['what'] == 'finalize':
            rankings = mr.ranking.filter(final=False,
                                         paid=False).order_by('-match_total')
            print(rankings.count(), "to finalize")
            for ranking in rankings:
                ranking.final = True
                ranking.save()
            print(rankings.count(), " finalied")

        # payout rankings (round must be finalized first)
        if options['what'] == 'payout':
            rankings = mr.ranking.filter(final=True,
                                         paid=False).order_by('-match_total')
            print(rankings.count(), " to pay")
            w3 = get_web3(network)
            for ranking in rankings:

                # figure out amount_owed
                profile = ranking.profile
                owed_rankings = profile.match_rankings.filter(final=True,
                                                              paid=False)
                amount_owed = sum(
                    owed_rankings.values_list('match_total', flat=True))
                print(
                    f"paying {ranking.profile.handle} who is owed {amount_owed} ({ranking.match_total} from this round)"
                )

                # validate
                error = None
                if amount_owed < payment_threshold_usd:
                    error = ("- less than amount owed; continue")
                address = profile.preferred_payout_address
                if not address:
                    error = ("- address not on file")

                if error:
                    print(error)
                    ranking.payout_tx_status = error
                    ranking.save()
                    continue

                # issue payment
                contract = w3.eth.contract(Web3.toChecksumAddress(DAI_ADDRESS),
                                           abi=abi)
                address = Web3.toChecksumAddress(address)
                amount = int(amount_owed * 10**18)
                tx = contract.functions.transfer(
                    address, amount
                ).buildTransaction({
                    'nonce':
                    w3.eth.getTransactionCount(from_address),
                    'gas':
                    100000,
                    'gasPrice':
                    int(
                        float(recommend_min_gas_price_to_confirm_in_time(1)) *
                        10**9 * 1.4)
                })

                signed = w3.eth.account.signTransaction(tx, from_pk)
                tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex()
                print("paid via", tx_id)

                # wait for tx to clear
                while not has_tx_mined(tx_id, network):
                    time.sleep(1)

                ranking.payout_tx_status, ranking.payout_tx_issued = get_tx_status(
                    tx_id, network, timezone.now())

                ranking.paid = True
                ranking.payout_txid = tx_id
                ranking.save()
                for other_ranking in owed_rankings:
                    other_ranking.paid = True
                    other_ranking.payout_txid = ranking.payout_txid
                    other_ranking.payout_tx_issued = ranking.payout_tx_issued
                    other_ranking.payout_tx_status = ranking.payout_tx_status
                    other_ranking.save()

                # create earning object
                from_profile = Profile.objects.get(handle='gitcoinbot')
                Earning.objects.update_or_create(
                    source_type=ContentType.objects.get(app_label='townsquare',
                                                        model='matchranking'),
                    source_id=ranking.pk,
                    defaults={
                        "created_on": ranking.created_on,
                        "org_profile": None,
                        "from_profile": from_profile,
                        "to_profile": ranking.profile,
                        "value_usd": amount_owed,
                        "url": 'https://gitcoin.co/#clr',
                        "network": network,
                    })

                Activity.objects.create(created_on=timezone.now(),
                                        profile=ranking.profile,
                                        activity_type='mini_clr_payout',
                                        metadata={
                                            "amount":
                                            float(amount_owed),
                                            "number":
                                            int(mr.number),
                                            "mr_pk":
                                            int(mr.pk),
                                            "round_description":
                                            f"Mini CLR Round {mr.number}"
                                        })

                from marketing.mails import match_distribution
                match_distribution(ranking)

                print("paid ", ranking)
                time.sleep(30)

        # announce finalists (round must be finalized first)
        from_profile = Profile.objects.get(handle='gitcoinbot')
        if options['what'] == 'announce':
            copy = f"Mini CLR Round {mr.number} Winners:<BR>"
            rankings = mr.ranking.filter(
                final=True).order_by('-match_total')[0:10]
            print(rankings.count(), " to announce")
            for ranking in rankings:
                profile_link = f"<a href=/{ranking.profile}>@{ranking.profile}</a>"
                copy += f" - {profile_link} was ranked <strong>#{ranking.number}</strong>. <BR>"
            metadata = {
                'copy': copy,
            }

            Activity.objects.create(
                created_on=timezone.now(),
                profile=from_profile,
                activity_type='consolidated_mini_clr_payout',
                metadata=metadata)
Exemple #12
0
def process_subscription(subscription, live):
    is_ready_to_be_processed_db = subscription.get_is_ready_to_be_processed_from_db()

    logger.info("  - subscription %d", subscription.pk)
    if is_ready_to_be_processed_db:
        logger.info("   -- (ready via db) ")
        are_we_past_next_valid_timestamp = subscription.get_are_we_past_next_valid_timestamp()
        has_approve_tx_mined = has_tx_mined(subscription.new_approve_tx_id, subscription.grant.network)

        # FOR DEBUGGING
        if not live:
            is_ready_to_be_processed_web3 = subscription.get_are_we_past_next_valid_timestamp()
            is_active_web3 = subscription.get_is_active_from_web3()
            signer = subscription.get_subscription_signer_from_web3()
            logger.info("    ---  DEBUG INFO")
            logger.info(
                "    --- %s, %s, %s, %s", are_we_past_next_valid_timestamp, is_ready_to_be_processed_web3,
                is_active_web3, signer,
            )

        if not are_we_past_next_valid_timestamp:
            logger.info(f"   -- ( NOT ready via web3, will be ready on {subscription.get_next_valid_timestamp()}) ")
        elif not has_approve_tx_mined:
            logger.info(f"   -- ( NOT ready via approve tx, will be ready when {subscription.new_approve_tx_id} mines) ")
        else:
            if subscription.contributor_signature == "onetime":
                subscription.error = True
                subscription.subminer_comments = "One time subscription"
                subscription.save()
                logger.info('skipping one time subscription: %s' % subscription.id)
                return
            web3_hash_arguments = subscription.get_subscription_hash_arguments()
            if web3_hash_arguments['periodSeconds'] < METATX_FREE_INTERVAL_SECONDS and web3_hash_arguments['gasPrice'] <= METATX_GAS_PRICE_THRESHOLD:
                subscription.error = True
                subscription.subminer_comments = "Gas price was too low to process"
                subscription.save()
                warn_subscription_failed(subscription)
                return

            logger.info("   -- (ready via web3) ")
            status = 'failure'
            txid = None
            error = ""
            try:
                if live:
                    logger.info("   -- *executing* ")

                    txid = subscription.do_execute_subscription_via_web3()
                    logger.info("   -- *waiting for mine* (txid %s) ", txid)

                    override = False
                    counter = 0
                    while not has_tx_mined(txid, subscription.grant.network) and not override:
                        time.sleep(SLEEP_TIME)
                        logger.info(f"   -- *waiting {SLEEP_TIME} seconds for {txid} to mine*")
                        counter += 1
                        if counter > MAX_COUNTER:
                            override = True
                            # force the subminer to continue on; this tx is taking too long.
                            # an admin will have to look at this later and determine what went wrong
                            # KO 2019/02/06

                    status, __ = get_tx_status(txid, subscription.grant.network, timezone.now())
                    if status != 'success':
                        error = f"tx status from RPC is {status} not success, txid: {txid}"
                else:
                    logger.info("   -- *not live, not executing* ")
            except Exception as e:
                error = str(e)
                logger.info("   -- *not live, not executing* ")

            logger.info("   -- *mined* (status: %s / error: %s) ", status, error)
            was_success = status == 'success'
            if live:
                if not was_success:
                    logger.warning('subscription processing failed')
                    subscription.error = True
                    error_comments = f"{error}\n\ndebug info: {subscription.get_debug_info()}"
                    subscription.subminer_comments = error_comments
                    subscription.save()
                    grant = subscription.grant
                    grant.updateActiveSubscriptions()
                    grant.save()
                    warn_subscription_failed(subscription)
                else:
                    logger.info('subscription processing successful')
                    subscription.successful_contribution(txid)
                    subscription.save()
Exemple #13
0
def grants_transaction_validator(contribution, w3):
    # To facilitate testing on Rinkeby, we pass in a web3 instance instead of using the mainnet
    # instance defined at the top of this file

    tx_list = [contribution.tx_id, contribution.split_tx_id]
    network = contribution.subscription.network

    token_transfer = {}
    txns = []
    validation = {'passed': False, 'comment': 'Default'}
    token_originators = []
    amounts = [
        contribution.subscription.amount_per_period_minus_gas_price,
        contribution.subscription.amount_per_period
    ]

    for tx in tx_list:

        if not tx:
            continue

        # check for dropped and replaced txn
        status, timestamp = get_tx_status(tx, network, timezone.now())
        maybeprint(120, round(time.time(), 2))
        if status in ['pending', 'dropped', 'unknown', '']:
            new_tx = getReplacedTX(tx)
            if new_tx:
                tx = new_tx
                status, timestamp = get_tx_status(tx, network, timezone.now())

        maybeprint(127, round(time.time(), 2))
        # check for txfrs
        if status == 'success':

            # check if it was an ETH transaction
            maybeprint(132, round(time.time(), 2))
            transaction_receipt = w3.eth.getTransactionReceipt(tx)
            from_address = transaction_receipt['from']
            # todo save back to the txn if needed?
            if (transaction_receipt != None
                    and transaction_receipt.cumulativeGasUsed >= 2100):
                maybeprint(138, round(time.time(), 2))
                transaction_hash = transaction_receipt.transactionHash.hex()
                transaction = w3.eth.getTransaction(transaction_hash)
                if transaction.value > 0.001:
                    recipient_address = Web3.toChecksumAddress(
                        contribution.subscription.grant.admin_address)
                    transfer = get_token_originators(recipient_address,
                                                     '0x0',
                                                     from_address=from_address,
                                                     return_what='transfers',
                                                     tx_id=tx,
                                                     amounts=amounts)
                    if not transfer:
                        transfer = get_token_originators(
                            recipient_address,
                            '0x0',
                            from_address=from_address,
                            return_what='transfers',
                            tx_id=tx)
                    if transfer:
                        token_transfer = transfer
                maybeprint(148, round(time.time(), 2))
                if not token_originators:

                    token_originators = get_token_originators(
                        from_address,
                        '0x0',
                        from_address=None,
                        return_what='originators')

            maybeprint(150, round(time.time(), 2))
            # check if it was an ERC20 transaction
            if contribution.subscription.contributor_address and \
                contribution.subscription.grant.admin_address and \
                contribution.subscription.token_address:

                from_address = Web3.toChecksumAddress(
                    contribution.subscription.contributor_address)
                recipient_address = Web3.toChecksumAddress(
                    contribution.subscription.grant.admin_address)
                token_address = Web3.toChecksumAddress(
                    contribution.subscription.token_address)

                maybeprint(160, round(time.time(), 2))
                # get token transfers
                if not token_transfer:
                    transfers = get_token_originators(
                        recipient_address,
                        token_address,
                        from_address=from_address,
                        return_what='transfers',
                        tx_id=tx,
                        amounts=amounts)
                    if transfers:
                        token_transfer = transfers
                maybeprint(169, round(time.time(), 2))
                if not token_originators:
                    token_originators = get_token_originators(
                        from_address,
                        token_address,
                        from_address=None,
                        return_what='originators')
                maybeprint(170, round(time.time(), 2))

        # log transaction and and any xfr
        txns.append({
            'id': tx,
            'status': status,
        })

    if not token_transfer:
        transaction_receipt = w3.eth.getTransactionReceipt(tx)
        is_bulk_checkout = transaction_receipt['to'].lower(
        ) == "0x7d655c57f71464B6f83811C55D84009Cd9f5221C".lower()
        if is_bulk_checkout:
            validation['comment'] = "Bulk checkout"
            validation['passed'] = transaction_receipt['status'] == 1
        else:
            validation['comment'] = "No Transfers Occured"
            validation['passed'] = False
    else:
        if token_transfer[
                'token_name'] != contribution.subscription.token_symbol:
            validation[
                'comment'] = f"Tokens do not match, {token_transfer['token_name']} != {contribution.subscription.token_symbol}"
            validation['passed'] = False

            from_address = Web3.toChecksumAddress(
                contribution.subscription.contributor_address)
            recipient_address = Web3.toChecksumAddress(
                contribution.subscription.grant.admin_address)
            token_address = Web3.toChecksumAddress(
                contribution.subscription.token_address)
            _transfers = get_token_originators(recipient_address,
                                               token_address,
                                               from_address=from_address,
                                               return_what='transfers',
                                               tx_id=tx,
                                               amounts=amounts)
            failsafe = _transfers[
                'token_name'] == contribution.subscription.token_symbol
            if failsafe:
                validation[
                    'comment'] = f"Token Transfer Passed on the second try"
                validation['passed'] = True
                token_transfer = _transfers

        else:
            delta1 = float(token_transfer['token_amount_decimal']) - float(
                contribution.subscription.amount_per_period_minus_gas_price)
            delta2 = float(token_transfer['token_amount_decimal']) - float(
                contribution.subscription.amount_per_period)
            threshold = float(
                float(
                    abs(contribution.subscription.
                        amount_per_period_minus_gas_price)) *
                float(validation_threshold_pct))
            validation['passed'] = (
                abs(delta1) <= threshold or abs(delta2) <= threshold) or (
                    abs(delta1) <= validation_threshold_total
                    or abs(delta2) <= validation_threshold_total)
            validation[
                'comment'] = f"Transfer Amount is off by {round(delta1, 2)} / {round(delta2, 2)}"

    return {
        'contribution': {
            'pk': contribution.pk,
            'amount_per_period_to_gitcoin':
            contribution.subscription.amount_per_period_to_gitcoin,
            'amount_per_period_to_grantee':
            contribution.subscription.amount_per_period_minus_gas_price,
            'from': contribution.subscription.contributor_address,
            'to': contribution.subscription.grant.admin_address,
        },
        'validation': validation,
        'transfers': token_transfer,
        'originator': token_originators,
        'txns': txns,
    }
Exemple #14
0
def grants_transaction_validator(contribution):
    tx_list = [contribution.tx_id, contribution.split_tx_id]
    network = contribution.subscription.network

    token_transfer = {}
    txns = []
    validation = {
        'passed': False,
        'comment': 'Default'
    }
    token_originators = []

    for tx in tx_list:

        if not tx:
            continue

        # check for dropped and replaced txn
        status, timestamp = get_tx_status(tx, network, timezone.now())
        maybeprint(120, round(time.time(),2))
        if status in ['pending', 'dropped', 'unknown', '']:
            new_tx = getReplacedTX(tx)
            if new_tx:
                tx = new_tx
                status, timestamp = get_tx_status(tx, network, timezone.now())

        maybeprint(127, round(time.time(),2))
        # check for txfrs
        if status == 'success':

            # check if it was an ETH transaction
            maybeprint(132, round(time.time(),2))
            transaction_receipt = w3.eth.getTransactionReceipt(tx)
            from_address = transaction_receipt['from']
            # todo save back to the txn if needed?
            if (transaction_receipt != None and transaction_receipt.cumulativeGasUsed >= 2100):
                maybeprint(138, round(time.time(),2))
                transaction_hash = transaction_receipt.transactionHash.hex()
                transaction = check_transaction(transaction_hash)
                if transaction.value > 0.001:
                    token_transfer = {
                        'to': transaction.to,
                        'token_name': 'ETH',
                        'token_address': '0x0',
                        'token_amount_int': Decimal(transaction.value),
                        'token_amount_decimal': Decimal(transaction.value / 10 **18),
                        'decimals': 18,
                        }
                maybeprint(148, round(time.time(),2))
                if not token_originators:
                    token_originators = get_token_originators(from_address, '0x0', from_address=None, return_what='originators')

            maybeprint(150, round(time.time(),2))
            # check if it was an ERC20 transaction
            if contribution.subscription.contributor_address and \
                contribution.subscription.grant.admin_address and \
                contribution.subscription.token_address:

                from_address = Web3.toChecksumAddress(contribution.subscription.contributor_address)
                recipient_address = Web3.toChecksumAddress(contribution.subscription.grant.admin_address)
                token_address = Web3.toChecksumAddress(contribution.subscription.token_address)

                maybeprint(160, round(time.time(),2))
                # get token transfers
                if not token_transfer:
                    transfers = get_token_originators(recipient_address, token_address, from_address=from_address, return_what='transfers')
                    if transfers:
                        token_transfer = transfers
                maybeprint(169, round(time.time(),2))
                if not token_originators:
                    token_originators = get_token_originators(from_address, token_address, from_address=None, return_what='originators')
                maybeprint(170, round(time.time(),2))


        # log transaction and and any xfr
        txns.append({
            'id': tx,
            'status': status,
            })

    if not token_transfer:
        validation['comment'] = "No Transfers Occured"
        validation['passed'] = False
    else:
        if token_transfer['token_name'] != contribution.subscription.token_symbol:
            validation['comment'] = f"Tokens do not match, {token_transfer['token_name']} != {contribution.subscription.token_symbol}"
            validation['passed'] = False
        else:
            delta = Decimal(token_transfer['token_amount_decimal']) - Decimal(contribution.subscription.amount_per_period_minus_gas_price)
            # TODO what about gitcoin transfers
            validation['comment'] = f"Transfer Amount is off by {round(delta, 2)}"
            validation['passed'] = abs(delta) <= 0.01


    return {
        'contribution': {
            'pk': contribution.pk,
            'amount_per_period_to_gitcoin': contribution.subscription.amount_per_period_to_gitcoin,
            'amount_per_period_to_grantee': contribution.subscription.amount_per_period_minus_gas_price,
            'from': contribution.subscription.contributor_address,
            'to': contribution.subscription.grant.admin_address,
        },
        'validation': validation,
        'transfers': token_transfer,
        'originator': token_originators,
        'txns': txns,
    }
Exemple #15
0
def process_subscription(subscription, live):
    is_ready_to_be_processed_db = subscription.get_is_ready_to_be_processed_from_db(
    )

    logger.info("  - subscription %d", subscription.pk)
    if is_ready_to_be_processed_db:
        logger.info("   -- (ready via db) ")
        are_we_past_next_valid_timestamp = subscription.get_are_we_past_next_valid_timestamp(
        )

        # FOR DEBUGGING
        if not live:
            is_ready_to_be_processed_web3 = subscription.get_is_subscription_ready_from_web3(
            )
            is_active_web3 = subscription.get_is_active_from_web3()
            signer = subscription.get_subscription_signer_from_web3()
            logger.info("    ---  DEBUG INFO")
            logger.info(
                "    --- %s, %s, %s, %s",
                are_we_past_next_valid_timestamp,
                is_ready_to_be_processed_web3,
                is_active_web3,
                signer,
            )

        if not are_we_past_next_valid_timestamp:
            logger.info(
                f"   -- ( NOT ready via web3, will be ready on {subscription.get_next_valid_timestamp()}) "
            )
        else:
            logger.info("   -- (ready via web3) ")
            status = 'failure'
            txid = None
            error = ""
            try:
                if live:
                    logger.info("   -- *executing* ")
                    counter = 0
                    while not has_tx_mined(subscription.new_approve_tx_id,
                                           subscription.grant.network):
                        time.sleep(SLEEP_TIME)
                        logger.info(
                            f"   -- *waiting {SLEEP_TIME} seconds for {subscription.new_approve_tx_id} to mine*"
                        )
                        counter += 1
                        if counter > MAX_COUNTER:
                            raise Exception(
                                f"waited more than {MAX_COUNTER} times for tx  to mine"
                            )

                    txid = subscription.do_execute_subscription_via_web3()
                    logger.info("   -- *waiting for mine* (txid %s) ", txid)

                    override = False
                    counter = 0
                    while not has_tx_mined(
                            txid, subscription.grant.network) and not override:
                        time.sleep(SLEEP_TIME)
                        logger.info(
                            f"   -- *waiting {SLEEP_TIME} seconds for {txid} to mine*"
                        )
                        counter += 1
                        if counter > MAX_COUNTER:
                            override = True
                            # force the subminer to continue on; this tx is taking too long.
                            # an admin will have to look at this later and determine what went wrong
                            # KO 2019/02/06

                    status, __ = get_tx_status(txid,
                                               subscription.grant.network,
                                               timezone.now())
                    if status != 'success':
                        error = f"tx status from RPC is {status} not success, txid: {txid}"
                else:
                    logger.info("   -- *not live, not executing* ")
            except Exception as e:
                error = str(e)
                logger.info("   -- *not live, not executing* ")

            logger.info("   -- *mined* (status: %s / error: %s) ", status,
                        error)
            was_success = status == 'success'
            if live:
                if not was_success:
                    logger.warning('subscription processing failed')
                    subscription.error = True
                    error_comments = f"{error}\n\ndebug info: {subscription.get_debug_info()}"
                    subscription.subminer_comments = error_comments
                    subscription.save()
                    warn_subscription_failed(subscription)
                else:
                    logger.info('subscription processing successful')
                    subscription.successful_contribution(txid)
                    subscription.save()
Exemple #16
0
    def handle(self, *args, **options):
        # setup
        minutes_ago = 10 if not settings.DEBUG else 40
        payment_threshold_usd = 1
        network = 'mainnet' if not settings.DEBUG else 'rinkeby'
        from_address = settings.MINICLR_ADDRESS
        from_pk = settings.MINICLR_PRIVATE_KEY
        DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network == 'mainnet' else '0x8f2e097e79b1c51be9cba42658862f0192c3e487'
        provider = settings.WEB3_HTTP_PROVIDER if network == 'mainnet' else "https://rinkeby.infura.io/"

        # find a round that has recently expired
        cursor_time = timezone.now() - timezone.timedelta(minutes=minutes_ago)
        mr = MatchRound.objects.filter(valid_from__lt=cursor_time,
                                       valid_to__gt=cursor_time,
                                       valid_to__lt=timezone.now()).first()
        if not mr:
            print(
                f'No Match Round Found that ended between {cursor_time} <> {timezone.now()}'
            )
            return
        print(mr)

        # finalize rankings
        rankings = mr.ranking.filter(final=False,
                                     paid=False).order_by('number')
        print(rankings.count())
        for ranking in rankings:
            ranking.final = True
            ranking.save()

        # payout rankings
        rankings = mr.ranking.filter(final=True, paid=False).order_by('number')
        print(rankings.count())
        w3 = Web3(HTTPProvider(provider))
        for ranking in rankings:
            print(ranking)

            # figure out amount_owed
            profile = ranking.profile
            owed_rankings = profile.match_rankings.filter(final=True,
                                                          paid=False)
            amount_owed = sum(
                owed_rankings.values_list('match_total', flat=True))

            # validate
            error = None
            if amount_owed < payment_threshold_usd:
                error = ("- less than amount owed; continue")
            address = profile.preferred_payout_address
            if not address:
                error = ("- address not on file")

            if error:
                ranking.payout_tx_status = error
                ranking.save()
                continue

            # issue payment
            contract = w3.eth.contract(Web3.toChecksumAddress(DAI_ADDRESS),
                                       abi=abi)
            address = Web3.toChecksumAddress(address)
            amount = int(amount_owed * 10**18)
            tx = contract.functions.transfer(address, amount).buildTransaction(
                {
                    'nonce':
                    w3.eth.getTransactionCount(from_address),
                    'gas':
                    100000,
                    'gasPrice':
                    recommend_min_gas_price_to_confirm_in_time(1) * 10**9
                })

            signed = w3.eth.account.signTransaction(tx, from_pk)
            tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex()

            # wait for tx to clear
            while not has_tx_mined(tx_id, network):
                time.sleep(1)

            ranking.payout_tx_status, ranking.payout_tx_issued = get_tx_status(
                tx_id, network, timezone.now())

            ranking.paid = True
            ranking.payout_txid = tx_id
            ranking.save()
            for other_ranking in owed_rankings:
                other_ranking.paid = True
                other_ranking.payout_txid = ranking.payout_txid
                other_ranking.payout_tx_issued = ranking.payout_tx_issued
                other_ranking.payout_tx_status = ranking.payout_tx_status
                other_ranking.save()

            # create earning object
            from dashboard.models import Earning, Profile, Activity
            from django.contrib.contenttypes.models import ContentType
            from_profile = Profile.objects.get(handle='gitcoinbot')
            Earning.objects.update_or_create(
                source_type=ContentType.objects.get(app_label='townsquare',
                                                    model='matchranking'),
                source_id=ranking.pk,
                defaults={
                    "created_on": ranking.created_on,
                    "org_profile": None,
                    "from_profile": from_profile,
                    "to_profile": ranking.profile,
                    "value_usd": amount_owed,
                    "url": 'https://gitcoin.co/#clr',
                    "network": network,
                })

            Activity.objects.create(created_on=timezone.now(),
                                    profile=ranking.profile,
                                    activity_type='mini_clr_payout',
                                    metadata={
                                        "amount": amount_owed,
                                    })

            from marketing.mails import match_distribution
            match_distribution(ranking)