From aada1bfff8c009f09c42fb017e491b8f94fdf361 Mon Sep 17 00:00:00 2001 From: Martin Derouet Date: Sun, 3 Mar 2024 14:40:31 +0100 Subject: [PATCH 1/4] RMM Property Retrieval Proposal --- finasync/constants.py | 1 + finasync/realt.py | 52 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/finasync/constants.py b/finasync/constants.py index c1338b2..4f56bd3 100644 --- a/finasync/constants.py +++ b/finasync/constants.py @@ -1,6 +1,7 @@ GNOSIS_API_TOKENLIST_URI = ( "https://blockscout.com/xdai/mainnet/api?module=account&action=tokenlist&address=" ) +GRAPH_API_TOKENLIST_URI = "https://api.studio.thegraph.com/query/48997/rmm-v3-wrapper-gnosis/version/latest" REALT_API_TOKENLIST_URI = "https://api.realt.community/v1/token" REALT_OFFLINE_TOKENS_LIST = "RealT_OfflineTokensList.json" REALT_OFFLINE_TOKENS_LIST_FREE = "RealT_OfflineTokensList_Free.json" diff --git a/finasync/realt.py b/finasync/realt.py index 3eb5ff8..4eaf929 100644 --- a/finasync/realt.py +++ b/finasync/realt.py @@ -28,6 +28,7 @@ from .constants import ( GNOSIS_API_TOKENLIST_URI, + GRAPH_API_TOKENLIST_URI, REALT_API_TOKENLIST_URI, REALT_OFFLINE_TOKENS_LIST, REALT_OFFLINE_TOKENS_LIST_FREE, @@ -135,7 +136,58 @@ def get_realt_rentals_finary(session: requests.Session): return json.dumps(myFinary_realT) +def get_realt_rentals_rmm(wallet_address): + if not GRAPH_API_TOKENLIST_URI: + logging.error("The GRAPH_API_TOKENLIST_URI environment variable is not set or is empty.") + return [] + + query = """ + query getUserBalance($userAddress: String!) { + userRealTokens(where: {user: $userAddress}, first: 1000) { + id + amount + token { + symbol + address + } + } + } + """ + + payload = { + "query": query.strip(), + "variables": {"userAddress": wallet_address} + } + + headers = {'Content-Type': 'application/json'} + + try: + response = requests.post(GRAPH_API_TOKENLIST_URI, headers=headers, data=json.dumps(payload)) + response.raise_for_status() + + response_json = response.json() + if 'errors' in response_json: + print('GraphQL errors:', response_json['errors']) + return [] + + return response_json.get('data', {}).get('userRealTokens', []) + + except requests.exceptions.HTTPError as e: + print('HTTP request failed:', e) + except requests.exceptions.RequestException as e: + print('Request exception:', e) + except json.JSONDecodeError as e: + print('JSON decode error:', e) + + return [] + def get_realt_rentals_blockchain(wallet_address): + realT_rentals_rmm = get_realt_rentals_rmm(wallet_address) + for token_info in realT_rentals_rmm: + amount_in_ether = int(token_info['amount']) / 1e18 + log_message = f"RMM Property found: {token_info['token']['symbol']}, Amount: {amount_in_ether} tokens" + print(log_message) + myWallet = json.loads(requests.get(GNOSIS_API_TOKENLIST_URI + wallet_address).text) myRealT_rentals = {} logging.debug("My wallet details") From 4e23a6a9b230b7b66be3d8fe90605eda15a279ae Mon Sep 17 00:00:00 2001 From: Martin Derouet Date: Sun, 3 Mar 2024 14:59:45 +0100 Subject: [PATCH 2/4] Improve logging, readability and dollar approximation log --- finasync/realt.py | 70 ++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 31 deletions(-) diff --git a/finasync/realt.py b/finasync/realt.py index 4eaf929..0cb9ba3 100644 --- a/finasync/realt.py +++ b/finasync/realt.py @@ -136,56 +136,64 @@ def get_realt_rentals_finary(session: requests.Session): return json.dumps(myFinary_realT) -def get_realt_rentals_rmm(wallet_address): + +def get_realt_rentals_rmm(wallet_address): if not GRAPH_API_TOKENLIST_URI: logging.error("The GRAPH_API_TOKENLIST_URI environment variable is not set or is empty.") return [] - query = """ - query getUserBalance($userAddress: String!) { - userRealTokens(where: {user: $userAddress}, first: 1000) { - id - amount - token { - symbol - address - } - } - } - """ - payload = { - "query": query.strip(), + "query": get_real_tokens_rmm_by_address_query().strip(), "variables": {"userAddress": wallet_address} } headers = {'Content-Type': 'application/json'} - try: - response = requests.post(GRAPH_API_TOKENLIST_URI, headers=headers, data=json.dumps(payload)) - response.raise_for_status() + with requests.Session() as session: + try: + response = session.post(GRAPH_API_TOKENLIST_URI, headers=headers, json=payload) + response.raise_for_status() + + response_json = response.json() + real_token_data = response_json.get('data', {}).get('userRealTokens') + + if 'errors' in response_json: + logging.error('GraphQL errors: %s', response_json['errors']) + return [] - response_json = response.json() - if 'errors' in response_json: - print('GraphQL errors:', response_json['errors']) - return [] + if real_token_data is None: + logging.error('No userRealTokens field in the response.') + return [] - return response_json.get('data', {}).get('userRealTokens', []) + return real_token_data - except requests.exceptions.HTTPError as e: - print('HTTP request failed:', e) - except requests.exceptions.RequestException as e: - print('Request exception:', e) - except json.JSONDecodeError as e: - print('JSON decode error:', e) + except requests.exceptions.RequestException as e: + logging.exception('Request exception: %s', e) + except json.JSONDecodeError as e: + logging.exception('JSON decode error: %s', e) return [] +def get_real_tokens_rmm_by_address_query(): + return """ + query getUserBalance($userAddress: String!) { + userRealTokens(where: {user: $userAddress}, first: 1000) { + id + amount + token { + symbol + address + } + } + } + """ + def get_realt_rentals_blockchain(wallet_address): realT_rentals_rmm = get_realt_rentals_rmm(wallet_address) for token_info in realT_rentals_rmm: - amount_in_ether = int(token_info['amount']) / 1e18 - log_message = f"RMM Property found: {token_info['token']['symbol']}, Amount: {amount_in_ether} tokens" + realt_token_amount = int(token_info['amount']) / 1e18 + approx_usd_value = realt_token_amount * 50 + log_message = f"RMM Property found: {token_info['token']['symbol']}, Amount: {realt_token_amount} tokens, Approximate value: ${approx_usd_value:.2f}" print(log_message) myWallet = json.loads(requests.get(GNOSIS_API_TOKENLIST_URI + wallet_address).text) From 68770b4b5267449175495bb2ec974d4d4616edda Mon Sep 17 00:00:00 2001 From: Martin Derouet Date: Sun, 3 Mar 2024 15:18:34 +0100 Subject: [PATCH 3/4] Get token price and total amount hold --- finasync/realt.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/finasync/realt.py b/finasync/realt.py index 0cb9ba3..0c8871f 100644 --- a/finasync/realt.py +++ b/finasync/realt.py @@ -182,7 +182,9 @@ def get_real_tokens_rmm_by_address_query(): amount token { symbol + name address + price } } } @@ -192,9 +194,14 @@ def get_realt_rentals_blockchain(wallet_address): realT_rentals_rmm = get_realt_rentals_rmm(wallet_address) for token_info in realT_rentals_rmm: realt_token_amount = int(token_info['amount']) / 1e18 - approx_usd_value = realt_token_amount * 50 - log_message = f"RMM Property found: {token_info['token']['symbol']}, Amount: {realt_token_amount} tokens, Approximate value: ${approx_usd_value:.2f}" - print(log_message) + token_price = int(token_info['token']['price']) / 1e8 + total_value = realt_token_amount * token_price + + log_message = f"RMM Property found: {token_info['token']['name']}, " \ + f"Amount: {realt_token_amount} tokens, " \ + f"Price per token: ${token_price:.2f}, " \ + f"Total value: ${total_value:.2f}" + logging.info(log_message) myWallet = json.loads(requests.get(GNOSIS_API_TOKENLIST_URI + wallet_address).text) myRealT_rentals = {} From dfe888e6852c45ae21ba574e69795c810580c0a3 Mon Sep 17 00:00:00 2001 From: Martin Derouet Date: Sun, 3 Mar 2024 19:06:13 +0100 Subject: [PATCH 4/4] Finalize RMM integration --- finasync/realt.py | 40 ++++++++++++++++++++++++++-------------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/finasync/realt.py b/finasync/realt.py index 0c8871f..8542091 100644 --- a/finasync/realt.py +++ b/finasync/realt.py @@ -191,22 +191,25 @@ def get_real_tokens_rmm_by_address_query(): """ def get_realt_rentals_blockchain(wallet_address): - realT_rentals_rmm = get_realt_rentals_rmm(wallet_address) - for token_info in realT_rentals_rmm: + myRealT_rentals = {} + + realt_rentals_rmm = get_realt_rentals_rmm(wallet_address) + for token_info in realt_rentals_rmm: + contract_address = token_info['token']['address'].lower() realt_token_amount = int(token_info['amount']) / 1e18 - token_price = int(token_info['token']['price']) / 1e8 - total_value = realt_token_amount * token_price + symbol = token_info['token']['symbol'] + + myRealT_rentals.update({ + contract_address: { + "name": symbol, + "balance": realt_token_amount, + "contractAddress": contract_address, + } + }) - log_message = f"RMM Property found: {token_info['token']['name']}, " \ - f"Amount: {realt_token_amount} tokens, " \ - f"Price per token: ${token_price:.2f}, " \ - f"Total value: ${total_value:.2f}" - logging.info(log_message) + logging.info(f"Number of properties from RMM: {len(realt_rentals_rmm)}") myWallet = json.loads(requests.get(GNOSIS_API_TOKENLIST_URI + wallet_address).text) - myRealT_rentals = {} - logging.debug("My wallet details") - logging.debug(myWallet) for item in myWallet["result"]: if re.match(r"^REALTOKEN", str(item.get("symbol")), re.IGNORECASE): logging.debug("Updating RealT Token to Finary: " + item["symbol"]) @@ -245,11 +248,20 @@ def get_realt_rentals_blockchain(wallet_address): } } ) - logging.debug("My RealT portfolio from the blockchain") - logging.debug(myRealT_rentals) + + log_realt_rentals_info(myRealT_rentals) return json.dumps(myRealT_rentals) +def log_realt_rentals_info(my_real_t_rentals): + token_count = len(my_real_t_rentals) + logging.info(f"Number of RealT properties: {token_count}") + + total_balance = sum(rental_info["balance"] for rental_info in my_real_t_rentals.values()) + logging.info(f"Total RealT tokens in wallet: {total_balance}") + + portfolio_value = total_balance * 50 # Token price is approximately 50$ + logging.info(f"Approximate portfolio value: ${portfolio_value:.2f}") def get_building_type(realT_propertyType): # building type: house, building, apartment, land, commercial, parking_box, or other