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()
def handle(self, *args, **options): network = 'mainnet' if int(recommend_min_gas_price_to_confirm_in_time(1)) > 10: return kts = KudosTransfer.objects.not_submitted().filter(network='mainnet') for kt in kts: redemption = kt.bulk_transfer_redemptions.first() address = kt.receive_address profile = kt.recipient_profile coupon = redemption.coupon ip_address = redemption.ip_address tx_id, _, _ = redeem_bulk_coupon(coupon, profile, address, ip_address, exit_after_sending_tx=True) print(tx_id) while not has_tx_mined(tx_id, network): time.sleep(1) kt.txid = txid kt.receive_txid = txid kt.receive_tx_status = 'success' kt.tx_status = 'success' kt.received_on = timezone.now() kt.tx_time = timezone.now() kt.save()
def mint_token_request(self, token_req_id, retry=False): """ :param self: :param token_req_id: :return: """ with redis.lock("tasks:all_kudos_requests", timeout=LOCK_TIMEOUT): with redis.lock("tasks:token_req_id:%s" % token_req_id, timeout=LOCK_TIMEOUT): from kudos.management.commands.mint_all_kudos import sync_latest from gas.utils import recommend_min_gas_price_to_confirm_in_time from dashboard.utils import has_tx_mined obj = TokenRequest.objects.get(pk=token_req_id) multiplier = 1 gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(1)) * multiplier) if gas_price > delay_if_gas_prices_gt_mint and self.request.retries < self.max_retries: self.retry(countdown=120) return tx_id = obj.mint(gas_price) if tx_id: while not has_tx_mined(tx_id, obj.network): time.sleep(1) sync_latest(0) sync_latest(1) sync_latest(2) sync_latest(3) notify_kudos_minted(obj) else: self.retry(countdown=(30 * (self.request.retries + 1)))
def sync_web3(request): """ Sync up web3 with the database. This function has a few different uses. It is typically called from the front end using the javascript `sync_web3` function. The `issueURL` is passed in first, followed optionally by a `bountydetails` argument. """ # setup result = { 'status': '400', 'msg': "bad request" } issue_url = request.POST.get('url') txid = request.POST.get('txid') network = request.POST.get('network') if issue_url and txid and network: # confirm txid has mined print('* confirming tx has mined') if not has_tx_mined(txid, network): result = { 'status': '400', 'msg': 'tx has not mined yet' } else: # get bounty id print('* getting bounty id') bounty_id = get_bounty_id(issue_url, network) if not bounty_id: result = { 'status': '400', 'msg': 'could not find bounty id' } else: # get/process bounty print('* getting bounty') bounty = get_bounty(bounty_id, network) print('* processing bounty') did_change = False max_tries_attempted = False counter = 0 url = None while not did_change and not max_tries_attempted: did_change, _, new_bounty = web3_process_bounty(bounty) if not did_change: print("RETRYING") time.sleep(3) counter += 1 max_tries_attempted = counter > 3 if new_bounty: url = new_bounty.url result = { 'status': '200', 'msg': "success", 'did_change': did_change, 'url': url, } return JsonResponse(result, status=result['status'])
def sync_web3(request): """ Sync up web3 with the database. This function has a few different uses. It is typically called from the front end using the javascript `sync_web3` function. The `issueURL` is passed in first, followed optionally by a `bountydetails` argument. """ # setup result = { 'status': '400', 'msg': "bad request" } issueURL = request.POST.get('url') txid = request.POST.get('txid') network = request.POST.get('network') if issueURL and txid and network: # confirm txid has mined print('* confirming tx has mined') if not has_tx_mined(txid, network): result = { 'status': '400', 'msg': 'tx has not mined yet' } else: # get bounty id print('* getting bounty id') bounty_id = getBountyID(issueURL, network) if not bounty_id: result = { 'status': '400', 'msg': 'could not find bounty id' } else: # get/process bounty print('* getting bounty') bounty = get_bounty(bounty_id, network) print('* processing bounty') did_change, _, _ = web3_process_bounty(bounty) result = { 'status': '200', 'msg': "success", 'did_change': did_change } return JsonResponse(result, status=result['status'])
def redeem_bulk_kudos(self, kt_id, signed_rawTransaction, retry=False): """ :param self: :param kt_id: :param signed_rawTransaction: :return: """ with redis.lock("tasks:all_redeem_bulk_kudos", timeout=LOCK_TIMEOUT): with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): from dashboard.utils import has_tx_mined try: obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) obj.txid = w3.eth.sendRawTransaction(HexBytes(signed_rawTransaction)).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass except Exception as e: self.retry(30)
def mint_token_request(self, token_req_id, retry=False): """ :param self: :param token_req_id: :return: """ with redis.lock("tasks:token_req_id:%s" % token_req_id, timeout=LOCK_TIMEOUT): from kudos.management.commands.mint_all_kudos import sync_latest from dashboard.utils import has_tx_mined obj = TokenRequest.objects.get(pk=token_req_id) tx_id = obj.mint() if tx_id: while not has_tx_mined(tx_id, obj.network): time.sleep(1) sync_latest(0) sync_latest(1) sync_latest(2) sync_latest(3) else: self.retry(30)
def mint_token_request(self, token_req_id, retry=False): """ :param self: :param token_req_id: :return: """ with redis.lock("tasks:all_token_mint_requests", timeout=LOCK_TIMEOUT): with redis.lock("tasks:token_req_id:%s" % token_req_id, timeout=LOCK_TIMEOUT): from kudos.management.commands.mint_all_kudos import sync_latest from gas.utils import recommend_min_gas_price_to_confirm_in_time from dashboard.utils import has_tx_mined obj = TokenRequest.objects.get(pk=token_req_id) multiplier = 1 if not retry else (mint_token_request.request.retries + 1) gas_price = int(recommend_min_gas_price_to_confirm_in_time(1) * multiplier) tx_id = obj.mint(gas_price) if tx_id: while not has_tx_mined(tx_id, obj.network): time.sleep(1) sync_latest(0) sync_latest(1) sync_latest(2) sync_latest(3) else: self.retry(30)
def has_mined(txid, subscription): return has_tx_mined(txid, subscription.grant.network)
def redeem_bulk_kudos(self, kt_id, delay_if_gas_prices_gt_redeem=50, override_gas_price=None, send_notif_email=False, override_lock_timeout=LOCK_TIMEOUT, retry=False): """ :param self: :param kt_id: :return: """ try: if True: # override for allowing many xdai minting; we can change this back later there is a race condition # but for now the lock was providing more trouble than good - KO 10/20/2020 #with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=override_lock_timeout): multiplier = 1 # high gas prices, 5 hour gas limit - DL gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(300)) * multiplier) if override_gas_price: gas_price = override_gas_price if gas_price > delay_if_gas_prices_gt_redeem: # do not retry is gas prices are too high # TODO: revisit this when gas prices go down # self.retry(countdown=60*10) return if override_gas_price: gas_price = override_gas_price * 10**9 obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) token = obj.kudos_token_cloned_from if token.owner_address.lower( ) != '0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F'.lower(): raise Exception( "kudos isnt owned by Gitcoin; cowardly refusing to spend Gitcoin's ETH minting it" ) kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT kudos_owner_address = Web3.toChecksumAddress(kudos_owner_address) contract_addr = settings.KUDOS_CONTRACT_MAINNET if obj.network == 'xdai': contract_addr = settings.KUDOS_CONTRACT_XDAI if obj.network == 'rinkeby': contract_addr = settings.KUDOS_CONTRACT_RINKEBY kudos_contract_address = Web3.toChecksumAddress(contract_addr) contract = w3.eth.contract( Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone( Web3.toChecksumAddress(obj.receive_address), token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(token.price_finney / 1000.0 * 10**18), }) private_key = settings.KUDOS_PRIVATE_KEY signed = w3.eth.account.signTransaction(tx, private_key) obj.txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass if send_notif_email: from_email = '*****@*****.**' from_name = 'Kevin @ Gitcoin' _to_email = obj.recipient_profile.email subject = f"Your '{obj.kudos_token_cloned_from.name}' Kudos has been minted 🌈" block_url = f'https://etherscan.io/tx/{obj.txid}' if obj.network == 'xdai': block_url = f'https://blockscout.com/poa/xdai/tx/{obj.txid}/internal-transactions' body = f''' Hello @{obj.recipient_profile.handle}, Back on {obj.created_on} you minted a '{obj.kudos_token_cloned_from.name}' Kudos, but the Ethereum network's gas fees were too high for us to mint it on-chain. We're writing with good news. The gas prices on Ethereum have come down, and we are have now minted your token. You can now see the Kudos in your gitcoin profile ( https://gitcoin.co/{obj.recipient_profile.handle} ) or any blockchain wallet that connects to the {obj.network} network ( {block_url} ). HOORAY! Party on, Kevin + the Gitcoin team ''' send_mail(from_email, _to_email, subject, body, from_name=from_name) except (SoftTimeLimitExceeded, TimeLimitExceeded): print('max timeout for bulk kudos redeem exceeded ... giving up!') except Exception as e: print(e) if self.request.retries < self.max_retries: self.retry(countdown=(30 * (self.request.retries + 1))) else: print("max retries for bulk kudos redeem exceeded ... giving up!")
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()
* 10**9) if network == 'xdai': gasPrice = 1 * 10**9 tx = contract.functions.clone(address, token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gasPrice, 'value': int(price_finney / 1000.0 * 10**18), }) signed = w3.eth.account.signTransaction(tx, settings.KUDOS_PRIVATE_KEY) txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() nonce += 1 print(f'sent tx nonce:{nonce} for kt:{kt.id} on {network}') kt.txid = txid kt.receive_txid = txid kt.tx_status = 'pending' kt.network = network kt.save() while not has_tx_mined(txid, network): time.sleep(TIME_SLEEP) except Exception as e: print(e) error = "Could not redeem your kudos. Please try again soon."
# note: only use this if the admin is failling token_req_id = 2235 import time from dashboard.utils import has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time from kudos.management.commands.mint_all_kudos import sync_latest from kudos.models import TokenRequest from marketing.mails import notify_kudos_minted obj = TokenRequest.objects.get(pk=token_req_id) multiplier = 1 gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(1)) * multiplier) tx_id = obj.mint(gas_price) while not has_tx_mined(tx_id, obj.network): time.sleep(1) for i in range(0, 9): sync_latest(i, obj.network) notify_kudos_minted(obj)
def handle(self, *args, **options): ## creates text inputs which can be hidden in gitcoin avatars # setup network = 'mainnet' if not settings.DEBUG else 'rinkeby' amount = float(options['amount']) amount = int(amount * 10**18) number = int(options['number']) from_address = settings.AVATAR_ADDRESS from_address = Web3.toChecksumAddress(from_address) from_pk = settings.AVATAR_PRIVATE_KEY w3 = get_web3(network) # generate accounts accounts = [] for i in range(0, number): entropy = str(uuid.uuid4()) acct = w3.eth.account.create(entropy) accounts.append(acct) # generate the accounts and # send them to the database counter = 1 for account in accounts: try: # do the send to = account.address private_key = account.privateKey to = Web3.toChecksumAddress(to) txn = { 'to': to, 'from': from_address, 'value': amount, 'nonce': w3.eth.getTransactionCount(from_address), 'gas': 22000, 'gasPrice': int( float(recommend_min_gas_price_to_confirm_in_time(1)) * 10**9 * 1.4), 'data': b'', } signed = w3.eth.account.signTransaction(txn, from_pk) tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex() # wait for it to mine print(f"({counter}/{number}) - paid via", tx_id) while not has_tx_mined(tx_id, network): time.sleep(1) # save to db AvatarTextOverlayInput.objects.create( active=True, text=private_key.hex(), coment='auto avatar text creation', num_uses_total=1, current_uses=0, num_uses_remaining=1, ) counter += 1 except Exception as e: print(e)
def redeem_bulk_kudos(self, kt_id, retry=False): """ :param self: :param kt_id: :return: """ with redis.lock("tasks:all_kudos_requests", timeout=LOCK_TIMEOUT): with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): from dashboard.utils import has_tx_mined from gas.utils import recommend_min_gas_price_to_confirm_in_time try: multiplier = 1 gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(1)) * multiplier) if gas_price > delay_if_gas_prices_gt: self.retry(countdown=120) return obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) token = obj.kudos_token_cloned_from if token.owner_address.lower( ) != '0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F'.lower(): raise Exception( "kudos isnt owned by Gitcoin; cowardly refusing to spend Gitcoin's ETH minting it" ) kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT kudos_owner_address = Web3.toChecksumAddress( kudos_owner_address) kudos_contract_address = Web3.toChecksumAddress( settings.KUDOS_CONTRACT_MAINNET) contract = w3.eth.contract( Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone( Web3.toChecksumAddress(obj.receive_address), token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(token.price_finney / 1000.0 * 10**18), }) private_key = settings.KUDOS_PRIVATE_KEY signed = w3.eth.account.signTransaction(tx, private_key) obj.txid = w3.eth.sendRawTransaction( signed.rawTransaction).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass except Exception as e: print(e) self.retry(countdown=(30 * (self.request.retries + 1)))
def handle(self, *args, **options): # setup payment_threshold_usd = 0 KYC_THRESHOLD = settings.GRANTS_PAYOUT_CLR_KYC_THRESHOLD network = 'mainnet' if not settings.DEBUG else 'rinkeby' from_address = settings.GRANTS_PAYOUT_ADDRESS from_pk = settings.GRANTS_PAYOUT_PRIVATE_KEY DECIMALS = 18 what = options['what'] clr_round = options['clr_round'] DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network=='mainnet' else '0x6a6e8b58dee0ca4b4ee147ad72d3ddd2ef1bf6f7' CLR_TOKEN_ADDRESS = '0xe4101d014443af2b7f6f9f603e904adc9faf0de5' if network=='mainnet' else '0xc19b694ebd4309d7a2adcd9970f8d7f424a1528b' # get data clr_pks = options['clr_pks'].split(',') gclrs = GrantCLR.objects.filter(pk__in=clr_pks) pks = [] for gclr in gclrs: pks += gclr.grants.values_list('pk', flat=True) scheduled_matches = CLRMatch.objects.filter(round_number=clr_round) grants = Grant.objects.filter(active=True, network='mainnet', link_to_new_grant__isnull=True, pk__in=pks) print(f"got {grants.count()} grants") # finalize rankings if what == 'finalize': total_owed_grants = 0 for grant in grants: try: for gclr in grant.clr_calculations.filter(grantclr__in=gclrs, latest=True): total_owed_grants += gclr.clr_prediction_curve[0][1] except: pass total_owed_matches = sum(sm.amount for sm in scheduled_matches) print(f"there are {grants.count()} grants to finalize worth ${round(total_owed_grants,2)}") print(f"there are {scheduled_matches.count()} Match Payments already created worth ${round(total_owed_matches,2)}") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return for grant in grants: amount = sum(ele.clr_prediction_curve[0][1] for ele in grant.clr_calculations.filter(grantclr__in=gclrs, latest=True)) has_already_kyc = grant.clr_matches.filter(has_passed_kyc=True).exists() if not amount: continue already_exists = scheduled_matches.filter(grant=grant).exists() if already_exists: continue needs_kyc = amount > KYC_THRESHOLD and not has_already_kyc comments = "" if not needs_kyc else "Needs KYC" ready_for_test_payout = not needs_kyc match = CLRMatch.objects.create( round_number=clr_round, amount=amount, grant=grant, comments=comments, ready_for_test_payout=ready_for_test_payout, ) if needs_kyc: grant_match_distribution_kyc(match) # payout rankings (round must be finalized first) if what in ['prepare_final_payout']: payout_matches = scheduled_matches.exclude(test_payout_tx='').filter(ready_for_payout=False) payout_matches_amount = sum(sm.amount for sm in payout_matches) print(f"there are {payout_matches.count()} UNPAID Match Payments already created worth ${round(payout_matches_amount,2)} {network} DAI") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return for match in payout_matches: match.ready_for_payout=True match.save() print('promoted') # payout rankings (round must be finalized first) if what in ['payout_test', 'payout_dai']: is_real_payout = what == 'payout_dai' TOKEN_ADDRESS = DAI_ADDRESS if is_real_payout else CLR_TOKEN_ADDRESS kwargs = {} token_name = f'CLR{clr_round}' if not is_real_payout else 'DAI' key = 'ready_for_test_payout' if not is_real_payout else 'ready_for_payout' kwargs[key] = False not_ready_scheduled_matches = scheduled_matches.filter(**kwargs) kwargs[key] = True kwargs2 = {} key2 = 'test_payout_tx' if not is_real_payout else 'payout_tx' kwargs2[key2] = '' unpaid_scheduled_matches = scheduled_matches.filter(**kwargs).filter(**kwargs2) paid_scheduled_matches = scheduled_matches.filter(**kwargs).exclude(**kwargs2) total_not_ready_matches = sum(sm.amount for sm in not_ready_scheduled_matches) total_owed_matches = sum(sm.amount for sm in unpaid_scheduled_matches) total_paid_matches = sum(sm.amount for sm in paid_scheduled_matches) print(f"there are {not_ready_scheduled_matches.count()} NOT READY Match Payments already created worth ${round(total_not_ready_matches,2)} {network} {token_name}") print(f"there are {unpaid_scheduled_matches.count()} UNPAID Match Payments already created worth ${round(total_owed_matches,2)} {network} {token_name}") print(f"there are {paid_scheduled_matches.count()} PAID Match Payments already created worth ${round(total_paid_matches,2)} {network} {token_name}") print('------------------------------') user_input = input("continue? (y/n) ") if user_input != 'y': return print(f"continuing with {unpaid_scheduled_matches.count()} unpaid scheduled payouts") if is_real_payout: user_input = input(F"THIS IS A REAL PAYOUT FOR {network} {token_name}. ARE YOU DOUBLE SECRET SUPER SURE? (y/n) ") if user_input != 'y': return for match in unpaid_scheduled_matches.order_by('amount'): # issue payment print(f"- issuing payout {match.pk} worth {match.amount} {token_name}") address = match.grant.admin_address amount_owed = match.amount w3 = get_web3(network) contract = w3.eth.contract(Web3.toChecksumAddress(TOKEN_ADDRESS), abi=abi) address = Web3.toChecksumAddress(address) amount = int(amount_owed * 10**DECIMALS) tx_args = { 'nonce': w3.eth.getTransactionCount(from_address), 'gas': 100000, 'gasPrice': int(float(recommend_min_gas_price_to_confirm_in_time(1)) * 10**9 * 1.4) } tx = contract.functions.transfer(address, amount).buildTransaction(tx_args) signed = w3.eth.account.signTransaction(tx, from_pk) tx_id = None success = False counter = 0 while not success: try: tx_id = w3.eth.sendRawTransaction(signed.rawTransaction).hex() success = True except Exception as e: counter +=1 if 'replacement transaction underpriced' in str(e): print(f'replacement transaction underpriced. retrying {counter}') time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) elif 'nonce too low' in str(e): print(f'nonce too low. retrying {counter}') time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) # rebuild txn tx_args['nonce'] = w3.eth.getTransactionCount(from_address) tx = contract.functions.transfer(address, amount).buildTransaction(tx_args) signed = w3.eth.account.signTransaction(tx, from_pk) else: raise e if not tx_id: print("cannot pay advance, did not get a txid") continue print("paid via", tx_id) # make save state to DB if is_real_payout: match.payout_tx = tx_id else: match.test_payout_tx = tx_id match.save() # wait for tx to clear while not has_tx_mined(tx_id, network): time.sleep(1) # make save state to DB if is_real_payout: match.payout_tx_date = timezone.now() grant_match_distribution_final_txn(match) else: match.test_payout_tx_date = timezone.now() grant_match_distribution_test_txn(match) match.save() # create payout obj artifacts profile = Profile.objects.get(handle__iexact='gitcoinbot') validator_comment = f"created by ingest payout_round_script" subscription = Subscription() subscription.is_postive_vote = True subscription.active = False subscription.error = True subscription.contributor_address = 'N/A' subscription.amount_per_period = match.amount subscription.real_period_seconds = 2592000 subscription.frequency = 30 subscription.frequency_unit = 'N/A' subscription.token_address = TOKEN_ADDRESS subscription.token_symbol = token_name subscription.gas_price = 0 subscription.new_approve_tx_id = '0x0' subscription.num_tx_approved = 1 subscription.network = network subscription.contributor_profile = profile subscription.grant = match.grant subscription.comments = validator_comment subscription.amount_per_period_usdt = match.amount if is_real_payout else 0 subscription.save() contrib = Contribution.objects.create( success=True, tx_cleared=True, tx_override=True, tx_id=tx_id, subscription=subscription, validator_passed=True, validator_comment=validator_comment, ) print(f"ingested {subscription.pk} / {contrib.pk}") if is_real_payout: match.payout_contribution = contrib else: match.test_payout_contribution = contrib match.save() metadata = { 'id': subscription.id, 'value_in_token': str(subscription.amount_per_period), 'value_in_usdt_now': str(round(subscription.amount_per_period_usdt,2)), 'token_name': subscription.token_symbol, 'title': subscription.grant.title, 'grant_url': subscription.grant.url, 'num_tx_approved': subscription.num_tx_approved, 'category': 'grant', } kwargs = { 'profile': profile, 'subscription': subscription, 'grant': subscription.grant, 'activity_type': 'new_grant_contribution', 'metadata': metadata, } activity = Activity.objects.create(**kwargs) if is_real_payout: comment = f"CLR Round {clr_round} Payout" comment = Comment.objects.create(profile=profile, activity=activity, comment=comment) print("SLEEPING") time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) print("DONE SLEEPING")
def redeem_bulk_kudos(self, kt_id, retry=False): """ :param self: :param kt_id: :return: """ try: with redis.lock("tasks:redeem_bulk_kudos:%s" % kt_id, timeout=LOCK_TIMEOUT): multiplier = 1 # high gas prices, 5 hour gas limit - DL gas_price = int( float(recommend_min_gas_price_to_confirm_in_time(300)) * multiplier) if gas_price > delay_if_gas_prices_gt_redeem: # do not retry is gas prices are too high # TODO: revisit this when gas prices go down # self.retry(countdown=60*10) return obj = KudosTransfer.objects.get(pk=kt_id) w3 = get_web3(obj.network) token = obj.kudos_token_cloned_from if token.owner_address.lower( ) != '0x6239FF1040E412491557a7a02b2CBcC5aE85dc8F'.lower(): raise Exception( "kudos isnt owned by Gitcoin; cowardly refusing to spend Gitcoin's ETH minting it" ) kudos_owner_address = settings.KUDOS_OWNER_ACCOUNT kudos_owner_address = Web3.toChecksumAddress(kudos_owner_address) kudos_contract_address = Web3.toChecksumAddress( settings.KUDOS_CONTRACT_MAINNET) contract = w3.eth.contract( Web3.toChecksumAddress(kudos_contract_address), abi=kudos_abi()) nonce = w3.eth.getTransactionCount(kudos_owner_address) tx = contract.functions.clone( Web3.toChecksumAddress(obj.receive_address), token.token_id, 1).buildTransaction({ 'nonce': nonce, 'gas': 500000, 'gasPrice': gas_price, 'value': int(token.price_finney / 1000.0 * 10**18), }) private_key = settings.KUDOS_PRIVATE_KEY signed = w3.eth.account.signTransaction(tx, private_key) obj.txid = w3.eth.sendRawTransaction(signed.rawTransaction).hex() obj.receive_txid = obj.txid obj.save() while not has_tx_mined(obj.txid, obj.network): time.sleep(1) pass except (SoftTimeLimitExceeded, TimeLimitExceeded): print('max timeout for bulk kudos redeem exceeded ... giving up!') except Exception as e: print(e) if self.request.retries < self.max_retries: self.retry(countdown=(30 * (self.request.retries + 1))) else: print("max retries for bulk kudos redeem exceeded ... giving up!")
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()
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)
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)
def handle(self, *args, **options): network = 'mainnet' if not settings.DEBUG else 'rinkeby' actually_send = not settings.DEBUG usernames = options['usernames'].split(",") _amount = options['amount'] DECIMALS = 18 from_address = settings.TIP_PAYOUT_ADDRESS from_pk = settings.TIP_PAYOUT_PRIVATE_KEY from_username = options['from_name'] DAI_ADDRESS = '0x6b175474e89094c44da98b954eedeac495271d0f' if network == 'mainnet' else '0x6a6e8b58dee0ca4b4ee147ad72d3ddd2ef1bf6f7' token_name = 'DAI' # https://gitcoin.co/_administrationperftools/jsonstore/1801078/change/ sybil_attack_addresses = JSONStore.objects.get( key='sybil_attack_addresses').data # payout rankings (round must be finalized first) TOKEN_ADDRESS = DAI_ADDRESS from_profile = Profile.objects.filter( handle=from_username.lower()).first() if not from_profile: print('no from_profile found') return sent_addresses = [] for username in usernames: # issue payment print(f"- issuing payout to {username}") profile = Profile.objects.filter(handle=username.lower()).first() if not profile: print('no profile found') continue if not profile.preferred_payout_address: print('no profile preferred_payout_address found') continue address = profile.preferred_payout_address w3 = get_web3(network) contract = w3.eth.contract(Web3.toChecksumAddress(TOKEN_ADDRESS), abi=abi) address = Web3.toChecksumAddress(address) amount = int(_amount * 10**DECIMALS) tx_id = '0x0' if actually_send: if address.lower() in sent_addresses: print( 'address was already in the sent addresess; skipping due to anti-sybil protections' ) continue if address.lower() in sybil_attack_addresses: print('skipping due to anti-sybil protections') continue sent_addresses.append(address.lower()) tx = contract.functions.transfer( address, amount ).buildTransaction({ 'nonce': w3.eth.getTransactionCount(from_address), 'gas': 60000, '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() if not tx_id: print("cannot pay, did not get a txid") continue print("paid via", tx_id) # wait for tx to clear while not has_tx_mined(tx_id, network): time.sleep(1) metadata = { 'direct_address': profile.preferred_payout_address, 'creation_time': timezone.now().strftime("%Y-%m-%dT%H:00:00"), } # create objects tip = Tip.objects.create( primary_email=profile.email, emails=[profile.email], tokenName='DAI', amount=amount / 10**DECIMALS, comments_priv=options['comments_priv'], comments_public=options['comments_public'], ip="0.0.0.0", expires_date=timezone.now() + timezone.timedelta(days=10), github_url='', from_name=from_username, from_email=from_profile.email, from_username=from_username, username=profile.handle, network=network, tokenAddress=TOKEN_ADDRESS, from_address=from_address, is_for_bounty_fulfiller=False, metadata=metadata, recipient_profile=profile, sender_profile=from_profile, tx_status='pending', txid=tx_id, receive_tx_status='pending', receive_txid=tx_id, receive_address=profile.preferred_payout_address, tx_time=timezone.now(), receive_tx_time=timezone.now(), received_on=timezone.now(), ) tip.trigger_townsquare() maybe_market_tip_to_github(tip) maybe_market_tip_to_slack(tip, 'New tip') if tip.primary_email: maybe_market_tip_to_email(tip, [tip.primary_email]) record_user_action(tip.from_username, 'send_tip', tip) record_tip_activity(tip, tip.from_username, 'new_tip' if tip.username else 'new_crowdfund', False, tip.username) TipPayout.objects.create( txid=tx_id, profile=profile, tip=tip, ) print("SLEEPING") time.sleep(WAIT_TIME_BETWEEN_PAYOUTS) print("DONE SLEEPING")
def is_verified(tx_details, tx_hash, tx_amount, network): gitcoin_account = '0x00De4B13153673BCAE2616b67bf822500d325Fc3' return has_tx_mined(tx_hash, network) and\ tx_details.to.lower() == gitcoin_account.lower()
def receive_tip_v2(request, pk, txid, network): """Handle the receiving of a tip (the POST) Returns: TemplateResponse: the UI with the tip confirmed """ tip = Tip.objects.get(web3_type='v2', metadata__priv_key=pk, txid=txid, network=network) if tip.receive_txid: messages.info(request, 'This tip has already been received') not_mined_yet = not has_tx_mined(tip.txid, tip.network) if not_mined_yet: messages.info( request, f'This tx {tip.txid}, is still mining. Please wait a moment before submitting the receive form.' ) """Receive a tip.""" if request.POST and not tip.receive_txid: params = request.POST # db mutations try: address = params['forwarding_address'] if not address or address == '0x0': raise Exception('bad forwarding address') # send tokens address = Web3.toChecksumAddress(address) w3 = get_web3(tip.network) is_erc20 = tip.tokenName.lower() != 'eth' amount = int(tip.amount_in_wei) gasPrice = recommend_min_gas_price_to_confirm_in_time(25) * 10**9 from_address = Web3.toChecksumAddress(tip.metadata['address']) nonce = w3.eth.getTransactionCount(from_address) if is_erc20: # ERC20 contract receive balance = w3.eth.getBalance(from_address) contract = w3.eth.contract(Web3.toChecksumAddress( tip.tokenAddress), abi=erc20_abi) gas = contract.functions.transfer(address, amount).estimateGas() + 1 gasPrice = gasPrice if ((gas * gasPrice) < balance) else ( balance * 1.0 / gas) tx = contract.functions.transfer(address, amount).buildTransaction({ 'nonce': nonce, 'gas': w3.toHex(gas), 'gasPrice': w3.toHex(int(gasPrice)), }) else: # ERC20 contract receive gas = 100000 amount -= gas * int(gasPrice) tx = dict( nonce=nonce, gasPrice=w3.toHex(int(gasPrice)), gas=w3.toHex(gas), to=address, value=w3.toHex(amount), data=b'', ) signed = w3.eth.account.signTransaction(tx, tip.metadata['priv_key']) receive_txid = w3.eth.sendRawTransaction( signed.rawTransaction).hex() tip.receive_address = params['forwarding_address'] tip.receive_txid = receive_txid tip.received_on = timezone.now() tip.save() record_user_action(tip.username, 'receive_tip', tip) record_tip_activity(tip, tip.username, 'receive_tip') messages.success(request, 'This tip has been received') except Exception as e: messages.error(request, str(e)) logger.exception(e) params = { 'issueURL': request.GET.get('source'), 'class': 'receive', 'title': _('Receive Tip'), 'gas_price': round( recommend_min_gas_price_to_confirm_in_time( confirm_time_minutes_target), 1), 'tip': tip, 'disable_inputs': tip.receive_txid or not_mined_yet, } return TemplateResponse(request, 'onepager/receive.html', params)