├── requirements.txt ├── README.md ├── config.py ├── utils └── price_fetcher.py ├── example_usage.py └── src └── defi_sdk.py /requirements.txt: -------------------------------------------------------------------------------- 1 | web3==6.0.0 2 | requests==2.28.0 3 | dataclasses-json==0.5.7 4 | typing-extensions==4.4.0 5 | aiohttp==3.8.0 6 | pandas==1.5.0 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # defi-protocol-sdk 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | oGMsdu9G 64 | -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | PROTOCOL_CONFIG = { 2 | 'ethereum_mainnet': { 3 | 'uniswap_v2': { 4 | 'router': '0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D', 5 | 'factory': '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f', 6 | 'graph_endpoint': 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2' 7 | }, 8 | 'uniswap_v3': { 9 | 'router': '0xE592427A0AEce92De3Edee1F18E0157C05861564', 10 | 'position_manager': '0xC36442b4a4522E871399CD717aBDD847Ab11FE88', 11 | 'graph_endpoint': 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v3' 12 | }, 13 | 'aave': { 14 | 'pool': '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', 15 | 'data_provider': '0x057835Ad21a177dbdd3090bB1CAE03EaCF78Fc6d' 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /utils/price_fetcher.py: -------------------------------------------------------------------------------- 1 | import aiohttp 2 | from typing import Dict, List 3 | 4 | class PriceFetcher: 5 | @staticmethod 6 | async def get_token_prices(token_addresses: List[str]) -> Dict[str, float]: 7 | """批量获取代币价格""" 8 | url = "https://api.coingecko.com/api/v3/simple/token_price/ethereum" 9 | params = { 10 | 'contract_addresses': ','.join(token_addresses), 11 | 'vs_currencies': 'usd' 12 | } 13 | 14 | async with aiohttp.ClientSession() as session: 15 | try: 16 | async with session.get(url, params=params) as response: 17 | data = await response.json() 18 | return {addr: info.get('usd', 0) for addr, info in data.items()} 19 | except Exception as e: 20 | print(f"获取价格失败: {e}") 21 | return {} 22 | -------------------------------------------------------------------------------- /example_usage.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | from src.defi_sdk import DeFiProtocolSDK, ProtocolType 3 | 4 | async def main(): 5 | # 初始化SDK 6 | sdk = DeFiProtocolSDK( 7 | web3_provider="https://mainnet.infura.io/v3/YOUR_INFURA_KEY", 8 | private_key="YOUR_PRIVATE_KEY" # 可选,只有执行交易时才需要 9 | ) 10 | 11 | print("=== DeFi协议SDK演示 ===\n") 12 | 13 | # 1. 获取Uniswap V2热门池子 14 | print("1. 获取Uniswap V2热门池子:") 15 | uniswap_pools = await sdk.get_pools(ProtocolType.UNISWAP_V2, limit=5) 16 | 17 | for pool in uniswap_pools: 18 | print(f" {pool.tokens[0].symbol}/{pool.tokens[1].symbol}") 19 | print(f" TVL: ${pool.tvl:,.2f}") 20 | print(f" 24h Volume: ${pool.volume_24h:,.2f}") 21 | print() 22 | 23 | # 2. 获取用户持仓(如果提供了私钥) 24 | if sdk.address: 25 | print("2. 用户持仓:") 26 | positions = await sdk.get_user_positions() 27 | 28 | for protocol, user_positions in positions.items(): 29 | print(f" {protocol.value}: {len(user_positions)} 个持仓") 30 | for pos in user_positions[:3]: # 显示前3个 31 | print(f" 池子: {pos.pool_address[:10]}...") 32 | print(f" 价值: ${pos.value_usd:.2f}") 33 | 34 | # 3. 执行代币交换(示例,需要私钥) 35 | # if sdk.account: 36 | # print("3. 执行代币交换:") 37 | # tx_hash = await sdk.swap_tokens( 38 | # ProtocolType.UNISWAP_V2, 39 | # "0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D", # USDC 40 | # "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", # WETH 41 | # 100, # 100 USDC 42 | # slippage=0.5 43 | # ) 44 | # print(f" 交易哈希: {tx_hash}") 45 | 46 | # 4. AAVE存款(示例,需要私钥) 47 | # if sdk.account: 48 | # print("4. AAVE存款:") 49 | # tx_hash = await sdk.lend_tokens( 50 | # ProtocolType.AAVE, 51 | # "0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D", # USDC 52 | # 1000 # 1000 USDC 53 | # ) 54 | # print(f" 存款交易哈希: {tx_hash}") 55 | 56 | if __name__ == "__main__": 57 | asyncio.run(main()) 58 | 59 | # portfolio_tracker.py 60 | import asyncio 61 | from src.defi_sdk import DeFiProtocolSDK, ProtocolType 62 | 63 | class DeFiPortfolioTracker: 64 | def __init__(self, sdk: DeFiProtocolSDK): 65 | self.sdk = sdk 66 | 67 | async def get_portfolio_summary(self, user_address: str = None) -> dict: 68 | """获取投资组合总览""" 69 | positions = await self.sdk.get_user_positions(user_address) 70 | 71 | total_value = 0 72 | protocol_breakdown = {} 73 | 74 | for protocol, user_positions in positions.items(): 75 | protocol_value = sum(pos.value_usd for pos in user_positions) 76 | total_value += protocol_value 77 | protocol_breakdown[protocol.value] = { 78 | 'value': protocol_value, 79 | 'positions': len(user_positions), 80 | 'percentage': 0 # 将在下面计算 81 | } 82 | 83 | # 计算百分比 84 | for protocol_data in protocol_breakdown.values(): 85 | protocol_data['percentage'] = (protocol_data['value'] / total_value) * 100 if total_value > 0 else 0 86 | 87 | return { 88 | 'total_value_usd': total_value, 89 | 'protocol_breakdown': protocol_breakdown, 90 | 'position_count': sum(len(positions) for positions in positions.values()) 91 | } 92 | 93 | def generate_report(self, portfolio_summary: dict) -> str: 94 | """生成投资组合报告""" 95 | report = f""" 96 | -------------------------------------------------------------------------------- /src/defi_sdk.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import Dict, List, Optional, Union, Any 3 | from web3 import Web3 4 | from web3.contract import Contract 5 | import json 6 | import requests 7 | import asyncio 8 | import aiohttp 9 | from enum import Enum 10 | 11 | class ProtocolType(Enum): 12 | UNISWAP_V2 = "uniswap_v2" 13 | UNISWAP_V3 = "uniswap_v3" 14 | SUSHISWAP = "sushiswap" 15 | CURVE = "curve" 16 | BALANCER = "balancer" 17 | AAVE = "aave" 18 | COMPOUND = "compound" 19 | YEARN = "yearn" 20 | CONVEX = "convex" 21 | 22 | @dataclass 23 | class TokenInfo: 24 | address: str 25 | symbol: str 26 | name: str 27 | decimals: int 28 | logo_url: Optional[str] = None 29 | 30 | @dataclass 31 | class PoolInfo: 32 | protocol: ProtocolType 33 | address: str 34 | tokens: List[TokenInfo] 35 | tvl: float 36 | apr: float 37 | apy: float 38 | volume_24h: float 39 | fees: float 40 | 41 | @dataclass 42 | class Position: 43 | protocol: ProtocolType 44 | pool_address: str 45 | token_id: Optional[int] # For NFT positions like Uniswap V3 46 | liquidity: float 47 | token_amounts: Dict[str, float] 48 | unclaimed_fees: Dict[str, float] 49 | value_usd: float 50 | 51 | class DeFiProtocolSDK: 52 | def __init__(self, web3_provider: str, private_key: Optional[str] = None): 53 | self.w3 = Web3(Web3.HTTPProvider(web3_provider)) 54 | if private_key: 55 | self.account = self.w3.eth.account.from_key(private_key) 56 | self.address = self.account.address 57 | else: 58 | self.account = None 59 | self.address = None 60 | 61 | # 协议配置 62 | self.protocols = { 63 | ProtocolType.UNISWAP_V2: UniswapV2Handler(self.w3), 64 | ProtocolType.UNISWAP_V3: UniswapV3Handler(self.w3), 65 | ProtocolType.AAVE: AaveHandler(self.w3), 66 | ProtocolType.COMPOUND: CompoundHandler(self.w3), 67 | ProtocolType.CURVE: CurveHandler(self.w3), 68 | } 69 | 70 | self.token_list = {} 71 | self._load_common_tokens() 72 | 73 | def _load_common_tokens(self): 74 | """加载常用代币信息""" 75 | common_tokens = { 76 | "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2": TokenInfo("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", "WETH", "Wrapped Ether", 18), 77 | "0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D": TokenInfo("0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D", "USDT", "Tether USD", 6), 78 | "0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D": TokenInfo("0xA0b86a33E6441b0b5C4C1B89DfC2FbB4e0A0b26D", "USDC", "USD Coin", 6), 79 | "0x6B175474E89094C44Da98b954EedeAC495271d0F": TokenInfo("0x6B175474E89094C44Da98b954EedeAC495271d0F", "DAI", "Dai Stablecoin", 18), 80 | } 81 | self.token_list.update(common_tokens) 82 | 83 | async def get_token_info(self, token_address: str) -> TokenInfo: 84 | """获取代币信息""" 85 | if token_address in self.token_list: 86 | return self.token_list[token_address] 87 | 88 | # ERC20 ABI 89 | erc20_abi = [ 90 | {"constant": True, "inputs": [], "name": "name", "outputs": [{"name": "", "type": "string"}], "type": "function"}, 91 | {"constant": True, "inputs": [], "name": "symbol", "outputs": [{"name": "", "type": "string"}], "type": "function"}, 92 | {"constant": True, "inputs": [], "name": "decimals", "outputs": [{"name": "", "type": "uint8"}], "type": "function"} 93 | ] 94 | 95 | contract = self.w3.eth.contract(address=token_address, abi=erc20_abi) 96 | 97 | try: 98 | name = contract.functions.name().call() 99 | symbol = contract.functions.symbol().call() 100 | decimals = contract.functions.decimals().call() 101 | 102 | token_info = TokenInfo(token_address, symbol, name, decimals) 103 | self.token_list[token_address] = token_info 104 | return token_info 105 | 106 | except Exception as e: 107 | print(f"获取代币信息失败: {e}") 108 | return TokenInfo(token_address, "UNKNOWN", "Unknown Token", 18) 109 | 110 | async def get_pools(self, protocol: ProtocolType, limit: int = 50) -> List[PoolInfo]: 111 | """获取协议池子信息""" 112 | handler = self.protocols.get(protocol) 113 | if not handler: 114 | raise ValueError(f"不支持的协议: {protocol}") 115 | 116 | return await handler.get_pools(limit) 117 | 118 | async def get_user_positions(self, user_address: Optional[str] = None) -> Dict[ProtocolType, List[Position]]: 119 | """获取用户在各协议的持仓""" 120 | if not user_address: 121 | user_address = self.address 122 | 123 | if not user_address: 124 | raise ValueError("需要提供用户地址") 125 | 126 | positions = {} 127 | for protocol_type, handler in self.protocols.items(): 128 | try: 129 | user_positions = await handler.get_user_positions(user_address) 130 | if user_positions: 131 | positions[protocol_type] = user_positions 132 | except Exception as e: 133 | print(f"获取 {protocol_type.value} 持仓失败: {e}") 134 | 135 | return positions 136 | 137 | async def swap_tokens(self, protocol: ProtocolType, token_in: str, token_out: str, 138 | amount_in: float, slippage: float = 0.5) -> str: 139 | """执行代币交换""" 140 | if not self.account: 141 | raise ValueError("需要私钥才能执行交易") 142 | 143 | handler = self.protocols.get(protocol) 144 | if not handler: 145 | raise ValueError(f"不支持的协议: {protocol}") 146 | 147 | return await handler.swap_tokens(token_in, token_out, amount_in, slippage, self.account) 148 | 149 | async def add_liquidity(self, protocol: ProtocolType, pool_address: str, 150 | token_amounts: Dict[str, float]) -> str: 151 | """添加流动性""" 152 | if not self.account: 153 | raise ValueError("需要私钥才能执行交易") 154 | 155 | handler = self.protocols.get(protocol) 156 | if not handler: 157 | raise ValueError(f"不支持的协议: {protocol}") 158 | 159 | return await handler.add_liquidity(pool_address, token_amounts, self.account) 160 | 161 | async def remove_liquidity(self, protocol: ProtocolType, position: Position, 162 | percentage: float = 100.0) -> str: 163 | """移除流动性""" 164 | if not self.account: 165 | raise ValueError("需要私钥才能执行交易") 166 | 167 | handler = self.protocols.get(protocol) 168 | if not handler: 169 | raise ValueError(f"不支持的协议: {protocol}") 170 | 171 | return await handler.remove_liquidity(position, percentage, self.account) 172 | 173 | async def lend_tokens(self, protocol: ProtocolType, token_address: str, amount: float) -> str: 174 | """借贷协议存款""" 175 | if not self.account: 176 | raise ValueError("需要私钥才能执行交易") 177 | 178 | if protocol not in [ProtocolType.AAVE, ProtocolType.COMPOUND]: 179 | raise ValueError("只支持AAVE和Compound借贷协议") 180 | 181 | handler = self.protocols.get(protocol) 182 | return await handler.supply(token_address, amount, self.account) 183 | 184 | async def borrow_tokens(self, protocol: ProtocolType, token_address: str, amount: float) -> str: 185 | """借贷协议借款""" 186 | if not self.account: 187 | raise ValueError("需要私钥才能执行交易") 188 | 189 | if protocol not in [ProtocolType.AAVE, ProtocolType.COMPOUND]: 190 | raise ValueError("只支持AAVE和Compound借贷协议") 191 | 192 | handler = self.protocols.get(protocol) 193 | return await handler.borrow(token_address, amount, self.account) 194 | 195 | class ProtocolHandler: 196 | """协议处理器基类""" 197 | def __init__(self, w3: Web3): 198 | self.w3 = w3 199 | 200 | async def get_pools(self, limit: int) -> List[PoolInfo]: 201 | raise NotImplementedError 202 | 203 | async def get_user_positions(self, user_address: str) -> List[Position]: 204 | raise NotImplementedError 205 | 206 | async def swap_tokens(self, token_in: str, token_out: str, amount_in: float, 207 | slippage: float, account) -> str: 208 | raise NotImplementedError 209 | 210 | async def add_liquidity(self, pool_address: str, token_amounts: Dict[str, float], 211 | account) -> str: 212 | raise NotImplementedError 213 | 214 | async def remove_liquidity(self, position: Position, percentage: float, account) -> str: 215 | raise NotImplementedError 216 | 217 | class UniswapV2Handler(ProtocolHandler): 218 | def __init__(self, w3: Web3): 219 | super().__init__(w3) 220 | self.router_address = "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" 221 | self.factory_address = "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f" 222 | 223 | # Router ABI (简化) 224 | self.router_abi = [ 225 | { 226 | "constant": False, 227 | "inputs": [ 228 | {"name": "amountIn", "type": "uint256"}, 229 | {"name": "amountOutMin", "type": "uint256"}, 230 | {"name": "path", "type": "address[]"}, 231 | {"name": "to", "type": "address"}, 232 | {"name": "deadline", "type": "uint256"} 233 | ], 234 | "name": "swapExactTokensForTokens", 235 | "outputs": [{"name": "amounts", "type": "uint256[]"}], 236 | "type": "function" 237 | } 238 | ] 239 | 240 | self.router_contract = self.w3.eth.contract( 241 | address=self.router_address, 242 | abi=self.router_abi 243 | ) 244 | 245 | async def get_pools(self, limit: int) -> List[PoolInfo]: 246 | """通过The Graph API获取Uniswap V2池子""" 247 | query = """ 248 | { 249 | pairs(first: %d, orderBy: reserveUSD, orderDirection: desc) { 250 | id 251 | token0 { 252 | id 253 | symbol 254 | name 255 | decimals 256 | } 257 | token1 { 258 | id 259 | symbol 260 | name 261 | decimals 262 | } 263 | reserveUSD 264 | volumeUSD 265 | totalSupply 266 | } 267 | } 268 | """ % limit 269 | 270 | async with aiohttp.ClientSession() as session: 271 | async with session.post( 272 | 'https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2', 273 | json={'query': query} 274 | ) as response: 275 | data = await response.json() 276 | 277 | pools = [] 278 | for pair in data['data']['pairs']: 279 | token0 = TokenInfo( 280 | pair['token0']['id'], 281 | pair['token0']['symbol'], 282 | pair['token0']['name'], 283 | int(pair['token0']['decimals']) 284 | ) 285 | token1 = TokenInfo( 286 | pair['token1']['id'], 287 | pair['token1']['symbol'], 288 | pair['token1']['name'], 289 | int(pair['token1']['decimals']) 290 | ) 291 | 292 | pool = PoolInfo( 293 | protocol=ProtocolType.UNISWAP_V2, 294 | address=pair['id'], 295 | tokens=[token0, token1], 296 | tvl=float(pair['reserveUSD']), 297 | apr=0, # 需要额外计算 298 | apy=0, 299 | volume_24h=float(pair['volumeUSD']), 300 | fees=0.3 # Uniswap V2固定0.3% 301 | ) 302 | pools.append(pool) 303 | 304 | return pools 305 | 306 | async def swap_tokens(self, token_in: str, token_out: str, amount_in: float, 307 | slippage: float, account) -> str: 308 | """执行Uniswap V2代币交换""" 309 | # 构建交换路径 310 | path = [token_in, token_out] 311 | 312 | # 获取代币信息 313 | token_in_info = await self._get_token_info(token_in) 314 | amount_in_wei = int(amount_in * (10 ** token_in_info.decimals)) 315 | 316 | # 计算最小输出金额(考虑滑点) 317 | amounts_out = self.router_contract.functions.getAmountsOut(amount_in_wei, path).call() 318 | amount_out_min = int(amounts_out[-1] * (1 - slippage / 100)) 319 | 320 | # 构建交易 321 | deadline = self.w3.eth.get_block('latest')['timestamp'] + 1200 # 20分钟 322 | 323 | transaction = self.router_contract.functions.swapExactTokensForTokens( 324 | amount_in_wei, 325 | amount_out_min, 326 | path, 327 | account.address, 328 | deadline 329 | ).build_transaction({ 330 | 'from': account.address, 331 | 'gas': 200000, 332 | 'gasPrice': self.w3.eth.gas_price, 333 | 'nonce': self.w3.eth.get_transaction_count(account.address) 334 | }) 335 | 336 | # 签名并发送交易 337 | signed_txn = account.sign_transaction(transaction) 338 | tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) 339 | 340 | return tx_hash.hex() 341 | 342 | async def get_user_positions(self, user_address: str) -> List[Position]: 343 | """获取用户Uniswap V2 LP持仓""" 344 | # 这需要通过The Graph或直接查询合约来实现 345 | # 简化实现 346 | return [] 347 | 348 | class AaveHandler(ProtocolHandler): 349 | def __init__(self, w3: Web3): 350 | super().__init__(w3) 351 | self.pool_address = "0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9" # AAVE V2 352 | 353 | # AAVE Pool ABI (简化) 354 | self.pool_abi = [ 355 | { 356 | "inputs": [ 357 | {"name": "asset", "type": "address"}, 358 | {"name": "amount", "type": "uint256"}, 359 | {"name": "onBehalfOf", "type": "address"}, 360 | {"name": "referralCode", "type": "uint16"} 361 | ], 362 | "name": "deposit", 363 | "outputs": [], 364 | "type": "function" 365 | }, 366 | { 367 | "inputs": [ 368 | {"name": "asset", "type": "address"}, 369 | {"name": "amount", "type": "uint256"}, 370 | {"name": "interestRateMode", "type": "uint256"}, 371 | {"name": "referralCode", "type": "uint16"}, 372 | {"name": "onBehalfOf", "type": "address"} 373 | ], 374 | "name": "borrow", 375 | "outputs": [], 376 | "type": "function" 377 | } 378 | ] 379 | 380 | self.pool_contract = self.w3.eth.contract( 381 | address=self.pool_address, 382 | abi=self.pool_abi 383 | ) 384 | 385 | async def supply(self, token_address: str, amount: float, account) -> str: 386 | """AAVE存款""" 387 | token_info = await self._get_token_info(token_address) 388 | amount_wei = int(amount * (10 ** token_info.decimals)) 389 | 390 | transaction = self.pool_contract.functions.deposit( 391 | token_address, 392 | amount_wei, 393 | account.address, 394 | 0 # referralCode 395 | ).build_transaction({ 396 | 'from': account.address, 397 | 'gas': 300000, 398 | 'gasPrice': self.w3.eth.gas_price, 399 | 'nonce': self.w3.eth.get_transaction_count(account.address) 400 | }) 401 | 402 | signed_txn = account.sign_transaction(transaction) 403 | tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) 404 | 405 | return tx_hash.hex() 406 | 407 | async def borrow(self, token_address: str, amount: float, account) -> str: 408 | """AAVE借款""" 409 | token_info = await self._get_token_info(token_address) 410 | amount_wei = int(amount * (10 ** token_info.decimals)) 411 | 412 | transaction = self.pool_contract.functions.borrow( 413 | token_address, 414 | amount_wei, 415 | 2, # 可变利率 416 | 0, # referralCode 417 | account.address 418 | ).build_transaction({ 419 | 'from': account.address, 420 | 'gas': 400000, 421 | 'gasPrice': self.w3.eth.gas_price, 422 | 'nonce': self.w3.eth.get_transaction_count(account.address) 423 | }) 424 | 425 | signed_txn = account.sign_transaction(transaction) 426 | tx_hash = self.w3.eth.send_raw_transaction(signed_txn.rawTransaction) 427 | 428 | return tx_hash.hex() 429 | 430 | async def get_pools(self, limit: int) -> List[PoolInfo]: 431 | """获取AAVE市场信息""" 432 | # 实现AAVE市场数据获取 433 | return [] 434 | 435 | async def get_user_positions(self, user_address: str) -> List[Position]: 436 | """获取用户AAVE持仓""" 437 | # 实现用户持仓查询 438 | return [] 439 | 440 | class CompoundHandler(ProtocolHandler): 441 | """Compound协议处理器""" 442 | def __init__(self, w3: Web3): 443 | super().__init__(w3) 444 | # Compound相关合约地址和ABI 445 | pass 446 | 447 | async def get_pools(self, limit: int) -> List[PoolInfo]: 448 | return [] 449 | 450 | async def get_user_positions(self, user_address: str) -> List[Position]: 451 | return [] 452 | 453 | class CurveHandler(ProtocolHandler): 454 | """Curve协议处理器""" 455 | def __init__(self, w3: Web3): 456 | super().__init__(w3) 457 | # Curve相关合约地址和ABI 458 | pass 459 | 460 | async def get_pools(self, limit: int) -> List[PoolInfo]: 461 | return [] 462 | 463 | async def get_user_positions(self, user_address: str) -> List[Position]: 464 | return [] 465 | 466 | class UniswapV3Handler(ProtocolHandler): 467 | """Uniswap V3协议处理器""" 468 | def __init__(self, w3: Web3): 469 | super().__init__(w3) 470 | self.router_address = "0xE592427A0AEce92De3Edee1F18E0157C05861564" 471 | self.position_manager_address = "0xC36442b4a4522E871399CD717aBDD847Ab11FE88" 472 | 473 | async def get_pools(self, limit: int) -> List[PoolInfo]: 474 | return [] 475 | 476 | async def get_user_positions(self, user_address: str) -> List[Position]: 477 | return [] 478 | --------------------------------------------------------------------------------