You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:13:16