-
Notifications
You must be signed in to change notification settings - Fork 7
Description
A customer recently reached out and flagged that our API is returning an incorrect price for ZARP. Here’s the API response they received:
➜ curl -X GET "https://withered-fabled-sheet.base-mainnet.quiknode.pro/<API_KEY>/addon/1051/v1/prices?target=aero&limit=50&symbols=ZARP" | jq
{
"prices": {
"0xb755506531786c8ac63b756bab1ac387bacb0c04": {
"token": {
"address": "0xb755506531786C8aC63B756BaB1ac387bACB0C04",
"symbol": "ZARP",
"decimals": 18,
"listed": true
},
"price": 230.2870163751035,
"price_display": 230.287016
}
}
}
The Price of ZARP on the Aerodrome UI is .05 -
So the exact calls on my end:
- chain.get_all_tokens() → Calls Sugar contract's all() function → Returns ~5000 tokens
- chain.get_prices(tokens) → Calls Oracle contract's getManyRatesToEthWithCustomConnectors() → Returns prices
The second call is where ZARP gets the wrong $230 price.
To debug, we reviewed the source pool being used, which appears to be a concentrated liquidity (CL) pool.
We pulled sqrtPriceX96 directly from the CL pool contract (0xB86D18195349aAA6941042BD539289F26142A87b) and used the standard Uniswap V3 price formula for verification.
Details:
sqrtPriceX96: 345470727104832111786044952533601973
Calculated final price for ZARP: $0.052594
Aerodrome UI shows: $0.0524
Difference: $0.000194
Below is the minimal Python script we used to verify pricing against the CL pool:
#!/usr/bin/env python3
"""
Correctly Calculate ZARP Price from CL Pool
===========================================
Prove that Aerodrome uses the CL pool's sqrtPriceX96 correctly.
"""
import sys
import os
import asyncio
from web3 import Web3
from app.core.config import settings
sys.path.insert(0, 'Desktop/aerodrome/sugar-sdk')
from sugar.chains import AsyncBaseChain
async def test_oracle_calls():
print("=" * 80)
print("CORRECTLY CALCULATING ZARP PRICE FROM CL POOL")
print("=" * 80)
config = {
"rpc_uri": settings.SUGAR_RPC_URI_8453,
}
if settings.SUGAR_PK:
os.environ["SUGAR_PK"] = settings.SUGAR_PK
async with AsyncBaseChain(**config) as chain:
cl_pool_address = "0xB86D18195349aAA6941042BD539289F26142A87b"
print(f"\nStep 1: Fetching CL pool structure")
pools = await chain.get_pools()
cl_pool = next((p for p in pools if p.lp.lower() == cl_pool_address.lower()), None)
if cl_pool:
print(f" Token0: {cl_pool.token0.symbol} ({cl_pool.token0.decimals} decimals)")
print(f" Token1: {cl_pool.token1.symbol} ({cl_pool.token1.decimals} decimals)")
print(f" Pool: {cl_pool.symbol}")
print(f"\nStep 2: Retrieving slot0 and token addresses")
CL_POOL_ABI = [
{
"inputs": [],
"name": "slot0",
"outputs": [
{"internalType": "uint160", "name": "sqrtPriceX96", "type": "uint160"},
{"internalType": "int24", "name": "tick", "type": "int24"},
{"internalType": "uint16", "name": "observationIndex", "type": "uint16"},
{"internalType": "uint16", "name": "observationCardinality", "type": "uint16"},
{"internalType": "uint16", "name": "observationCardinalityNext", "type": "uint16"},
{"internalType": "bool", "name": "unlocked", "type": "bool"}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token0",
"outputs": [{"internalType": "address", "name": "", "type": "address"}],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "token1",
"outputs": [{"internalType": "address", "name": "", "type": "address"}],
"stateMutability": "view",
"type": "function"
}
]
try:
pool_contract = chain.web3.eth.contract(
address=Web3.to_checksum_address(cl_pool_address),
abi=CL_POOL_ABI
)
token0_addr = await pool_contract.functions.token0().call()
token1_addr = await pool_contract.functions.token1().call()
print(f" Token0 address: {token0_addr}")
print(f" Token1 address: {token1_addr}")
slot0 = await pool_contract.functions.slot0().call()
sqrt_price_x96 = slot0[0]
tick = slot0[1]
print(f" sqrtPriceX96: {sqrt_price_x96}")
print(f" Tick: {tick}")
print(f"\nStep 3: Calculating price from sqrtPriceX96")
sqrt_price = sqrt_price_x96 / (2**96)
zarp_per_usdc = sqrt_price ** 2
usdc_per_zarp = 1 / zarp_per_usdc
decimal_adjustment = (10**18) / (10**6)
final_price = usdc_per_zarp * decimal_adjustment
print(f" Final ZARP price: ${final_price:.6f}")
aerodrome_price = 0.0524
difference = abs(final_price - aerodrome_price)
ratio = final_price / aerodrome_price
print(f"\nComparison with Aerodrome:")
print(f" Our calculation: ${final_price:.6f}")
print(f" Aerodrome shows: ${aerodrome_price:.6f}")
print(f" Difference: ${difference:.6f}")
print(f" Ratio: {ratio:.2f}x")
if difference < 0.01:
print(f"\n SUCCESS: Price matches closely with Aerodrome.")
print(f" Confirmed: Aerodrome uses sqrtPriceX96 correctly.")
else:
print(f"\n Warning: Price mismatch detected. Further investigation may be needed.")
except Exception as e:
print(f" Error: {e}")
import traceback
traceback.print_exc()
print("Running CL pool price calculation test...")
asyncio.run(test_oracle_calls())
python3 test_actual_oracle_call.py
2025-07-21 18:16:12,216 - root - INFO - Set Sugar SDK thread pool size to 20 workers
2025-07-21 18:16:12,216 - root - INFO - Set Sugar SDK price threshold filter to 0
2025-07-21 18:16:12,216 - root - INFO - Logging configured. Level: INFO, Debug mode: False
Running CL pool price calculation test...
================================================================================
CORRECTLY CALCULATING ZARP PRICE FROM CL POOL
================================================================================
Step 1: Fetching CL pool structure
Token0: USDC (6 decimals)
Token1: ZARP (18 decimals)
Pool: CL10-USDC/ZARP
Step 2: Retrieving slot0 and token addresses
Token0 address: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Token1 address: 0xb755506531786C8aC63B756BaB1ac387bACB0C04
sqrtPriceX96: 345470727104832111786044952533601973
Tick: 305777
Step 3: Calculating price from sqrtPriceX96
Final ZARP price: $0.052594
Comparison with Aerodrome:
Our calculation: $0.052594
Aerodrome shows: $0.052400
Difference: $0.000194
Ratio: 1.00x
SUCCESS: Price matches closely with Aerodrome.
Confirmed: Aerodrome uses sqrtPriceX96 correctly.
# The SDK we use, however, is returning ~230 as the price. Based on the contract data and comparison with your UI, it seems the SDK oracle may not be
# interpreting sqrtPriceX96 from the CL pool correctly — potentially treating ZARP as the base unit incorrectly or failing to handle token decimals.
# Is there a known issue or nuance when reading CL pool pricing from Aerodrome that we should account for? Or anything subtle in the SDK vs. UI logic?