import time
from decimal import Decimal
from web3 import Web3
# Config
RPC_URL = "YOUR_CHAINSTACK_ENDPOINT"
POOL = Web3.to_checksum_address("0x38E2a053E67697e411344B184B3aBAe4fab42cC2")
CMETH_ADDR = Web3.to_checksum_address("0xE6829d9a7eE3040e1276Fa75293Bde931859e8fA")
USDE_ADDR = Web3.to_checksum_address("0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34")
# Minimal ERC-20 ABI
ERC20_ABI = [
{"constant":True,"name":"decimals","inputs":[],"outputs":[{"name":"","type":"uint8"}],"type":"function"},
{"constant":True,"name":"symbol","inputs":[],"outputs":[{"name":"","type":"string"}],"type":"function"},
{"constant":True,"name":"balanceOf","inputs":[{"name":"owner","type":"address"}],
"outputs":[{"name":"","type":"uint256"}],"type":"function"},
]
# Liquidity Book Pool ABI (minimal for price reading)
LB_POOL_ABI = [
{"inputs":[],"name":"getActiveId","outputs":[{"internalType":"uint24","name":"activeId","type":"uint24"}],"stateMutability":"view","type":"function"},
{"inputs":[],"name":"getBinStep","outputs":[{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"pure","type":"function"},
{"inputs":[],"name":"getTokenX","outputs":[{"internalType":"contract IERC20","name":"tokenX","type":"address"}],"stateMutability":"pure","type":"function"},
{"inputs":[],"name":"getTokenY","outputs":[{"internalType":"contract IERC20","name":"tokenY","type":"address"}],"stateMutability":"pure","type":"function"},
]
# Connect to RPC
w3 = Web3(Web3.HTTPProvider(RPC_URL))
assert w3.is_connected(), "❌ RPC not reachable"
cmeth = w3.eth.contract(address=CMETH_ADDR, abi=ERC20_ABI)
usde = w3.eth.contract(address=USDE_ADDR, abi=ERC20_ABI)
pool = w3.eth.contract(address=POOL, abi=LB_POOL_ABI)
dec_cmeth = cmeth.functions.decimals().call()
dec_usde = usde.functions.decimals().call()
sym_cmeth = cmeth.functions.symbol().call()
sym_usde = usde.functions.symbol().call()
# Get bin step for price calculation
bin_step = pool.functions.getBinStep().call()
# Determine token order in the pool
token_x = pool.functions.getTokenX().call()
token_y = pool.functions.getTokenY().call()
def calculate_price_from_bin_id(active_id, bin_step):
"""Calculate price from Liquidity Book bin ID and bin step"""
# LB price formula: price = (1 + binStep / 10_000) ^ (activeId - 8388608)
# 8388608 = 2^23 = reference bin ID where tokenX:tokenY price ratio = 1:1
# This is a protocol constant, not the actual trading bin (which is activeId)
BASE_BIN_ID = 8388608
bin_step_decimal = Decimal(bin_step) / Decimal(10000)
price_multiplier = (Decimal(1) + bin_step_decimal) ** (Decimal(active_id) - Decimal(BASE_BIN_ID))
return price_multiplier
def fetch_price():
"""Return cmETH price in USDe using Liquidity Book active bin"""
# Get current active bin ID
active_id = pool.functions.getActiveId().call()
# Calculate the price from the active bin
bin_price = calculate_price_from_bin_id(active_id, bin_step)
# Get reserves for display
bal_cmeth = cmeth.functions.balanceOf(POOL).call()
bal_usde = usde.functions.balanceOf(POOL).call()
cmeth_float = Decimal(bal_cmeth) / (10 ** dec_cmeth)
usde_float = Decimal(bal_usde) / (10 ** dec_usde)
# Determine which token is X and which is Y to get correct price direction
if token_x.lower() == CMETH_ADDR.lower():
# cmETH is tokenX, USDe is tokenY
# bin_price gives us tokenY/tokenX = USDe/cmETH
cmeth_in_usde = bin_price
usde_in_cmeth = Decimal(1) / bin_price if bin_price else Decimal(0)
else:
# USDe is tokenX, cmETH is tokenY
# bin_price gives us tokenY/tokenX = cmETH/USDe
usde_in_cmeth = bin_price
cmeth_in_usde = Decimal(1) / bin_price if bin_price else Decimal(0)
return cmeth_in_usde, usde_in_cmeth, usde_float, cmeth_float
if __name__ == "__main__":
print(f"📡 Reading Merchant Moe {sym_cmeth}/{sym_usde} pool … ⛓️ Mantle")
while True:
cmeth_usde, usde_cmeth, r_usde, r_cmeth = fetch_price()
print(
f"💧Reserves {r_usde:.2f} {sym_usde} | {r_cmeth:.5f} {sym_cmeth}\n"
f"💰1 {sym_cmeth} ≈ {cmeth_usde:.2f} {sym_usde} | "
f"1 {sym_usde} ≈ {usde_cmeth:.6f} {sym_cmeth}\n"
f"⏱️ {time.strftime('%Y-%m-%d %H:%M:%S')}\n{'-'*60}"
)
time.sleep(3) # poll every 3s