Skip to content

Commit

Permalink
Merge pull request #54 from ilyarolf/feature/erc20-trc20-tokens
Browse files Browse the repository at this point in the history
Feature/erc20 trc20 tokens
  • Loading branch information
ilyarolf authored Oct 15, 2024
2 parents 95b8a71 + aa93d0e commit c869ee6
Show file tree
Hide file tree
Showing 15 changed files with 388 additions and 136 deletions.
3 changes: 2 additions & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ DB_NAME = ""
NGROK_TOKEN = ""
PAGE_ENTRIES = ""
LANGUAGE = ""
MULTIBOT = ""
MULTIBOT = ""
ETHPLORER_API_KEY = ""
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@
PAGE_ENTRIES = int(os.environ.get("PAGE_ENTRIES"))
LANGUAGE = os.environ.get("LANGUAGE")
MULTIBOT = os.environ.get("MULTIBOT", False) == 'true'
ETHPLORER_API_KEY = os.environ.get("ETHPLORER_API_KEY")
163 changes: 131 additions & 32 deletions crypto_api/CryptoApiManager.py
Original file line number Diff line number Diff line change
@@ -1,59 +1,158 @@
from typing import Any
from datetime import datetime, timedelta

import aiohttp
import grequests

import config
from services.deposit import DepositService


class CryptoApiManager:
def __init__(self, btc_address, ltc_address, trx_address):
def __init__(self, btc_address, ltc_address, trx_address, eth_address, user_id):
self.btc_address = btc_address.strip()
self.ltc_address = ltc_address.strip()
self.trx_address = trx_address.strip()
self.eth_address = eth_address.strip().lower()
self.user_id = user_id
self.min_timestamp = int((datetime.now() - timedelta(hours=24)).timestamp()) * 1000

@staticmethod
async def fetch_api_request(url: str) -> dict:
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
if response.status == 200:
data = await response.json()
return data

async def get_btc_balance(self, deposits) -> float:
url = f'https://mempool.space/api/address/{self.btc_address}/utxo'
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if deposit.network == "BTC"]
deposit_sum = 0.0
for deposit in data:
if deposit["txid"] not in deposits and deposit['status']['confirmed']:
await DepositService.create(deposit['txid'], self.user_id, "BTC", None,
deposit["value"], deposit['vout'])
deposit_sum += float(deposit["value"]) / 100_000_000
return deposit_sum

async def get_ltc_balance(self, deposits) -> float:
url = f"https://api.blockcypher.com/v1/ltc/main/addrs/{self.ltc_address}?unspentOnly=true"
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if deposit.network == "LTC"]
deposits_sum = 0.0
if data['n_tx'] > 0:
for deposit in data['txrefs']:
if deposit["confirmations"] > 0 and deposit['tx_hash'] not in deposits:
await DepositService.create(deposit['tx_hash'], self.user_id, "LTC", None,
deposit["value"], deposit['tx_output_n'])
deposits_sum += float(deposit['value']) / 100_000_000
return deposits_sum

async def get_usdt_trc20_balance(self, deposits) -> float:
url = f"https://api.trongrid.io/v1/accounts/{self.trx_address}/transactions/trc20?only_confirmed=true&min_timestamp={self.min_timestamp}&contract_address=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t&only_to=true"
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if
deposit.network == "TRX" and deposit.token_name == "USDT_TRC20"]
deposits_sum = 0.0
for deposit in data['data']:
if deposit['transaction_id'] not in deposits:
await DepositService.create(deposit['transaction_id'], self.user_id, "TRX",
"USDT_TRC20", deposit['value'])
deposits_sum += float(deposit['value']) / pow(10, deposit['token_info']['decimals'])
return deposits_sum

async def get_usdd_trc20_balance(self, deposits) -> float:
url = f"https://api.trongrid.io/v1/accounts/{self.trx_address}/transactions/trc20?only_confirmed=true&min_timestamp={self.min_timestamp}&contract_address=TPYmHEhy5n8TCEfYGqW2rPxsghSfzghPDn&only_to=true"
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if
deposit.network == "TRX" and deposit.token_name == "USDD_TRC20"]
deposits_sum = 0.0
for deposit in data['data']:
if deposit['transaction_id'] not in deposits:
await DepositService.create(deposit['transaction_id'], self.user_id, "TRX",
"USDD_TRC20", deposit['value'])
deposits_sum += float(deposit['value']) / pow(10, deposit['token_info']['decimals'])
return deposits_sum

async def get_usdt_erc20_balance(self, deposits) -> float:
url = f'https://api.ethplorer.io/getAddressHistory/{self.eth_address}?type=transfer&token=0xdAC17F958D2ee523a2206206994597C13D831ec7&apiKey={config.ETHPLORER_API_KEY}&limit=1000'
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if
deposit.network == "ETH" and deposit.token_name == "USDT_ERC20"]
deposits_sum = 0.0
for deposit in data['operations']:
if deposit['transactionHash'] not in deposits and deposit['to'] == self.eth_address:
await DepositService.create(deposit['transactionHash'], self.user_id, "ETH", "USDT_ERC20",
deposit['value'])
deposits_sum += float(deposit['value']) / pow(10, 6)
return deposits_sum

async def get_usdc_erc20_balance(self, deposits):
url = f'https://api.ethplorer.io/getAddressHistory/{self.eth_address}?type=transfer&token=0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48&apiKey={config.ETHPLORER_API_KEY}&limit=1000'
data = await self.fetch_api_request(url)
deposits = [deposit.tx_id for deposit in deposits if
deposit.network == "ETH" and deposit.token_name == "USDC_ERC20"]
deposits_sum = 0.0
for deposit in data['operations']:
if deposit['transactionHash'] not in deposits and deposit['to'] == self.eth_address:
await DepositService.create(deposit['transactionHash'], self.user_id, "ETH", "USDC_ERC20",
deposit['value'])
deposits_sum += float(deposit['value']) / pow(10, 6)
return deposits_sum

async def get_top_ups(self):
urls = {
"btc_balance": f'https://blockchain.info/rawaddr/{self.btc_address}',
"usdt_balance": f'https://apilist.tronscan.org/api/account?address={self.trx_address}&includeToken=true',
"ltc_balance": f'https://api.blockcypher.com/v1/ltc/main/addrs/{self.ltc_address}'
user_deposits = await DepositService.get_by_user_id(self.user_id)
balances = {"btc__deposit": await self.get_btc_balance(user_deposits),
"ltc__deposit": await self.get_ltc_balance(user_deposits),
"usdt_trc20_deposit": await self.get_usdt_trc20_balance(user_deposits),
"usdd_trc20_deposit": await self.get_usdd_trc20_balance(user_deposits),
"usdt_erc20_deposit": await self.get_usdt_erc20_balance(user_deposits),
"usdc_erc20_deposit": await self.get_usdc_erc20_balance(user_deposits)}
return balances

async def get_top_up_by_crypto_name(self, crypto_name: str):
user_deposits = await DepositService.get_by_user_id(self.user_id)

crypto_functions = {
"BTC": ("btc_deposit", self.get_btc_balance),
"LTC": ("ltc_deposit", self.get_ltc_balance),
"TRX_USDT": ("usdt_trc20_deposit", self.get_usdt_trc20_balance),
"TRX_USDD": ("usdd_trc20_deposit", self.get_usdd_trc20_balance),
"ETH_USDT": ("usdt_erc20_deposit", self.get_usdt_erc20_balance),
"ETH_USDC": ("usdc_erc20_deposit", self.get_usdc_erc20_balance),
}
balances = {}
rs = (grequests.get(url) for url in urls.values())
data_list = grequests.map(rs)

for symbol, data in zip(urls.keys(), data_list):
response_code = data.status_code
if response_code != 200:
balances[symbol] = 0
else:
data = data.json()
if 'total_received' in data:
balance = float(data['total_received']) / 100000000
balances[symbol] = balance
else:
usdt_balance = None
for token in data['trc20token_balances']:
if token['tokenName'] == 'Tether USD':
usdt_balance = round(float(token['balance']) * pow(10, -token['tokenDecimal']), 6)
break
if usdt_balance is not None:
balances[symbol] = usdt_balance
else:
balances[symbol] = 0.0

return balances
if "_" in crypto_name:
base, token = crypto_name.split('_')
else:
base, token = crypto_name, None

key = f"{base}_{token}" if token else base
deposit_name, balance_func = crypto_functions.get(key, (None, None))

if deposit_name and balance_func:
return {deposit_name: await balance_func(user_deposits)}

raise ValueError(f"Unsupported crypto name: {crypto_name}")

@staticmethod
async def get_crypto_prices() -> dict[str, float]:
# TODO("NEED API FOR USDD-TRC-20")
usd_crypto_prices = {}
urls = {
"btc": 'https://api.kraken.com/0/public/Ticker?pair=BTCUSDT',
"usdt": 'https://api.kraken.com/0/public/Ticker?pair=USDTUSD',
"ltc": 'https://api.kraken.com/0/public/Ticker?pair=LTCUSD'
"usdc": "https://api.kraken.com/0/public/Ticker?pair=USDCUSD",
"ltc": 'https://api.kraken.com/0/public/Ticker?pair=LTCUSD',
"eth": 'https://api.kraken.com/0/public/Ticker?pair=ETHUSD',
"trx": "https://api.kraken.com/0/public/Ticker?pair=TRXUSD"
}
responses = (grequests.get(url) for url in urls.values())
datas = grequests.map(responses)
for symbol, data in zip(urls.keys(), datas):
data = data.json()
price = float(next(iter(data['result'].values()))['l'][1])
usd_crypto_prices[symbol] = price
usd_crypto_prices["usdd"] = 1.0 # 1USDD=1USD
return usd_crypto_prices

1 change: 1 addition & 0 deletions db.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from models.buyItem import BuyItem
from models.category import Category
from models.subcategory import Subcategory
from models.deposit import Deposit

url = f"sqlite+aiosqlite:///data/{DB_NAME}"
data_folder = Path("data")
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ services:
PAGE_ENTRIES: 20 # Items per page
LANGUAGE: "en" # The name of your file from the l10n folder without the .json suffix
MULTIBOT: "false" # Allows the use of a multibot
ETHPLORER_API_KEY: "" # API key from Ethplorer
ports:
- "4040:4040"
- "5000:5000" # ${WEBAPP_PORT}:${WEBAPP_PORT}
Expand Down
Loading

0 comments on commit c869ee6

Please sign in to comment.