Skip to content

Commit

Permalink
Merge pull request #2055 from ResearchHub/get-transaction-fee-from-ba…
Browse files Browse the repository at this point in the history
…sescan

Get gas price from basescan
  • Loading branch information
koutst authored Dec 23, 2024
2 parents f67c68b + 1d2091a commit ec4f23c
Show file tree
Hide file tree
Showing 9 changed files with 84 additions and 18 deletions.
1 change: 1 addition & 0 deletions keys.sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
SEGMENT_WRITE_KEY = ""

ETHERSCAN_API_KEY = ""
BASESCAN_API_KEY = ""
COIN_GECKO_API_KEY = ""

PERSONA_WEBHOOK_SECRET = ""
Expand Down
1 change: 1 addition & 0 deletions src/config/ci/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@
SEGMENT_WRITE_KEY = os.environ.get("SEGMENT_WRITE_KEY", "")

ETHERSCAN_API_KEY = os.environ.get("ETHERSCAN_API_KEY", "")
BASESCAN_API_KEY = os.environ.get("BASESCAN_API_KEY", "")
COIN_GECKO_API_KEY = os.environ.get("COIN_GECKO_API_KEY", "")
1 change: 1 addition & 0 deletions src/config/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@
SEGMENT_WRITE_KEY = os.environ.get("SEGMENT_WRITE_KEY", "")

ETHERSCAN_API_KEY = os.environ.get("ETHERSCAN_API_KEY", "")
BASESCAN_API_KEY = os.environ.get("BASESCAN_API_KEY", "")
COIN_GECKO_API_KEY = os.environ.get("COIN_GECKO_API_KEY", "")
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Generated by Django 5.1.4 on 2024-12-20 00:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("institution", "0005_alter_institution_associated_institutions_and_more"),
("paper", "0150_remove_authorship_unique_paper_author_and_more"),
("user", "0127_author_user_author_openalex_ids_idx"),
]

operations = [
migrations.RemoveConstraint(
model_name="authorship",
name="paper_authorship_paper_author_unique",
),
migrations.AddConstraint(
model_name="authorship",
constraint=models.UniqueConstraint(
fields=("paper", "author"), name="unique_paper_author"
),
),
]
2 changes: 1 addition & 1 deletion src/reputation/lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ def check_hotwallet():
)
send_email = True

if base_balance_eth < 0.08:
if base_balance_eth < 0.001:
messages.append(
f"ETH is running low in the Base hotwallet: {base_balance_eth:,}"
)
Expand Down
22 changes: 22 additions & 0 deletions src/reputation/migrations/0098_deposit_network.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.4 on 2024-12-20 00:54

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("reputation", "0097_alter_withdrawal_network"),
]

operations = [
migrations.AddField(
model_name="deposit",
name="network",
field=models.CharField(
choices=[("BASE", "Base"), ("ETHEREUM", "Ethereum")],
db_default="ETHEREUM",
max_length=10,
),
),
]
17 changes: 7 additions & 10 deletions src/reputation/tests/test_views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import os
import decimal
import re
import time
from datetime import datetime, timedelta
Expand All @@ -10,10 +10,9 @@
from pytz import utc
from rest_framework.test import APITestCase

from purchase.models import RscExchangeRate
from reputation.distributions import Distribution as Dist
from reputation.distributor import Distributor
from reputation.lib import PendingWithdrawal, gwei_to_eth
from reputation.lib import PendingWithdrawal
from reputation.models import Withdrawal
from reputation.tests.helpers import create_deposit, create_withdrawals
from reputation.views.withdrawal_view import WithdrawalViewSet
Expand Down Expand Up @@ -45,6 +44,10 @@ def setUp(self):
etherscan_matcher = re.compile("https://api.etherscan.io/.*")
self.mocker.get(etherscan_matcher, json={"result": {"SafeGasPrice": "30"}})

# Mock calls to basescan
basescan_matcher = re.compile("https://api.basescan.org/.*")
self.mocker.get(basescan_matcher, json={"result": "0x38a5ef"})

# Mock calls to coingecko
coingecko_matcher = re.compile("https://api.coingecko.com/.*")
self.mocker.get(
Expand Down Expand Up @@ -220,7 +223,6 @@ def test_verified_user_can_rewithdraw_rsc_after_24_hours(self):
"transaction_fee": 15,
},
)

self.assertEqual(response.status_code, 201)

def test_unverified_user_cannot_rewithdraw_rsc_within_14_days(self):
Expand Down Expand Up @@ -665,12 +667,7 @@ def test_base_network_transaction_fee_is_lower(self):
# Base fee should be lower than Ethereum fee
self.assertLess(base_fee, eth_fee)

# Base fee should be reasonable (using 1 gwei gas price)
expected_base_gas_fee = gwei_to_eth(1 * 100000) # 1 gwei * 100000 gas
expected_base_rsc = int(
round(RscExchangeRate.eth_to_rsc(expected_base_gas_fee))
)
self.assertEqual(base_fee, expected_base_rsc)
self.assertEqual(base_fee, decimal.Decimal("0.01"))

"""
Helper methods
Expand Down
30 changes: 23 additions & 7 deletions src/reputation/views/withdrawal_view.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import decimal
import logging
import math
import os
from datetime import datetime, timedelta

Expand Down Expand Up @@ -28,15 +29,19 @@
from reputation.models import Withdrawal
from reputation.permissions import AllowWithdrawalIfNotSuspecious
from reputation.serializers import WithdrawalSerializer
from researchhub.settings import ETHERSCAN_API_KEY, WEB3_PROVIDER_URL, WEB3_RSC_ADDRESS
from researchhub.settings import (
BASESCAN_API_KEY,
ETHERSCAN_API_KEY,
WEB3_PROVIDER_URL,
WEB3_RSC_ADDRESS,
)
from user.related_models.user_model import User
from user.related_models.user_verification_model import UserVerification
from user.serializers import UserSerializer
from utils import sentry
from utils.permissions import CreateOrReadOnly, CreateOrUpdateIfAllowed, UserNotSpammer
from utils.throttles import THROTTLE_CLASSES

TRANSACTION_FEE = int(os.environ.get("TRANSACTION_FEE", 100))
NETWORKS = {
"ETHEREUM": {
"provider_url": WEB3_PROVIDER_URL,
Expand Down Expand Up @@ -203,9 +208,17 @@ def calculate_transaction_fee(self, network="ETHEREUM"):
"""

if network == "BASE":
# Base network typically has lower fees
gas_price = 1 # 1 gwei is usually sufficient for Base
gas_limit = 100000.0 # Lower gas limit for Base
# Get gas price from Basescan API
res = requests.get(
f"https://api.basescan.org/api"
f"?module=proxy"
f"&action=eth_gasPrice"
f"&apikey={BASESCAN_API_KEY}",
timeout=10,
)
json = res.json()
gas_price_wei = int(json.get("result", "0x0"), 16) # Convert hex to int
gas_price = gas_price_wei / 10**9 # Convert wei to gwei
else:
# For Ethereum network
res = requests.get(
Expand All @@ -214,11 +227,14 @@ def calculate_transaction_fee(self, network="ETHEREUM"):
)
json = res.json()
gas_price = json.get("result", {}).get("SafeGasPrice", 40)
gas_limit = 120000.0

gas_limit = (
120000.0 # Maximum amount of gas we are willing to consume on a transaction
)

gas_fee_in_eth = gwei_to_eth(float(gas_price) * gas_limit)
rsc = RscExchangeRate.eth_to_rsc(gas_fee_in_eth)
return int(round(rsc))
return decimal.Decimal(str(math.ceil(rsc * 100) / 100))

# 5 minute cache
@method_decorator(cache_page(60 * 5))
Expand Down
3 changes: 3 additions & 0 deletions src/researchhub/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -859,6 +859,9 @@ def before_send(event, hint):
# Etherscan API Key
ETHERSCAN_API_KEY = os.environ.get("ETHERSCAN_API_KEY", keys.ETHERSCAN_API_KEY)

# Basescan API Key
BASESCAN_API_KEY = os.environ.get("BASESCAN_API_KEY", keys.BASESCAN_API_KEY)

# CoinGecko API Key
COIN_GECKO_API_KEY = os.environ.get("COIN_GECKO_API_KEY", keys.COIN_GECKO_API_KEY)

Expand Down

0 comments on commit ec4f23c

Please sign in to comment.