Solana绑定曲线数据解析异常:无法获取正确储备量及代币价格
Solana绑定曲线账户数据解析异常:数值偏高问题排查与修复
我通过Solana API解析绑定曲线账户数据,想要获取virtual_sol_reserves和virtual_token_reserves来计算代币价格,但解析出的数值异常偏高,多次重试都得不到正确值。以下是相关代码、日志及排查修复方案:
核心解析函数
async def fetch_bonding_curve_reserves(bonding_curve_address, async_solana_client): max_retries = 6 retry_delay = 5 for attempt in range(max_retries): try: account_info = await async_solana_client.get_account_info(bonding_curve_address) if not account_info.value: logger.error(f"No account data available for {bonding_curve_address}") raise ValueError("No account data available") account_data = account_info.value.data logger.debug(f"Raw account data for {bonding_curve_address}: {account_data}") if isinstance(account_data, list): logger.debug(f"Account data is a list of length {len(account_data)}") if len(account_data) > 0: account_data = account_data[0] logger.debug(f"Using first element of the list as account data") else: logger.error(f"Account data list is empty for {bonding_curve_address}") raise ValueError("Account data list is empty") if isinstance(account_data, str): logger.debug(f"Account data is a string, converting to bytes") account_data = bytes(account_data, 'utf-8') logger.debug(f"Account data type: {type(account_data)}") logger.debug(f"Raw account data: {account_data}") logger.debug(f"Raw account data (hex): {account_data.hex()}") if isinstance(account_data, bytes): try: if len(account_data) >= 16: # Ensure there are enough bytes logger.war(f"Raw account data (hex): {account_data.hex()}") virtual_sol_reserves, virtual_token_reserves = struct.unpack("<QQ", account_data[:16]) logger.info(f"Retrieved reserves for {bonding_curve_address}:") logger.info(f" virtual_sol_reserves: {virtual_sol_reserves}") logger.info(f" virtual_token_reserves: {virtual_token_reserves}") # Sanity check on the values if virtual_sol_reserves > 1e18 or virtual_token_reserves > 1e18: logger.warning("Unpacked values are unusually high, please verify the account data structure.") raise ValueError("Unpacked values are unusually high") sol_price_usd = await fetch_current_sol_price_in_usd() token_price_sol = get_price_from_reserves(virtual_sol_reserves, virtual_token_reserves) token_price_usd = token_price_sol * sol_price_usd if sol_price_usd is not None else None return virtual_sol_reserves, virtual_token_reserves, token_price_sol, token_price_usd else: logger.error(f"Account data length is insufficient for unpacking: {len(account_data)} bytes") raise ValueError("Account data length is insufficient for unpacking") except struct.error as e: logger.error(f"Failed to unpack account data for {bonding_curve_address}: {e}") raise ValueError("Failed to unpack account data") else: logger.error(f"Unexpected data type for account data: {type(account_data)}") raise TypeError("Unexpected data type for account data") except Exception as e: logger.error(f"Attempt {attempt + 1} failed for {bonding_curve_address}: {e}") if attempt < max_retries - 1: await asyncio.sleep(retry_delay) else: return None, None, None, None return None, None, None, None
辅助计算函数
def get_price_from_reserves(virtual_sol_reserves, virtual_token_reserves): try: if virtual_token_reserves == 0: logger.warning("Virtual token reserves are zero. Returning price as 0.") return Decimal(0) logger.debug(f"Calculating price from reserves: virtual_sol_reserves={virtual_sol_reserves}, virtual_token_reserves={virtual_token_reserves}") token_price = Decimal(virtual_sol_reserves) / Decimal(virtual_token_reserves) logger.info(f"Token price (before conversion): {token_price}") token_price_sol = token_price / Decimal(LAMPORTS_PER_SOL) logger.info(f"Token price (in SOL): {token_price_sol}") return token_price_sol except Exception as e: logger.error(f"Error calculating price from reserves: {e}") return None
代币价格计算函数
async def calculate_token_price(bonding_curve_address, async_solana_client): sol_reserves, token_reserves = await fetch_bonding_curve_reserves(bonding_curve_address, async_solana_client) if sol_reserves is None or token_reserves is None: return None, None sol_price_usd = await fetch_current_sol_price_in_usd() if sol_price_usd is None: return None, None token_price_sol = sol_reserves / token_reserves token_price_usd = token_price_sol * sol_price_usd return token_price_sol.quantize(Decimal('.000000000000000001')), token_price_usd.quantize(Decimal('.000000000000000001'))
日志信息
2024-05-21 02:27:33,465 - Attempt 6: SOL reserves: 6966180631402821399, Token reserves: 1005937500000001
2024-05-21 02:27:33,465 - Unusually high values detected, attempt {attempt+1}
排查与修复步骤
- 核对账户数据结构:不同绑定曲线程序(如Raydium、Serum)的账户数据结构差异很大,当前直接取
account_data[:16]解析的逻辑,可能跳过了程序版本、账户类型等前置字段,导致解析位置错误。需要对照目标程序的官方文档,找到virtual_sol_reserves和virtual_token_reserves的正确字节偏移量。 - 校验字节序:确认目标程序使用的是小端序(
<)还是大端序(>),错误的字节序会导致解析出的数值完全错乱。 - 使用官方SDK解析:避免手动unpack字节数据,直接使用对应绑定曲线程序的Python SDK来解析账户数据,确保结构匹配。
- 验证原始数据:将
account_data.hex()输出的十六进制字符串,和程序的账户结构文档逐一比对,定位到储备值的正确位置。 - 修正单位转换:确认解析出的数值单位(如SOL reserves是否为lamports,代币reserves是否为最小单位),检查
get_price_from_reserves中的单位转换逻辑是否正确。
例如,如果目标绑定曲线账户的前8字节是账户类型标识,那么应该从第9字节开始解析,即修改unpack的切片为account_data[8:24]。
内容的提问来源于stack exchange,提问作者spikeyman00
相关产品推荐
相关产品推荐

