Skip to content

Commit

Permalink
GITC-394: polygon tx validation (#9475)
Browse files Browse the repository at this point in the history
* update admin + checkout type

* update get_web3

* handle polygon tx validation

* fix travis

* check fix

* get_tx_status fix import

* bug fix get_web3

* pass polygon boolean

* set infura id env for polygon

* check fix

* try maticvigil

* revert to infura

* use chain over is_polygon

* refac
  • Loading branch information
chibie authored Sep 22, 2021
1 parent 2efa4a6 commit f12cde4
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 34 deletions.
19 changes: 12 additions & 7 deletions app/dashboard/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ def ipfs_cat_requests(key):
return None, 500


def get_web3(network, sockets=False):
def get_web3(network, sockets=False, chain='std'):
"""Get a Web3 session for the provided network.
Attributes:
Expand All @@ -298,8 +298,13 @@ def get_web3(network, sockets=False):
web3.main.Web3: A web3 instance for the provided network.
"""
if network in ['mainnet', 'rinkeby', 'ropsten']:
if sockets:
if network in ['mainnet', 'rinkeby', 'ropsten', 'testnet']:
if network == 'mainnet' and chain == 'polygon':
network = 'polygon-mainnet'
elif network == 'testnet':
network = 'polygon-mumbai'

if sockets and chain != 'polygon': # polygon doesn't yet have socket support in infura
if settings.INFURA_USE_V3:
provider = WebsocketProvider(f'wss://{network}.infura.io/ws/v3/{settings.INFURA_V3_PROJECT_ID}')
else:
Expand Down Expand Up @@ -925,12 +930,12 @@ def is_valid_eth_address(eth_address):
return (bool(re.match(r"^0x[a-zA-Z0-9]{40}$", eth_address)) or eth_address == "0x0")


def get_tx_status(txid, network, created_on):
status, timestamp, tx = get_tx_status_and_details(txid, network, created_on)
def get_tx_status(txid, network, created_on, chain='std'):
status, timestamp, tx = get_tx_status_and_details(txid, network, created_on, chain=chain)
return status, timestamp


def get_tx_status_and_details(txid, network, created_on):
def get_tx_status_and_details(txid, network, created_on, chain='std'):
from django.utils import timezone

import pytz
Expand All @@ -944,7 +949,7 @@ def get_tx_status_and_details(txid, network, created_on):
if txid == 'override':
return 'success', None #overridden by admin
try:
web3 = get_web3(network)
web3 = get_web3(network, chain=chain)
tx = web3.eth.getTransactionReceipt(txid)
if not tx:
drop_dead_date = created_on + timezone.timedelta(days=DROPPED_DAYS)
Expand Down
36 changes: 23 additions & 13 deletions app/economy/tx.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@
from django.conf import settings
from django.utils import timezone

import requests
from dashboard.abi import erc20_abi
from dashboard.utils import get_tx_status, get_web3
from economy.models import Token
from web3 import Web3
from web3.exceptions import BadFunctionCallOutput
Expand Down Expand Up @@ -107,39 +105,51 @@ def parse_token_amount(token_symbol, amount, network):
parsed_amount = int(amount * 10 ** decimals)
return parsed_amount

def check_for_replaced_tx(tx_hash, network):
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
"""
status, timestamp = get_tx_status(tx_hash, network, timezone.now())
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, timezone.now())
status, timestamp = get_tx_status(tx_hash, network, datetime)

return tx_hash, status, timestamp


def grants_transaction_validator(contribution, w3):
def grants_transaction_validator(contribution, w3, chain='std'):
"""
This function is used to validate contributions sent on L1 through the BulkCheckout contract.
This function is used to validate contributions sent on L1 & Polygon L2 through the BulkCheckout contract.
This contract can be found here:
- On GitHub: https://github.com/gitcoinco/BulkTransactions/blob/master/contracts/BulkCheckout.sol
- On mainnet: https://etherscan.io/address/0x7d655c57f71464b6f83811c55d84009cd9f5221c
- On Polygon mainnet: https://polygonscan.com/address/0xb99080b9407436eBb2b8Fe56D45fFA47E9bb8877
- On Polygon testnet: https://mumbai.polygonscan.com/address/0x3E2849E2A489C8fE47F52847c42aF2E8A82B9973
To facilitate testing on Rinkeby, we pass in a web3 instance instead of using the mainnet
To facilitate testing on Rinkeby and Mumbai, we pass in a web3 instance instead of using the mainnet
instance defined at the top of this file
"""

# Get bulk checkout contract instance
bulk_checkout_contract = w3.eth.contract(address=bulk_checkout_address, abi=bulk_checkout_abi)

# Get specific info about this contribution that we use later
tx_hash = contribution.split_tx_id
network = contribution.subscription.network

if network == 'mainnet' and chain == 'polygon':
bulk_checkout_address = '0xb99080b9407436eBb2b8Fe56D45fFA47E9bb8877'
elif network == 'testnet':
bulk_checkout_address = '0x3E2849E2A489C8fE47F52847c42aF2E8A82B9973'

# Get bulk checkout contract instance
bulk_checkout_contract = w3.eth.contract(address=bulk_checkout_address, abi=bulk_checkout_abi)

# Response that calling function uses to set fields on Contribution. Set the defaults here
response = {
# We set `passed` to `True` if matching transfer is found for this contribution. The
Expand Down Expand Up @@ -168,7 +178,7 @@ def grants_transaction_validator(contribution, w3):
return response

# Check for dropped and replaced txn
tx_hash, status, timestamp = check_for_replaced_tx(tx_hash, network)
tx_hash, status, _ = check_for_replaced_tx(tx_hash, network, chain=chain)

# If transaction was successful, continue to validate it
if status == 'success':
Expand Down Expand Up @@ -220,7 +230,7 @@ def grants_transaction_validator(contribution, w3):
is_correct_token = event['args']['token'].lower() == expected_token

transfer_amount = event['args']['amount']
is_correct_amount = transfer_amount > expected_amount_min and transfer_amount < expected_amount_max
is_correct_amount = transfer_amount >= expected_amount_min and transfer_amount <= expected_amount_max

if is_correct_recipient and is_correct_token and is_correct_amount:
# We found the event log corresponding to the contribution parameters
Expand Down
31 changes: 17 additions & 14 deletions app/grants/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import requests
from django_extensions.db.fields import AutoSlugField
from economy.models import SuperModel
from economy.tx import check_for_replaced_tx
from economy.utils import ConversionRateNotFoundError, convert_amount
from gas.utils import eth_usd_conv_rate, recommend_min_gas_price_to_confirm_in_time
from grants.utils import generate_collection_thumbnail, get_upload_filename, is_grant_team_member
Expand Down Expand Up @@ -1681,6 +1682,7 @@ class Contribution(SuperModel):
CHECKOUT_TYPES = [
('eth_std', 'eth_std'),
('eth_zksync', 'eth_zksync'),
('eth_polygon', 'eth_polygon'),
('zcash_std', 'zcash_std'),
('celo_std', 'celo_std'),
('zil_std', 'zil_std'),
Expand Down Expand Up @@ -1767,6 +1769,9 @@ def blockexplorer_url_txid(self):
def blockexplorer_url_helper(self, tx_id):
if self.checkout_type == 'eth_zksync':
return f'https://zkscan.io/explorer/transactions/{tx_id.replace("sync-tx:", "")}'
if self.checkout_type == 'eth_polygon':
network_sub = f"mumbai." if self.subscription and self.subscription.network != 'mainnet' else ''
return f'https://{network_sub}polygonscan.com/tx/{tx_id}'
if self.checkout_type == 'eth_std':
network_sub = f"{self.subscription.network}." if self.subscription and self.subscription.network != 'mainnet' else ''
return f'https://{network_sub}etherscan.io/tx/{tx_id}'
Expand Down Expand Up @@ -1808,17 +1813,16 @@ def leave_gitcoinbot_comment_for_status(self, status):
"comment":comment,
"is_edited":True,
}
);
)
except Exception as e:
print(e)


def update_tx_status(self):
"""Updates tx status for Ethereum contributions."""
try:
from dashboard.utils import get_web3
from economy.tx import grants_transaction_validator
from dashboard.utils import get_tx_status
from economy.tx import getReplacedTX

# If `tx_override` is True, we don't run the validator for this contribution
if self.tx_override:
Expand Down Expand Up @@ -1849,20 +1853,19 @@ def update_tx_status(self):
self.tx_cleared = True
self.validator_comment = "zkSync checkout. Success" if self.success else f"zkSync Checkout. {tx_data['fail_reason']}"

elif self.checkout_type == 'eth_std':
# Standard L1 checkout using the BulkCheckout contract
elif self.checkout_type == 'eth_std' or self.checkout_type == 'eth_polygon':
# Standard L1 and sidechain L2 checkout using the BulkCheckout contract

# get active chain std/polygon
chain = self.checkout_type.split('_')[-1]

# Prepare web3 provider
PROVIDER = "wss://" + network + ".infura.io/ws/v3/" + settings.INFURA_V3_PROJECT_ID
w3 = Web3(Web3.WebsocketProvider(PROVIDER))
w3 = get_web3(network, chain=chain)

# Handle dropped/replaced transactions
split_tx_status, _ = get_tx_status(self.split_tx_id, self.subscription.network, self.created_on)
if split_tx_status in ['pending', 'dropped', 'unknown', '']:
new_tx = getReplacedTX(self.split_tx_id)
if new_tx:
self.split_tx_id = new_tx
split_tx_status, _ = get_tx_status(self.split_tx_id, self.subscription.network, self.created_on)
_, split_tx_status, _ = check_for_replaced_tx(
self.split_tx_id, network, self.created_on, chain=chain
)

# Handle pending txns
if split_tx_status in ['pending']:
Expand All @@ -1888,7 +1891,7 @@ def update_tx_status(self):
return

# Validate that the token transfers occurred
response = grants_transaction_validator(self, w3)
response = grants_transaction_validator(self, w3, chain=chain)
if len(response['originator']):
self.originated_address = response['originator'][0]
self.validator_passed = response['validation']['passed']
Expand Down

0 comments on commit f12cde4

Please sign in to comment.