1+ import logging
2+ import subprocess
3+ import json
4+ import requests
5+
6+ from typing import Dict , List , Set
7+ from dotenv import load_dotenv
8+ from constants .summary_columns import SummaryColumn
9+ from constants .example_integrations import (
10+ ECHELON_SUSDE_USDC_XLPT_COLLATERAL_START_BLOCK
11+ )
12+ from constants .echelon import LENDING_CONTRACT_ADDRESS , XLPT_ORACLE_CONTRACT_ADDRESS , SUSDE_USDC_XLPT_MARKET_ADDRESS , SUSDE_USDC_TOKEN_ADDRESS , ETHENA_SUSDE_USDC_XLPT_ADDRESS_API_URL
13+ from constants .chains import Chain
14+ from integrations .integration_ids import IntegrationID as IntID
15+ from integrations .l2_delegation_integration import L2DelegationIntegration
16+
17+ load_dotenv ()
18+
19+ class EchelonAptosIntegration (L2DelegationIntegration ):
20+ def __init__ (
21+ self ,
22+ integration_id : IntID ,
23+ start_block : int ,
24+ token_address : str ,
25+ market_address : str ,
26+ decimals : int ,
27+ chain : Chain = Chain .APTOS ,
28+ reward_multiplier : int = 1 ,
29+ ):
30+ super ().__init__ (
31+ integration_id = integration_id ,
32+ start_block = start_block ,
33+ chain = chain ,
34+ summary_cols = [SummaryColumn .ECHELON_SHARDS ],
35+ reward_multiplier = reward_multiplier ,
36+ )
37+ self .token_address = token_address
38+ self .market_address = market_address
39+ self .decimals = str (decimals )
40+ self .echelon_ts_location = "ts/echelon_xlpt_balances.ts"
41+
42+ def get_l2_block_balances (
43+ self , cached_data : Dict [int , Dict [str , float ]], blocks : List [int ]
44+ ) -> Dict [int , Dict [str , float ]]:
45+ logging .info ("Getting block data for Echelon sUSDe/USDC xLPT Collateral..." )
46+ # Ensure blocks are sorted smallest to largest
47+ block_data : Dict [int , Dict [str , float ]] = {}
48+ sorted_blocks = sorted (blocks )
49+
50+ # Populate block data from smallest to largest
51+ for block in sorted_blocks :
52+ user_addresses = self .get_participants (block )
53+ result = self .get_participants_data (block , user_addresses [0 :20 ])
54+
55+ # Store the balances and cache the exchange rate
56+ block_data [block ] = result
57+
58+ return block_data
59+
60+ def get_participants_data (self , block , user_addresses = []):
61+ print ("Getting participants data for block" , block )
62+ try :
63+ response = subprocess .run (
64+ [
65+ "ts-node" ,
66+ self .echelon_ts_location ,
67+ LENDING_CONTRACT_ADDRESS ,
68+ self .market_address ,
69+ str (self .decimals ),
70+ str (block ),
71+ json .dumps (user_addresses ),
72+ XLPT_ORACLE_CONTRACT_ADDRESS ,
73+ SUSDE_USDC_TOKEN_ADDRESS ,
74+ ],
75+ capture_output = True ,
76+ text = True ,
77+ check = True
78+ )
79+
80+ # Debug output
81+ print ("TypeScript stdout:" , response .stdout )
82+ print ("TypeScript stderr:" , response .stderr )
83+
84+ try :
85+ result = json .loads (response .stdout )
86+ return result # Now returns dict with both balances and exchange rate
87+ except json .JSONDecodeError as e :
88+ print (f"JSON Decode Error: { e } " )
89+ print (f"Raw output: { response .stdout } " )
90+ raise
91+
92+ except subprocess .CalledProcessError as e :
93+ print (f"Process error: { e } " )
94+ print (f"stderr: { e .stderr } " )
95+ raise
96+ except Exception as e :
97+ print (f"Unexpected error: { e } " )
98+ raise
99+
100+ def get_participants (self , block : int ) -> List [str ]:
101+ try :
102+ response = requests .get (
103+ f"{ ETHENA_SUSDE_USDC_XLPT_ADDRESS_API_URL } ?block={ block } " ,
104+ timeout = 10
105+ )
106+ response .raise_for_status ()
107+
108+ data = response .json ()['data' ]
109+ if not isinstance (data , list ):
110+ logging .warning (f"Unexpected response format from API: { data } " )
111+ return []
112+
113+ return [addr for addr in data if isinstance (addr , str )]
114+
115+ except requests .RequestException as e :
116+ logging .error (f"Request failed for block { block } : { str (e )} " )
117+ return []
118+ except Exception as e :
119+ logging .error (f"Error processing participants for block { block } : { str (e )} " )
120+ return []
121+
122+ if __name__ == "__main__" :
123+ example_integration = EchelonAptosIntegration (
124+ integration_id = IntID .ECHELON_SUSDE_COLLATERAL ,
125+ start_block = ECHELON_SUSDE_USDC_XLPT_COLLATERAL_START_BLOCK ,
126+ market_address = SUSDE_USDC_XLPT_MARKET_ADDRESS ,
127+ token_address = SUSDE_USDC_TOKEN_ADDRESS ,
128+ decimals = 8 ,
129+ chain = Chain .APTOS ,
130+ reward_multiplier = 5 ,
131+ )
132+
133+ example_integration_output = example_integration .get_l2_block_balances (
134+ cached_data = {}, blocks = list (range (ECHELON_SUSDE_USDC_XLPT_COLLATERAL_START_BLOCK , ECHELON_SUSDE_USDC_XLPT_COLLATERAL_START_BLOCK + 40548182 , 2000000 ))
135+ )
136+
137+ print ("=" * 120 )
138+ print ("Run without cached data" , example_integration_output )
139+ print ("=" * 120 , "\n " * 5 )
0 commit comments