diff --git a/constants/termmax.py b/constants/termmax.py new file mode 100644 index 00000000..b0d43db8 --- /dev/null +++ b/constants/termmax.py @@ -0,0 +1,19 @@ +from web3 import Web3 + +from constants.chains import Chain +from constants.integration_token import Token + +CHAIN_TO_CONFIG_MAP = { + Chain.ETHEREUM: { + "data_manager_api_origin": "https://data-manager-api.termmax.ts.finance", + "chain_id": "1", + "token_to_config_map": { + Token.SUSDE: { + "address": Web3.to_checksum_address( + "0x9d39a5de30e57443bff2a8307a4256c8797a3497" + ), + "start_block": 22174000, + }, + }, + }, +} diff --git a/integrations/integration_ids.py b/integrations/integration_ids.py index d4cafab4..52ebf2c3 100644 --- a/integrations/integration_ids.py +++ b/integrations/integration_ids.py @@ -81,6 +81,18 @@ class IntegrationID(Enum): Token.SUSDE, ) + # Termmax + TERMMAX_USDE = ( + "termmax_usde", + "Termmax USDe", + Token.USDE, + ) + TERMMAX_SUSDE = ( + "termmax_susde", + "Termmax sUSDe", + Token.SUSDE, + ) + # Stake DAO STAKEDAO_SUSDE_JULY_LPT = ( "stakedao_susde_july_effective_lpt_held", diff --git a/integrations/termmax_susde.py b/integrations/termmax_susde.py new file mode 100644 index 00000000..afc7f2d1 --- /dev/null +++ b/integrations/termmax_susde.py @@ -0,0 +1,125 @@ +import logging +from copy import deepcopy +from typing import Dict, List + +from eth_typing import ChecksumAddress +from web3 import Web3 + +from constants.chains import Chain +from constants.termmax import CHAIN_TO_CONFIG_MAP +from integrations.cached_balances_integration import CachedBalancesIntegration +from integrations.integration_ids import IntegrationID +from utils.request_utils import requests_retry_session + + +class TermmaxCachedBalancesIntegration(CachedBalancesIntegration): + def __init__( + self, + integration_id: IntegrationID, + start_block: int, + chain: Chain, + reward_multiplier: int = 1, + ): + super().__init__( + integration_id=integration_id, + start_block=start_block, + chain=chain, + reward_multiplier=reward_multiplier, + ) + + @property + def _chain_id(self) -> str: + return CHAIN_TO_CONFIG_MAP[self.chain]["chain_id"] + + @property + def _token_address(self) -> ChecksumAddress: + return CHAIN_TO_CONFIG_MAP[self.chain]["token_to_config_map"][ + self.integration_id.value[2] + ]["address"] + + @property + def _data_manager_api_origin(self) -> str: + return CHAIN_TO_CONFIG_MAP[self.chain]["data_manager_api_origin"] + + def get_block_balances( + self, cached_data: Dict[int, Dict[ChecksumAddress, float]], blocks: List[int] + ) -> Dict[int, Dict[ChecksumAddress, float]]: + block_number_to_account_address_to_balance_map = deepcopy(cached_data) + for block in blocks: + if block not in block_number_to_account_address_to_balance_map: + block_number_to_account_address_to_balance_map[block] = ( + self._get_account_address_to_balance_map_for_block( + block_id=f"{block}" + ) + ) + return block_number_to_account_address_to_balance_map + + def _get_account_address_to_balance_map_for_block( + self, block_id: str + ) -> Dict[ChecksumAddress, float]: + logging.info(f"[Termmax integration] [Block {block_id}] Getting balances...") + try: + account_addresses = self._get_account_addresses_for_block(block_id) + if len(account_addresses) == 0: + return {} + response = requests_retry_session().get( + f"{self._data_manager_api_origin}/v1/integrations/ethena/effective_balances", + params={ + "chain_id": self._chain_id, + "block_id": block_id, + "token_addresses": [self._token_address], + "account_addresses": account_addresses, + }, + ) + return { + Web3.to_checksum_address( + effective_balance_dict["account_address"] + ): float(effective_balance_dict["balance"]) + for effective_balance_dict in response.json()["data"][ + "effective_balances" + ] + } + except Exception as e: + logging.error( + f"[Termmax integration] [Block {block_id}] Error getting balances: {e}" + ) + raise e + + def _get_account_addresses_for_block(self, block_id: str) -> List[ChecksumAddress]: + logging.info( + f"[Termmax integration] [Block {block_id}] Getting account addresses..." + ) + try: + response = requests_retry_session().get( + f"{self._data_manager_api_origin}/v1/integrations/ethena/effective_accounts", + params={ + "chain_id": self._chain_id, + "block_id": block_id, + "token_addresses": [self._token_address], + }, + ) + return [ + Web3.to_checksum_address(effective_account_dict["account_address"]) + for effective_account_dict in response.json()["data"][ + "effective_accounts" + ] + ] + except Exception as e: + logging.error( + f"[Termmax integration] [Block {block_id}] Error getting account addresses: {e}" + ) + raise e + + +if __name__ == "__main__": + integration = TermmaxCachedBalancesIntegration( + integration_id=IntegrationID.TERMMAX_SUSDE, + start_block=CHAIN_TO_CONFIG_MAP[Chain.ETHEREUM]["token_to_config_map"][ + IntegrationID.TERMMAX_SUSDE.value[2] + ]["start_block"], + chain=Chain.ETHEREUM, + ) + balances = integration.get_block_balances( + cached_data={}, blocks=[22174000, 22174001, 22174002] + ) + print(balances)