Skip to content

Commit

Permalink
change gastap claimstrategy | change global settings fields
Browse files Browse the repository at this point in the history
  • Loading branch information
Mohamad Bastin committed Oct 27, 2023
1 parent 1f5b86f commit f8e8752
Show file tree
Hide file tree
Showing 25 changed files with 719 additions and 649 deletions.
90 changes: 48 additions & 42 deletions core/utils.py
Original file line number Diff line number Diff line change
@@ -1,57 +1,64 @@
import datetime

import pytz
from time import time
from web3 import Web3
from web3.middleware import geth_poa_middleware
from web3.contract.contract import Contract, ContractFunction
from web3.types import Type, TxParams
from django.utils import timezone
from web3.middleware import geth_poa_middleware
from web3.types import TxParams, Type


class TimeUtils:
@staticmethod
def get_last_monday():
now = int(time())
day = 86400 # seconds in a day
week = 7 * day
weeks = now // week # number of weeks since epoch
monday = 345600 # first monday midnight
last_monday_midnight = monday + (weeks * week)

# last monday could be off by one week
if last_monday_midnight > now:
last_monday_midnight -= week

return timezone.make_aware(
datetime.datetime.fromtimestamp(last_monday_midnight)
)

@staticmethod
def get_second_last_monday():
now = int(time())
day = 86400 # seconds in a day
week = 7 * day
weeks = now // week # number of weeks since epoch
monday = 345600 # first monday midnight
last_monday_midnight = monday + (weeks * week)

# last monday could be off by one week
if last_monday_midnight > now:
last_monday_midnight -= week

return timezone.make_aware(
datetime.datetime.fromtimestamp(last_monday_midnight - week)
)
# @staticmethod
# def get_last_monday():
# now = int(time())
# day = 86400 # seconds in a day
# week = 7 * day
# weeks = now // week # number of weeks since epoch
# monday = 345600 # first monday midnight
# last_monday_midnight = monday + (weeks * week)

# # last monday could be off by one week
# if last_monday_midnight > now:
# last_monday_midnight -= week

# return timezone.make_aware(
# datetime.datetime.fromtimestamp(last_monday_midnight)
# )

# @staticmethod
# def get_second_last_monday():
# now = int(time())
# day = 86400 # seconds in a day
# week = 7 * day
# weeks = now // week # number of weeks since epoch
# monday = 345600 # first monday midnight
# last_monday_midnight = monday + (weeks * week)

# # last monday could be off by one week
# if last_monday_midnight > now:
# last_monday_midnight -= week

# return timezone.make_aware(
# datetime.datetime.fromtimestamp(last_monday_midnight - week)
# )

@staticmethod
def get_first_day_of_the_month():
now = datetime.datetime.now(pytz.timezone("UTC"))
first_day = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
return first_day

@staticmethod
def get_first_day_of_last_month():
now = datetime.datetime.now(pytz.timezone("UTC"))
first_day = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
last_month = first_day - datetime.timedelta(days=1)
first_day_of_last_month = last_month.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
return first_day_of_last_month


class Web3Utils:
def __init__(self, rpc_url, poa = False) -> None:
def __init__(self, rpc_url, poa=False) -> None:
self._rpc_url = rpc_url
self._w3 = None
self._account = None
Expand All @@ -71,7 +78,7 @@ def w3(self) -> Web3:
return self._w3

raise Exception(f"RPC provider is not connected ({self._rpc_url})")

@property
def poa(self):
return self._poa
Expand Down Expand Up @@ -102,8 +109,7 @@ def contract_call(self, func: Type[ContractFunction], from_address=None):

def build_contract_txn(self, func: Type[ContractFunction]):
nonce = self.w3.eth.get_transaction_count(self.account.address)
tx_data = func.build_transaction(
{"from": self.account.address, "nonce": nonce})
tx_data = func.build_transaction({"from": self.account.address, "nonce": nonce})
return self.sign_tx(tx_data)

def sign_tx(self, tx_data: TxParams):
Expand All @@ -117,6 +123,6 @@ def wait_for_transaction_receipt(self, tx_hash):

def current_block(self):
return self.w3.eth.block_number

def get_transaction_by_hash(self, hash):
return self.w3.eth.get_transaction(hash)
35 changes: 20 additions & 15 deletions faucet/constraints.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import logging

import requests
from core.constraints import *

from core.constraints import ConstraintParam, ConstraintVerification
from core.utils import Web3Utils
from faucet.faucet_manager.credit_strategy import WeeklyCreditStrategy
from .models import DonationReceipt, Chain, ClaimReceipt
from faucet.faucet_manager.credit_strategy import RoundCreditStrategy

from .models import Chain, ClaimReceipt, DonationReceipt


class DonationConstraint(ConstraintVerification):
Expand All @@ -24,7 +28,8 @@ class OptimismDonationConstraint(DonationConstraint):
def is_observed(self, *args, **kwargs):
try:
chain = Chain.objects.get(chain_id=10)
except:
except Exception as e:
logging.error(e)
return False
self._param_values[ConstraintParam.CHAIN] = chain.pk
return super().is_observed(*args, **kwargs)
Expand All @@ -38,12 +43,11 @@ def is_observed(self, *args, **kwargs):
chain = Chain.objects.get(pk=chain_pk)
w3 = Web3Utils(chain.rpc_url_private, chain.poa)
current_block = w3.current_block()
user_address = self.user_profile.wallets.get(
wallet_type=chain.chain_type
).address
user_address = self.user_profile.wallets.get(wallet_type=chain.chain_type).address

first_internal_tx = requests.get(
f"{chain.explorer_api_url}/api?module=account&action=txlistinternal&address={user_address}&startblock=0&endblock={current_block}&page=1&offset=1&sort=asc&apikey={chain.explorer_api_key}"
f"{chain.explorer_api_url}/api?module=account&action=txlistinternal&address={user_address}&start"
f"block=0&endblock={current_block}&page=1&offset=1&sort=asc&apikey={chain.explorer_api_key}"
)
first_internal_tx = first_internal_tx.json()
if first_internal_tx and first_internal_tx["status"] == "1":
Expand All @@ -54,16 +58,15 @@ def is_observed(self, *args, **kwargs):
and first_internal_tx["isError"] == "0"
):
first_tx = requests.get(
f"{chain.explorer_api_url}/api?module=account&action=txlist&address={user_address}&startblock=0&endblock={current_block}&page=1&offset=1&sort=asc&apikey={chain.explorer_api_key}"
f"{chain.explorer_api_url}/api?module=account&action=txlist&address={user_address}&startblock=0&"
f"endblock={current_block}&page=1&offset=1&sort=asc&apikey={chain.explorer_api_key}"
)
first_tx = first_tx.json()
if first_tx:
if not first_tx["result"]:
return True
first_tx = first_tx["result"][0]
claiming_gas_tx = w3.get_transaction_by_hash(
first_internal_tx["hash"]
)
claiming_gas_tx = w3.get_transaction_by_hash(first_internal_tx["hash"])
web3_first_tx = w3.get_transaction_by_hash(first_tx["hash"])
return web3_first_tx["blockNumber"] > claiming_gas_tx["blockNumber"]
return False
Expand All @@ -75,7 +78,8 @@ class OptimismClaimingGasConstraint(EvmClaimingGasConstraint):
def is_observed(self, *args, **kwargs):
try:
chain = Chain.objects.get(chain_id=10)
except:
except Exception as e:
logging.error(e)
return False
self._param_values[ConstraintParam.CHAIN] = chain.pk
return super().is_observed(*args, **kwargs)
Expand All @@ -91,7 +95,7 @@ def is_observed(self, *args, **kwargs):
user_profile=self.user_profile,
chain=chain,
_status=ClaimReceipt.VERIFIED,
datetime__gte=WeeklyCreditStrategy.get_last_monday(),
datetime__gte=RoundCreditStrategy.get_start_of_the_round(),
).exists()


Expand All @@ -101,7 +105,8 @@ class OptimismHasClaimedGasInThisRound(HasClaimedGasInThisRound):
def is_observed(self, *args, **kwargs):
try:
chain = Chain.objects.get(chain_id=10)
except:
except Exception as e:
logging.error(e)
return False
self._param_values[ConstraintParam.CHAIN] = chain.pk
return super().is_observed(*args, **kwargs)
68 changes: 34 additions & 34 deletions faucet/faucet_manager/brightid_user_registry.py
Original file line number Diff line number Diff line change
@@ -1,40 +1,40 @@
from eth_account.signers.local import LocalAccount
from web3 import Web3
from web3.exceptions import TimeExhausted
from web3.gas_strategies.rpc import rpc_gas_price_strategy
from web3.middleware import geth_poa_middleware
from faucet.faucet_manager.brightid_user_registry_abi import bright_id_user_registry_abi
from faucet.models import Chain, BrightUser
# from eth_account.signers.local import LocalAccount
# from web3 import Web3
# from web3.exceptions import TimeExhausted
# from web3.gas_strategies.rpc import rpc_gas_price_strategy
# from web3.middleware import geth_poa_middleware
# from faucet.faucet_manager.brightid_user_registry_abi import bright_id_user_registry_abi
# from faucet.models import Chain, BrightUser


class BrightIdUserRegistry:
def __init__(self, chain: Chain, bright_id_user_registry_address: str):
self.chain = chain
self.abi = bright_id_user_registry_abi
self.bright_id_user_registry_address = bright_id_user_registry_address
# class BrightIdUserRegistry:
# def __init__(self, chain: Chain, bright_id_user_registry_address: str):
# self.chain = chain
# self.abi = bright_id_user_registry_abi
# self.bright_id_user_registry_address = bright_id_user_registry_address

@property
def w3(self) -> Web3:
assert self.chain.rpc_url_private is not None
_w3 = Web3(Web3.HTTPProvider(self.chain.rpc_url_private))
if self.chain.poa:
_w3.middleware_onion.inject(geth_poa_middleware, layer=0)
if _w3.isConnected():
_w3.eth.set_gas_price_strategy(rpc_gas_price_strategy)
return _w3
raise Exception(f"Could not connect to rpc {self.chain.rpc_url_private}")
# @property
# def w3(self) -> Web3:
# assert self.chain.rpc_url_private is not None
# _w3 = Web3(Web3.HTTPProvider(self.chain.rpc_url_private))
# if self.chain.poa:
# _w3.middleware_onion.inject(geth_poa_middleware, layer=0)
# if _w3.isConnected():
# _w3.eth.set_gas_price_strategy(rpc_gas_price_strategy)
# return _w3
# raise Exception(f"Could not connect to rpc {self.chain.rpc_url_private}")

@property
def contract(self):
return self.w3.eth.contract(
address=self.get_checksum_address(self.bright_id_user_registry_address),
abi=self.abi,
)
# @property
# def contract(self):
# return self.w3.eth.contract(
# address=self.get_checksum_address(self.bright_id_user_registry_address),
# abi=self.abi,
# )

def get_checksum_address(self, address):
return Web3.toChecksumAddress(address.lower())
# def get_checksum_address(self, address):
# return Web3.toChecksumAddress(address.lower())

def is_verified_user(self, address):
return self.contract.functions.isVerifiedUser(
self.get_checksum_address(address)
).call()
# def is_verified_user(self, address):
# return self.contract.functions.isVerifiedUser(
# self.get_checksum_address(address)
# ).call()
44 changes: 21 additions & 23 deletions faucet/faucet_manager/claim_manager.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import logging
import abc
import logging
from abc import ABC

from django.db import transaction
from django.utils import timezone
from authentication.models import UserProfile
from authentication.models import NetworkTypes
from faucet.faucet_manager.lnpay_client import LNPayClient

from authentication.models import NetworkTypes, UserProfile
from faucet.faucet_manager.credit_strategy import (
CreditStrategy,
CreditStrategyFactory,
WeeklyCreditStrategy,
RoundCreditStrategy,
)
from faucet.faucet_manager.fund_manager import EVMFundManager
from faucet.models import ClaimReceipt, BrightUser, GlobalSettings
from django.db import transaction
from faucet.faucet_manager.lnpay_client import LNPayClient
from faucet.models import BrightUser, ClaimReceipt, GlobalSettings


class ClaimManager(ABC):
Expand All @@ -36,18 +36,15 @@ def fund_manager(self):

def claim(self, amount, passive_address=None):
with transaction.atomic():
user_profile = UserProfile.objects.select_for_update().get(
pk=self.credit_strategy.user_profile.pk
)
user_profile = UserProfile.objects.select_for_update().get(pk=self.credit_strategy.user_profile.pk)
self.assert_pre_claim_conditions(amount, user_profile)
return self.create_pending_claim_receipt(
amount, passive_address
) # all pending claims will be processed periodically

def assert_pre_claim_conditions(self, amount, user_profile):
assert amount <= self.credit_strategy.get_unclaimed()
# TODO: uncomment this
assert self.user_is_meet_verified() == True
assert self.user_is_meet_verified() is True
assert not ClaimReceipt.objects.filter(
chain=self.credit_strategy.chain,
user_profile=user_profile,
Expand All @@ -72,13 +69,13 @@ def user_is_meet_verified(self) -> bool:


class LimitedChainClaimManager(SimpleClaimManager):
def get_weekly_limit(self):
limit = GlobalSettings.objects.first().weekly_chain_claim_limit
def get_round_limit(self):
limit = GlobalSettings.objects.first().gastap_round_claim_limit
return limit

@staticmethod
def get_total_weekly_claims(user_profile):
last_monday = WeeklyCreditStrategy.get_last_monday()
def get_total_round_claims(user_profile):
start_of_the_round = RoundCreditStrategy.get_start_of_the_round()
return ClaimReceipt.objects.filter(
user_profile=user_profile,
_status__in=[
Expand All @@ -87,27 +84,28 @@ def get_total_weekly_claims(user_profile):
BrightUser.PENDING,
BrightUser.VERIFIED,
],
datetime__gte=last_monday,
datetime__gte=start_of_the_round,
).count()

def assert_pre_claim_conditions(self, amount, user_profile):
super().assert_pre_claim_conditions(amount, user_profile)
total_claims = self.get_total_weekly_claims(user_profile)
assert total_claims < self.get_weekly_limit()
total_claims = self.get_total_round_claims(user_profile)
assert total_claims < self.get_round_limit()


class LightningClaimManger(LimitedChainClaimManager):
def claim(self, amount, passive_address):
try:
lnpay_client = LNPayClient(
self.credit_strategy.chain.rpc_url_private,
self.credit_strategy.chain.wallet.main_key,
self.credit_strategy.chain.fund_manager_address
self.credit_strategy.chain.rpc_url_private,
self.credit_strategy.chain.wallet.main_key,
self.credit_strategy.chain.fund_manager_address,
)
decoded_invoice = lnpay_client.decode_invoice(passive_address)
except Exception as e:
logging.error(e)
raise AssertionError("Could not decode the invoice")
assert int(decoded_invoice['num_satoshis']) == amount, "Invalid amount"
assert int(decoded_invoice["num_satoshis"]) == amount, "Invalid amount"
return super().claim(amount, passive_address)


Expand Down
Loading

0 comments on commit f8e8752

Please sign in to comment.