The Alef portfolio is 100% on-chain and auditable by anyone in real time. Reserve addresses across all active chains are published below. Proof of ownership of any address can be requested using the form at the bottom of this section.
You can also audit all assets and all liabilities using this open source program:
import requests
import warnings
# Some older Anaconda environments emit harmless pandas optional-dependency
# warnings while importing yfinance. They do not affect ALEF calculations.
with warnings.catch_warnings():
warnings.filterwarnings(
"ignore",
message=r"Pandas requires version .* of 'numexpr'.*",
category=UserWarning,
)
warnings.filterwarnings(
"ignore",
message=r"Pandas requires version .* of 'bottleneck'.*",
category=UserWarning,
)
import yfinance as yf
import sys
import os
import re
import json
import math
import html as html_lib
import base64
import hashlib
from decimal import Decimal, getcontext
from datetime import datetime, timezone
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
# ===============================================================
# ALEF AUDITOR / OPEN-SOURCE BUILD
# ===============================================================
# This build uses the same NAV calculation engine as the operational updater.
# It is designed for auditors and public review:
# - no Excel/price-string output
# - no clipboard writes
# - live non-financial progress messages while network calls run
# - a human-readable audit report after the calculation completes
# - exceptions propagate to the caller
#
# Call calculate_alef_nav() to obtain the complete structured calculation.
# Call print_audit_report(result) to render the human-readable report.
# Running the full file/cell performs both automatically.
AUDIT_BUILD = "v18-audit-v5"
def _audit_log(*args, **kwargs):
"""Financial/detail diagnostics stay silent in the public auditor build."""
return None
def _progress(message):
"""Emit non-financial progress only, immediately flushed for Jupyter/auditors."""
print(f"[ALEF audit] {message}", file=sys.stderr, flush=True)
getcontext().prec = 50
# ===============================================================
# ALEF CONFIGURATION
# ===============================================================
# Change this whenever you want to change the management fee.
# 0.005 = 0.50% per year.
ANNUAL_FEE_RATE = Decimal("0.005")
# ALEF has NO opening-price anchor or normalization.
# Price is always exactly:
# fee-adjusted NAV / current ALEF supply
# The state file stores only the continuously accrued management-fee factor.
# Deleting it restarts fee accrual from the next successful run.
# __file__ is unavailable in notebooks/Jupyter, so fall back to the current working directory.
try:
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
except NameError:
SCRIPT_DIR = os.getcwd()
ALEF_STATE_FILE = os.path.join(SCRIPT_DIR, "alef_nav_state.json")
ALEF_MULTIPLIER_CACHE_FILE = os.path.join(SCRIPT_DIR, "alef_xstock_multiplier_cache.json")
ALEF_WEBSITE = "https://alef.money/"
AAVE_V4_GRAPHQL = "https://api.v4.aave.com/graphql"
AAVE_V3_GRAPHQL = "https://api.v3.aave.com/graphql"
MORPHO_GRAPHQL = "https://api.morpho.org/graphql"
JUSTLEND_API = "https://openapi.just.network"
VENUS_API = "https://api.venus.io"
FLUID_API = "https://api.fluid.instadapp.io"
# ALEF currently uses one known Venus/Fluid Flux vault position on BNB Chain.
# Query that position directly instead of scanning every Venus Core market.
VENUS_FLUX_VAULT_NFT_ID = "800"
BNB_RPC = "https://bsc-dataseed.binance.org/"
SOLANA_RPC = "https://api.mainnet-beta.solana.com"
ETHEREUM_RPC = "https://ethereum-rpc.publicnode.com"
TRONGRID_API = "https://api.trongrid.io"
TONCENTER_API = "https://toncenter.com/api/v2"
# Optional keys. The script does not require these for the known ALEF holdings,
# but if supplied they improve arbitrary-token discovery/pricing.
ETHERSCAN_API_KEY = os.getenv("ETHERSCAN_API_KEY") or os.getenv("BSCSCAN_API_KEY")
JUPITER_API_KEY = os.getenv("JUPITER_API_KEY")
# Known Solana reserve assets whose mint->price identity can be verified
# independently of legacy contract-price endpoints. Add entries here if ALEF
# intentionally acquires another tokenized security that lacks a DEX quote.
SOLANA_KNOWN_ASSETS = {
"Xst6eFD4YT6sz9RLMysN9SyvaZWtraSdVJQGu5ZkAme": {
"symbol": "PPLTx",
"coingecko_id": "abrdn-physical-platinum-shares-xstock",
"yahoo_ticker": "PPLT",
},
}
SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
# Portfolio-level security filter. Wallet scanners enumerate holdings first;
# NAV includes only securities that map to the portfolio described on alef.money:
# QQQ + Magnificent Seven, plus precious-metal exposures. USDC is the liquidity reserve.
PORTFOLIO_SECURITY_TICKERS = {
# Broad / leveraged technology exposures used by ALEF.
"QQQ", "TQQQ", "MAGX",
# Magnificent Seven.
"AAPL", "MSFT", "AMZN", "NVDA", "META", "TSLA", "GOOG", "GOOGL",
# Precious-metal ETF/proxy tickers used by tokenized-security issuers.
"GLD", "IAU", "UGL", "SLV", "PPLT", "PLTM", "3GOL", "3GOL.L",
}
PORTFOLIO_CASH_SYMBOLS = {"USDC"}
# -----------------------------------------------------------------
# NAV PRICE SOURCE
# -----------------------------------------------------------------
# IMPORTANT: blockchain / issuer APIs are used to discover WHAT ALEF owns and
# HOW MANY economic units it owns. USD marks for portfolio securities come
# from the same Yahoo Finance function used by the original Excel updater;
# crypto marks come from the same CoinGecko/yfinance function above.
#
# Issuer/on-chain multiplier data is NOT a price. On Solana and TON it is only
# used to convert raw xStock units into share-equivalent units after dividends
# and splits. EVM xStock balanceOf() is already economically adjusted.
_NAV_STOCK_PRICE_CACHE = {}
_NAV_CRYPTO_PRICE_CACHE = {}
PORTFOLIO_CRYPTO_SYMBOLS = {
"BTC", "ETH", "BNB", "TRX", "XAUT", "PAXG",
}
PORTFOLIO_STABLE_SYMBOLS = {"USDC", "USDT", "USDON", "RUSDY", "USDY"}
PORTFOLIO_SYMBOL_ALIASES = {
"WBTC": "BTC", "BTCB": "BTC",
"WETH": "ETH", "WBNB": "BNB",
"USDC.E": "USDC", "USDCET": "USDC",
}
# Optional per-token quantity fallbacks. Keep empty unless a multiplier has been
# independently verified. Price is never stored here.
XSTOCK_KNOWN_MULTIPLIER_FALLBACKS = {}
def _seed_nav_price_caches(crypto_symbols, crypto_prices, stock_tickers, stock_prices):
_NAV_CRYPTO_PRICE_CACHE.clear()
_NAV_STOCK_PRICE_CACHE.clear()
for sym, p in zip(crypto_symbols, crypto_prices):
if sym and p is not None:
_NAV_CRYPTO_PRICE_CACHE[str(sym).upper()] = D(p)
for ticker, p in zip(stock_tickers, stock_prices):
if ticker and p is not None:
_NAV_STOCK_PRICE_CACHE[str(ticker).upper()] = D(p)
def _nav_stock_price_usd(ticker):
"""Yahoo Finance only. No blockchain/DEX/issuer security price marks."""
t = str(ticker or "").upper().strip().replace(".", "-")
if not t:
return None
cached = _NAV_STOCK_PRICE_CACHE.get(t)
if cached is not None and cached > 0:
return cached
try:
p = fetch_stock_prices([t])[0]
if p is not None and p > 0:
d = D(p)
_NAV_STOCK_PRICE_CACHE[t] = d
return d
except Exception:
pass
# GOOG/GOOGL are distinct share classes but are close substitutes for a
# price fallback if Yahoo temporarily fails on one symbol.
if t == "GOOGL":
return _nav_stock_price_usd("GOOG")
return None
def _nav_crypto_price_usd(symbol):
"""Original CoinGecko -> yfinance crypto price path only."""
s = str(symbol or "").upper().strip()
s = PORTFOLIO_SYMBOL_ALIASES.get(s, s)
if s in PORTFOLIO_STABLE_SYMBOLS:
return Decimal("1")
cached = _NAV_CRYPTO_PRICE_CACHE.get(s)
if cached is not None and cached > 0:
return cached
try:
p = fetch_crypto_prices([s])[0]
if p is not None and p > 0:
d = D(p)
_NAV_CRYPTO_PRICE_CACHE[s] = d
return d
except Exception:
pass
return None
def _portfolio_raw_token_price_usd(symbol):
"""Price only raw tokens that belong to the declared portfolio/cash set."""
s = str(symbol or "").upper().strip()
s = PORTFOLIO_SYMBOL_ALIASES.get(s, s)
if s in PORTFOLIO_STABLE_SYMBOLS:
return Decimal("1")
if s in PORTFOLIO_CRYPTO_SYMBOLS:
return _nav_crypto_price_usd(s)
return None
def _load_multiplier_cache():
try:
with open(ALEF_MULTIPLIER_CACHE_FILE, 'r', encoding='utf-8') as f:
data = json.load(f)
return data if isinstance(data, dict) else {}
except Exception:
return {}
def _save_multiplier_cache(data):
try:
tmp = ALEF_MULTIPLIER_CACHE_FILE + '.tmp'
with open(tmp, 'w', encoding='utf-8') as f:
json.dump(data, f, indent=2, sort_keys=True)
os.replace(tmp, ALEF_MULTIPLIER_CACHE_FILE)
except Exception as e:
_audit_log(f"[WARN] Could not save xStock multiplier cache: {e}", file=sys.stderr)
def _cached_xstock_multiplier(symbol, network):
key = f"{str(network or '').upper()}:{str(symbol or '').upper()}"
row = _load_multiplier_cache().get(key)
if isinstance(row, dict):
try:
m = D(row.get('multiplier'))
return m if m > 0 else None
except Exception:
return None
return None
def _remember_xstock_multiplier(symbol, network, multiplier, source):
try:
m = D(multiplier)
if m <= 0:
return
except Exception:
return
key = f"{str(network or '').upper()}:{str(symbol or '').upper()}"
data = _load_multiplier_cache()
data[key] = {
'multiplier': str(m),
'source': str(source),
'updated_at_utc': datetime.now(timezone.utc).isoformat(),
}
_save_multiplier_cache(data)
def _resolve_xstock_multiplier(symbol, network, *, onchain=None, registry_meta=None):
"""
Quantity conversion only; never a USD price source.
Order: on-chain -> registry -> issuer multiplier API -> last-known cache ->
verified per-token fallback -> 1.0 (non-fatal, loudly diagnosed).
"""
symbol = str(symbol or '')
network = str(network or '').upper()
if onchain is not None:
try:
m = D(onchain)
if m > 0:
_remember_xstock_multiplier(symbol, network, m, 'onchain')
return m, 'onchain'
except Exception:
pass
if isinstance(registry_meta, dict):
try:
m = D(registry_meta.get('multiplier'))
if m > 0:
_remember_xstock_multiplier(symbol, network, m, 'issuer-registry')
return m, 'issuer-registry'
except Exception:
pass
m = _xstocks_official_multiplier(symbol, network=network)
if m is not None and m > 0:
_remember_xstock_multiplier(symbol, network, m, 'issuer-api')
return m, 'issuer-api'
m = _cached_xstock_multiplier(symbol, network)
if m is not None and m > 0:
return m, 'last-known-cache'
m = XSTOCK_KNOWN_MULTIPLIER_FALLBACKS.get((network, symbol.upper()))
if m is not None and m > 0:
return m, 'known-fallback'
_audit_log(
f"[WARN] {network} multiplier unavailable for {symbol}; using 1.0 provisionally. "
"USD price still comes from Yahoo Finance.", file=sys.stderr,
)
return Decimal('1'), 'provisional-1x'
def _portfolio_security_allowed(ticker=None, symbol=None):
t = str(ticker or "").upper().strip()
s = str(symbol or "").upper().strip()
# Provider suffixes: xStocks -> x, Ondo -> on, bStocks -> B.
if not t and s.endswith("X"):
t = s[:-1]
if not t and s.endswith("ON"):
t = s[:-2]
if not t and s.endswith("B"):
t = s[:-1]
return t in PORTFOLIO_SECURITY_TICKERS or s in PORTFOLIO_CASH_SYMBOLS
def _portfolio_registry_subset(registry, provider):
"""Limit direct issuer contract probes to assets that can enter ALEF NAV.
Generic wallet/indexer scans still enumerate every token they can see.
This optimization applies only to direct provider contract probing.
"""
subset = {}
for addr, meta in (registry or {}).items():
meta = meta or {}
symbol = str(meta.get("symbol") or "")
ticker = str(meta.get("ticker") or meta.get("underlyingSymbol") or "")
kind = str(meta.get("kind") or "")
if provider == "xstocks" and not ticker and symbol.upper().endswith("X"):
ticker = symbol[:-1]
elif provider == "bstocks" and not ticker and symbol.upper().endswith("B"):
ticker = symbol[:-1]
elif provider == "ondo" and kind == "ondo-stock" and not ticker and symbol.upper().endswith("ON"):
ticker = symbol[:-2]
if provider in ("xstocks", "bstocks") or kind == "ondo-stock":
keep = _portfolio_security_allowed(ticker=ticker, symbol=symbol)
else:
keep = symbol.upper() in PORTFOLIO_CASH_SYMBOLS
if keep:
subset[addr] = meta
return subset
# Only explicitly trusted raw TRON assets are included in NAV. Public wallets
# commonly receive unsolicited TRC-20 dust/spam; valuing every token returned
# by TronGrid can let an attacker inflate reported NAV. Add an intentional
# holding here if ALEF later holds another raw TRC-20 asset.
TRON_TRUSTED_ASSETS = {
"TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t": {"symbol": "USDT", "coingecko_id": "tether"},
"TEkxiTehnzSmSe2XqrBj4w32RUN966rdz8": {"symbol": "USDC", "coingecko_id": "usd-coin"},
"TN3W4H6rK2ce4vX9YnFQHwKENnHjoxb3m9": {"symbol": "BTC", "coingecko_id": "bitcoin"},
}
# Venus's official BNB-chain toolkit exposes Flux/Fluid positions separately
# from ordinary Venus Core/isolated-pool vTokens. These are the currently
# published BNB Flux fTokens.
VENUS_FLUX_FTOKENS = [
{"symbol": "fU", "address": "0x007df53Cda786450Cf8145a73B2748B241a0069c"},
{"symbol": "fUSDC", "address": "0xfE60462E93cee34319F48Cfc6AcFbc13c2882Df9"},
{"symbol": "fUSDT", "address": "0xA5b8FCa32E5252B0B58EAbf1A8c79d958F8EE6A2"},
{"symbol": "fWBNB", "address": "0x527C2a0B8A3eDD9696B4A9443ef66Ec30fD7B84a"},
]
# Official Venus Agent Skills / Fluid deployment resolver for BNB Flux positions.
# Unlike ERC-20 balanceOf(), this resolver returns the user's actual underlyingAssets.
VENUS_FLUX_LENDING_RESOLVER = "0x48D32f49aFeAEC7AE66ad7B9264f446fc11a1569"
WBNB_BSC = "0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c"
# Current plus legacy ALEF mints must never be treated as portfolio backing.
# 9B3... was an older ALEF mint that can still be present in the reserve wallet.
ALEF_LEGACY_MINTS = {
"9B3rXYVdL2iUxGnY54TKhR9NroqGB1RBAivVQWb26Zqt",
}
METAPLEX_METADATA_PROGRAM = "metaqbxxUerdq28cj1RbAWkYQm3ybzjb6a8bt518x1s"
# Public indexers used only to enumerate raw wallet ERC-20 balances.
# Protocol receipt/debt tokens are excluded to avoid double-counting.
EVM_TOKEN_INDEXERS = {
"ethereum": [
"https://eth.blockscout.com",
],
"bsc": [],
}
SOLANA_TOKEN_PROGRAMS = [
"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
"TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb", # Token-2022
]
# Fee-state schema/version. v18 keeps pure NAV/supply pricing, reads the known
# Venus/Fluid vault NFT directly, and derives the USDC reserve from the live Raydium pool.
ALEF_NAV_ENGINE_VERSION = 16
# Aave V3 Ethereum main market / Pool.
# The ALEF reserve EVM wallet is discovered from alef.money.
AAVE_ETHEREUM_MARKETS = [
{"address": "0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2", "chainId": 1}
]
# Optional adjustment for assets/liabilities that are part of NAV but
# are NOT held in Aave, Morpho, JustLend or Venus (for example an LP,
# exchange balance, TON position, or other reserve not yet automated).
MANUAL_ASSETS_USD = Decimal("0")
MANUAL_LIABILITIES_USD = Decimal("0")
# USDC liquidity reserve β read live from the ALEF/USDC Raydium pool on Solana.
# The operator maintains the pool balance close to the committed reserve level;
# whatever USDC is in the pool at query time is used directly as the NAV input.
# No manual number is asserted β the value is fully on-chain verifiable.
# Date from which the current fee period is accruing.
# Updated automatically by settle_fees() each time fees are collected.
FEE_ACCRUAL_START = datetime(2026, 6, 17, 15, 53, 5, tzinfo=timezone.utc) # ALEF deployed on Solana
RAYDIUM_MANUAL_FALLBACK_USD = Decimal("0")
RAYDIUM_CLMM_PROGRAM_ID = "CAMMCzo5YL8w4VFF8KVHrK22GGUsp5VTaW7grrKgrWqK"
# Fail closed: do not publish an ALEF price from a partial NAV if one
# of the required protocol reads fails.
ALEF_STRICT_MODE = True
# Current official values from alef.money, used only if scraping the
# site fails or the page layout changes.
ALEF_ADDRESS_FALLBACKS = {
"evm": "0x34292E356Def567bA65a2351ec1DD2b738ec4A57",
"tron": "TFMDSKrH2FJx3pdGACb5oHoKPJRzaH8dKv",
"ton": "UQDd7e4W8l4W0UMMwT--rqOjcd9-O8mLPYYZDoc4uFsS7YyY",
"solana": "9JRZdE4rcSUQRDRUTqNrqsFtxm7caT4GfyiNxFJqBHUe",
"alef_mint": "FBHd9upXFkeWSwe9qEdcgRLa4Y6uzsLCSawrpfPkQZGg",
}
# ===============================================================
# HTTP helpers
# ===============================================================
def _http_session(max_retries=3, backoff_factor=1.0) -> requests.Session:
session = requests.Session()
retry = Retry(
total=max_retries,
read=max_retries,
connect=max_retries,
backoff_factor=backoff_factor,
status_forcelist=[429, 500, 502, 503, 504],
allowed_methods=["GET", "POST"],
respect_retry_after_header=True,
)
adapter = HTTPAdapter(max_retries=retry)
session.mount("https://", adapter)
session.mount("http://", adapter)
session.headers.update({"User-Agent": "Alef-NAV-Updater/1.0"})
return session
def _coin_gecko_session(max_retries=3, backoff_factor=1.0) -> requests.Session:
return _http_session(max_retries=max_retries, backoff_factor=backoff_factor)
def _json_get(url, *, params=None, timeout=20, headers=None):
s = _http_session()
r = s.get(url, params=params, timeout=timeout, headers=headers)
r.raise_for_status()
return r.json()
def _json_post(url, payload, *, timeout=25, headers=None):
s = _http_session()
h = {"Content-Type": "application/json"}
if headers:
h.update(headers)
r = s.post(url, json=payload, timeout=timeout, headers=h)
r.raise_for_status()
data = r.json()
if isinstance(data, dict) and data.get("errors"):
raise RuntimeError(f"GraphQL error from {url}: {data['errors']}")
return data
def D(x, default="0") -> Decimal:
if x is None or x == "":
return Decimal(default)
return Decimal(str(x))
# ===============================================================
# Crypto via CoinGecko (with yfinance fallback)
# ===============================================================
def fetch_crypto_prices(symbols, timeout=20):
"""
Try CoinGecko first; fall back to yfinance (BTC-USD style) on failure.
Returns a list of floats in the same order as `symbols`.
"""
cg_map_primary = {
'BTC': 'bitcoin', 'ETH': 'ethereum', 'BNB': 'binancecoin',
'XRP': 'ripple', 'DOGE': 'dogecoin', 'ADA': 'cardano',
'POL': 'polygon-ecosystem-token', 'TRX': 'tron',
'XAUT': 'tether-gold', 'SOL': 'solana',
'LEO': 'leo-token', 'PAXG': 'pax-gold', 'GRAM': 'gram'
}
cg_map_fallback = {'POL': 'matic-network'}
yf_map = {
'BTC': 'BTC-USD', 'ETH': 'ETH-USD', 'BNB': 'BNB-USD',
'XRP': 'XRP-USD', 'DOGE': 'DOGE-USD', 'ADA': 'ADA-USD',
'POL': 'POL-USD', 'TRX': 'TRX-USD', 'SOL': 'SOL-USD',
'XAUT': 'XAUT-USD', 'LEO': 'LEO-USD', 'PAXG': 'PAXG-USD'
}
ids_to_query = set(cg_map_primary.values()) | set(cg_map_fallback.values())
url = (
"https://api.coingecko.com/api/v3/simple/price"
f"?ids={','.join(sorted(ids_to_query))}&vs_currencies=usd"
)
session = _coin_gecko_session()
cg_data = None
try:
resp = session.get(url, timeout=timeout)
resp.raise_for_status()
cg_data = resp.json()
except Exception as e:
_audit_log(f"[WARN] CoinGecko unavailable, falling back to yfinance: {e}", file=sys.stderr)
prices = []
for sym in symbols:
if not sym:
prices.append(None)
continue
val = None
if cg_data:
coin_id = cg_map_primary.get(sym)
val = cg_data.get(coin_id, {}).get('usd') if coin_id else None
if val is None and sym in cg_map_fallback:
val = cg_data.get(cg_map_fallback[sym], {}).get('usd')
if val is None and sym in yf_map:
try:
tk = yf.Ticker(yf_map[sym])
fi = getattr(tk, "fast_info", None)
if fi and fi.get("last_price") is not None:
val = float(fi["last_price"])
else:
hist = tk.history(period="1d", interval="1d")
if not hist.empty:
val = float(hist["Close"].iloc[-1])
except Exception as e:
_audit_log(f"[WARN] yfinance fallback failed for {sym}: {e}", file=sys.stderr)
prices.append(float(val) if val is not None else None)
return prices
# ===============================================================
# Stocks via Yahoo Finance
# ===============================================================
def fetch_stock_prices(tickers):
"""
Return a list of floats (last price) in same order as `tickers`,
using yfinance fast_info with a safe fallback.
"""
out = []
for t in tickers:
if not t:
out.append(None)
continue
price = None
try:
tk = yf.Ticker(t)
fi = getattr(tk, "fast_info", None)
if fi and "last_price" in fi and fi["last_price"] is not None:
price = float(fi["last_price"])
else:
hist = tk.history(period="1d", interval="1d")
if not hist.empty and "Close" in hist.columns:
price = float(hist["Close"].iloc[-1])
except Exception:
price = None
out.append(price)
return out
# ===============================================================
# alef.money address discovery
# ===============================================================
def _extract_after_label(text, label, pattern, span=1200):
i = text.lower().find(label.lower())
if i < 0:
return None
m = re.search(pattern, text[i:i + span])
return m.group(0) if m else None
def fetch_alef_addresses(timeout=20):
"""
Read the published reserve addresses and ALEF mint from alef.money.
Falls back to the currently published values if the page cannot be parsed.
"""
found = {}
try:
r = _http_session().get(ALEF_WEBSITE, timeout=timeout)
r.raise_for_status()
raw = html_lib.unescape(r.text)
plain = re.sub(r"<[^>]+>", " ", raw)
plain = re.sub(r"\s+", " ", plain)
found["evm"] = _extract_after_label(
plain,
"Reserve Address (Ethereum & BNB Chain)",
r"0x[a-fA-F0-9]{40}",
)
found["tron"] = _extract_after_label(
plain,
"Reserve Address (Tron)",
r"T[1-9A-HJ-NP-Za-km-z]{33}",
)
found["ton"] = _extract_after_label(
plain,
"Reserve Address (TON)",
r"(?:EQ|UQ)[A-Za-z0-9_-]{46}",
)
found["solana"] = _extract_after_label(
plain,
"Reserve Address (Solana)",
r"[1-9A-HJ-NP-Za-km-z]{32,44}",
)
found["alef_mint"] = _extract_after_label(
plain,
"Token Mint",
r"[1-9A-HJ-NP-Za-km-z]{32,44}",
)
except Exception as e:
_audit_log(f"[WARN] Could not read addresses from alef.money: {e}", file=sys.stderr)
for key, fallback in ALEF_ADDRESS_FALLBACKS.items():
if not found.get(key):
found[key] = fallback
_audit_log(f"[WARN] Using fallback {key} address: {fallback}", file=sys.stderr)
return found
# ===============================================================
# Aave (V4 positions first; V3 legacy fallback)
# ===============================================================
def _graphql_data(url, query):
payload = _json_post(url, {"query": query}, timeout=25)
if payload.get("errors"):
msgs = "; ".join(str(x.get("message", x)) for x in payload["errors"])
raise RuntimeError(f"GraphQL error from {url}: {msgs}")
return payload.get("data") or {}
def _aave_exchange_value(node):
"""Extract the quoted value from Aave ExchangeAmount/WithChange shapes."""
node = node or {}
# WithChange is normally {current:{value,symbol}, ...}. Keep a few
# shape fallbacks so minor schema presentation changes do not zero NAV.
for key in ("current", "amount"):
if isinstance(node, dict) and isinstance(node.get(key), dict):
v = _aave_exchange_value(node[key])
if v is not None:
return v
if isinstance(node, dict) and node.get("value") is not None:
return D(node.get("value"))
return None
def _fetch_aave_v4(wallet):
# The V4 user summary is the least fragile accounting endpoint: Aave
# exposes totalPositions, totalSupplied and totalDebt directly. Alef's
# published reserve description specifies that its Aave positions are on
# Ethereum, so the reserve-address V4 summary is the desired Aave book.
summary_query = f"""
query AlefAaveSummary {{
userSummary(request: {{ user: "{wallet}" }}) {{
totalPositions
totalSupplied {{ value symbol }}
totalDebt {{ value symbol }}
}}
}}
"""
summary_error = None
try:
data = _graphql_data(AAVE_V4_GRAPHQL, summary_query)
summary = data.get("userSummary")
if summary:
assets = _aave_exchange_value(summary.get("totalSupplied")) or Decimal("0")
liabilities = _aave_exchange_value(summary.get("totalDebt")) or Decimal("0")
return {
"protocol": "Aave",
"version": "v4",
"position_count": int(summary.get("totalPositions") or 0),
"assets_usd": assets,
"liabilities_usd": liabilities,
"summary": summary,
"positions": [],
"supplies": [],
"borrows": [],
}
except Exception as e:
summary_error = e
# Fallback to chain-filtered positions if the summary query/schema changes.
query = f"""
query AlefAavePositions {{
userPositions(request: {{
user: "{wallet}",
filter: {{ chainIds: [1] }}
}}) {{
id
totalSupplied {{ current {{ value symbol }} }}
totalDebt {{ current {{ value symbol }} }}
spoke {{ name chain {{ chainId name }} }}
}}
}}
"""
try:
data = _graphql_data(AAVE_V4_GRAPHQL, query)
positions = data.get("userPositions") or []
assets = Decimal("0")
liabilities = Decimal("0")
for pos in positions:
supplied = _aave_exchange_value(pos.get("totalSupplied"))
debt = _aave_exchange_value(pos.get("totalDebt"))
if supplied is not None:
assets += supplied
if debt is not None:
liabilities += debt
return {
"protocol": "Aave",
"version": "v4",
"position_count": len(positions),
"assets_usd": assets,
"liabilities_usd": liabilities,
"positions": positions,
"supplies": [],
"borrows": [],
}
except Exception as e:
if summary_error:
raise RuntimeError(f"Aave V4 summary failed ({summary_error}); positions query failed ({e})")
raise
def _fetch_aave_v3(wallet):
markets_literal = ",".join(
f'{{address:"{m["address"]}",chainId:{m["chainId"]}}}'
for m in AAVE_ETHEREUM_MARKETS
)
query = f"""
{{
userSupplies(request: {{
markets: [{markets_literal}], user: "{wallet}"
}}) {{ currency {{ symbol }} balance {{ amount {{ value }} usd }} }}
userBorrows(request: {{
markets: [{markets_literal}], user: "{wallet}"
}}) {{ currency {{ symbol }} debt {{ amount {{ value }} usd }} }}
}}
"""
data = _graphql_data(AAVE_V3_GRAPHQL, query)
supplies = data.get("userSupplies") or []
borrows = data.get("userBorrows") or []
assets = sum((D((x.get("balance") or {}).get("usd")) for x in supplies), Decimal("0"))
liabilities = sum((D((x.get("debt") or {}).get("usd")) for x in borrows), Decimal("0"))
return {
"protocol": "Aave",
"version": "v3",
"position_count": int(bool(supplies or borrows)),
"assets_usd": assets,
"liabilities_usd": liabilities,
"supplies": supplies,
"borrows": borrows,
}
def fetch_aave_position_usd(wallet):
"""
Read Aave V4 positions first. If V4 is unavailable or genuinely has no
positions, check legacy V3. We deliberately do not add V3 and V4 totals
together automatically, which protects NAV from accidental duplication
during migrations.
"""
v4_error = None
try:
v4 = _fetch_aave_v4(wallet)
if v4["position_count"] > 0 or v4["assets_usd"] or v4["liabilities_usd"]:
return v4
except Exception as e:
v4_error = e
_audit_log(f"[WARN] Aave V4 read failed; trying V3 fallback: {e}", file=sys.stderr)
try:
v3 = _fetch_aave_v3(wallet)
if v3["assets_usd"] or v3["liabilities_usd"]:
return v3
if v4_error:
raise RuntimeError(f"Aave V4 failed ({v4_error}); V3 returned no position")
# V4 succeeded with zero and V3 is also zero.
return v3
except Exception:
if v4_error:
raise
raise
# ===============================================================
# Morpho (Ethereum)
# ===============================================================
def fetch_morpho_position_usd(wallet):
query = f"""
{{
userByAddress(chainId: 1, address: "{wallet}") {{
marketPositions {{
market {{ marketId }}
state {{
supplyAssetsUsd
borrowAssetsUsd
collateralUsd
}}
}}
vaultPositions {{
vault {{ address name }}
state {{ assetsUsd }}
}}
vaultV2Positions {{
vault {{ address name }}
assetsUsd
}}
}}
}}
"""
data = _json_post(MORPHO_GRAPHQL, {"query": query}).get("data", {})
user = data.get("userByAddress")
if not user:
return {
"protocol": "Morpho",
"assets_usd": Decimal("0"),
"liabilities_usd": Decimal("0"),
"market_positions": [],
"vault_positions": [],
"vault_v2_positions": [],
}
assets = Decimal("0")
liabilities = Decimal("0")
markets = user.get("marketPositions") or []
for p in markets:
st = p.get("state") or {}
assets += D(st.get("supplyAssetsUsd"))
assets += D(st.get("collateralUsd"))
liabilities += D(st.get("borrowAssetsUsd"))
vaults = user.get("vaultPositions") or []
for p in vaults:
assets += D((p.get("state") or {}).get("assetsUsd"))
vaults_v2 = user.get("vaultV2Positions") or []
for p in vaults_v2:
assets += D(p.get("assetsUsd"))
return {
"protocol": "Morpho",
"assets_usd": assets,
"liabilities_usd": liabilities,
"market_positions": markets,
"vault_positions": vaults,
"vault_v2_positions": vaults_v2,
}
# ===============================================================
# JustLend V1 + V2 (TRON)
# ===============================================================
def fetch_justlend_position_usd(wallet, trx_usd):
trx_usd = D(trx_usd)
if trx_usd <= 0:
raise RuntimeError("TRX/USD price is required for JustLend V1 valuation")
markets_resp = _json_get(f"{JUSTLEND_API}/lend/jtoken")
if markets_resp.get("code") != 0:
raise RuntimeError(f"JustLend market API error: {markets_resp}")
markets = (markets_resp.get("data") or {}).get("tokenList") or []
market_by_jtoken = {m.get("address"): m for m in markets if m.get("address")}
account_resp = _json_get(
f"{JUSTLEND_API}/lend/account",
params={"addresses": wallet, "pageNo": 1, "pageSize": 100},
)
if account_resp.get("code") != 0:
raise RuntimeError(f"JustLend account API error: {account_resp}")
account_list = (account_resp.get("data") or {}).get("list") or []
v1_assets = Decimal("0")
v1_liabilities = Decimal("0")
v1_positions = []
for acct in account_list:
if (acct.get("address") or "").lower() != wallet.lower():
continue
for p in acct.get("tokens") or []:
market = market_by_jtoken.get(p.get("address"))
if not market:
raise RuntimeError(f"Missing JustLend market metadata for {p.get('address')}")
underlying_price_trx = D(market.get("underlyingPriceInTrx"))
price_usd = underlying_price_trx * trx_usd
supplied = D(p.get("supplyBalanceUnderlying"))
borrowed = D(p.get("borrowBalanceUnderlying"))
supply_usd = supplied * price_usd
borrow_usd = borrowed * price_usd
v1_assets += supply_usd
v1_liabilities += borrow_usd
v1_positions.append({
"symbol": p.get("underlyingSymbol"),
"supply_usd": str(supply_usd),
"borrow_usd": str(borrow_usd),
})
# JustLend V2 / Moolah is a separate architecture, so it is additive.
v2_resp = _json_get(
f"{JUSTLEND_API}/v2/index/position",
params={"address": wallet},
)
if v2_resp.get("code") != 200:
raise RuntimeError(f"JustLend V2 position API error: {v2_resp}")
v2 = v2_resp.get("data") or {}
v2_assets = D(v2.get("totalCollateralUsd")) + D(v2.get("totalSupplyUsd"))
v2_liabilities = D(v2.get("totalBorrowUsd"))
return {
"protocol": "JustLend",
"assets_usd": v1_assets + v2_assets,
"liabilities_usd": v1_liabilities + v2_liabilities,
"v1_assets_usd": v1_assets,
"v1_liabilities_usd": v1_liabilities,
"v2_assets_usd": v2_assets,
"v2_liabilities_usd": v2_liabilities,
"v1_positions": v1_positions,
"v2_position": v2,
"jtoken_addresses": list(market_by_jtoken.keys()),
}
# ===============================================================
# Venus (BNB Chain) -- official market API + on-chain snapshots
# ===============================================================
SEL_GET_ACCOUNT_SNAPSHOT = "c37f68e2" # getAccountSnapshot(address)
SEL_MINTED_VAI = "2bc7e29e" # mintedVAIs(address)
def _pad_evm_addr(addr):
return addr.lower().replace("0x", "").rjust(64, "0")
def _decode_uint256_words(hexdata):
raw = hexdata[2:] if hexdata.startswith("0x") else hexdata
if len(raw) % 64 != 0:
return []
return [int(raw[i:i + 64], 16) for i in range(0, len(raw), 64)]
def _evm_rpc_call(rpc_url, to, data):
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "eth_call",
"params": [{"to": to, "data": data}, "latest"],
}
out = _json_post(rpc_url, payload, timeout=15)
if out.get("error"):
raise RuntimeError(out["error"])
return out.get("result", "0x")
def _venus_all_markets():
"""Fetch every listed BNB-chain Venus Core/isolated market with pagination."""
markets = []
page = 0
limit = 100
seen = set()
while True:
resp = _json_get(
f"{VENUS_API}/markets",
params={"chainId": 56, "limit": limit, "page": page},
headers={"accept-version": "stable"},
)
rows = resp.get("result") or []
if not isinstance(rows, list):
raise RuntimeError(f"Unexpected Venus market API response: {resp}")
for m in rows:
addr = (m.get("address") or "").lower()
if addr and addr not in seen:
seen.add(addr)
markets.append(m)
total = int(D(resp.get("total"), str(len(markets))))
if not rows or len(markets) >= total:
break
page += 1
if page > 25:
raise RuntimeError("Venus market pagination exceeded safety limit")
return markets
def _evm_uint_call(rpc_url, contract, selector, arg_hex=""):
ret = _evm_rpc_call(rpc_url, contract, "0x" + selector + arg_hex)
words = _decode_uint256_words(ret)
if not words:
raise RuntimeError(f"Empty uint return from {contract} selector {selector}")
return words[0]
def _evm_address_call(rpc_url, contract, selector):
ret = _evm_rpc_call(rpc_url, contract, "0x" + selector)
raw = ret[2:] if ret.startswith("0x") else ret
if len(raw) < 64:
raise RuntimeError(f"Invalid address return from {contract}")
return "0x" + raw[-40:]
def _evm_rpc_method(rpc_url, method, params):
payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
out = _json_post(rpc_url, payload, timeout=20)
if out.get("error"):
raise RuntimeError(out["error"])
return out.get("result")
def _evm_selector_from_rpc(rpc_url, signature):
"""Ask the EVM node for keccak(signature), avoiding a web3.py dependency."""
sig_hex = "0x" + signature.encode("utf-8").hex()
hashed = _evm_rpc_method(rpc_url, "web3_sha3", [sig_hex])
if not isinstance(hashed, str) or len(hashed) < 10:
raise RuntimeError(f"web3_sha3 returned invalid result for {signature}: {hashed}")
return hashed[:10]
def _abi_u256(raw, off):
if off < 0 or off + 32 > len(raw):
raise RuntimeError(f"ABI offset outside return data: {off}")
return int.from_bytes(raw[off:off + 32], "big")
def _abi_address(raw, off):
if off < 0 or off + 32 > len(raw):
raise RuntimeError(f"ABI address offset outside return data: {off}")
return "0x" + raw[off + 12:off + 32].hex()
def _abi_string(raw, tuple_base, rel_off):
pos = tuple_base + rel_off
n = _abi_u256(raw, pos)
start = pos + 32
end = start + n
if end > len(raw):
raise RuntimeError("ABI string exceeds return data")
return raw[start:end].decode("utf-8", errors="replace").rstrip("\x00")
def _decode_flux_user_positions(hexdata):
"""
Decode only the fields Alef needs from Fluid LendingResolver.getUserPositions().
Official return shape:
((fTokenDetails...), (fTokenShares, underlyingAssets,
underlyingBalance, allowance))[]
fTokenDetails contains dynamic name/symbol strings, so each array element is
dynamic. We intentionally decode only tokenAddress, symbol, decimals, asset,
and the four user-position uints. This keeps the script dependency-free.
"""
if not hexdata or hexdata == "0x":
return []
raw = bytes.fromhex(hexdata[2:] if hexdata.startswith("0x") else hexdata)
if len(raw) < 64:
raise RuntimeError("Flux resolver returned too little ABI data")
array_base = _abi_u256(raw, 0)
n = _abi_u256(raw, array_base)
offsets_base = array_base + 32
if n > 1000:
raise RuntimeError(f"Implausible Flux position count: {n}")
rows = []
for i in range(n):
# For a dynamic array of dynamic tuples, offsets are relative to the
# tuple-offset table (i.e. immediately after the array length word).
elem_rel = _abi_u256(raw, offsets_base + 32 * i)
elem_base = offsets_base + elem_rel
# Outer tuple = (dynamic fTokenDetails, static userPosition tuple).
fdetails_rel = _abi_u256(raw, elem_base)
fbase = elem_base + fdetails_rel
shares = _abi_u256(raw, elem_base + 32)
underlying_assets = _abi_u256(raw, elem_base + 64)
underlying_balance = _abi_u256(raw, elem_base + 96)
allowance = _abi_u256(raw, elem_base + 128)
# fTokenDetails field order from the official Venus Flux ABI:
# 0 tokenAddress, 1 eip2612Deposits, 2 isNativeUnderlying,
# 3 name(offset), 4 symbol(offset), 5 decimals, 6 asset, ...
token_address = _abi_address(raw, fbase)
symbol_rel = _abi_u256(raw, fbase + 4 * 32)
symbol = _abi_string(raw, fbase, symbol_rel)
decimals = _abi_u256(raw, fbase + 5 * 32)
asset = _abi_address(raw, fbase + 6 * 32)
is_native = bool(_abi_u256(raw, fbase + 2 * 32))
rows.append({
"symbol": symbol,
"fToken": token_address,
"underlying": asset,
"decimals": int(decimals),
"is_native": is_native,
"shares_raw": shares,
"underlying_assets_raw": underlying_assets,
"underlying_balance_raw": underlying_balance,
"allowance_raw": allowance,
})
return rows
def _fetch_venus_flux_supply_resolver_usd(wallet, bnb_usd=None):
"""Use Flux resolver for quantities; use our normal price functions for USD marks."""
selector = _evm_selector_from_rpc(BNB_RPC, "getUserPositions(address)")
data = selector + _pad_evm_addr(wallet)
ret = _evm_rpc_call(BNB_RPC, VENUS_FLUX_LENDING_RESOLVER, data)
rows = _decode_flux_user_positions(ret)
total = Decimal("0")
positions = []
for row in rows:
assets_raw = int(row["underlying_assets_raw"])
if assets_raw <= 0:
continue
amount = Decimal(assets_raw) / (Decimal(10) ** int(row["decimals"]))
sym = str(row.get("symbol") or "").upper()
if "WBNB" in sym or sym == "FBNB":
price = _nav_crypto_price_usd("BNB") or (D(bnb_usd) if bnb_usd is not None else None)
elif "USDC" in sym:
price = Decimal("1")
elif "USDT" in sym or sym == "FU":
price = Decimal("1")
else:
price = None
if price is None or price <= 0:
_audit_log(f"[WARN] Flux position {sym} found but no configured Yahoo/CoinGecko price mapping", file=sys.stderr)
continue
value = amount * price
total += value
positions.append({
**row, "amount": amount, "source": "flux-resolver",
"price_usd": str(price), "supply_usd": str(value), "borrow_usd": "0",
"price_source": "coingecko/yfinance",
})
return total, positions, rows
def _fetch_venus_flux_supply_direct_usd(wallet, bnb_usd=None):
"""Legacy Flux quantity fallback; USD marks come from our normal price functions."""
SEL_BALANCE_OF = "70a08231"
SEL_CONVERT_TO_ASSETS = "07a2d13a"
SEL_ASSET = "38d52e0f"
SEL_DECIMALS = "313ce567"
total = Decimal("0")
positions = []
for ft in VENUS_FLUX_FTOKENS:
ftoken = ft["address"]
try:
shares = _evm_uint_call(BNB_RPC, ftoken, SEL_BALANCE_OF, _pad_evm_addr(wallet))
if shares == 0:
continue
assets_raw = _evm_uint_call(BNB_RPC, ftoken, SEL_CONVERT_TO_ASSETS, hex(shares)[2:].rjust(64, "0"))
underlying = _evm_address_call(BNB_RPC, ftoken, SEL_ASSET)
decimals = _evm_uint_call(BNB_RPC, underlying, SEL_DECIMALS)
amount = Decimal(assets_raw) / (Decimal(10) ** int(decimals))
except Exception:
continue
sym = str(ft["symbol"] or "").upper()
if "WBNB" in sym:
price = _nav_crypto_price_usd("BNB") or (D(bnb_usd) if bnb_usd is not None else None)
elif "USDC" in sym or "USDT" in sym or sym == "FU":
price = Decimal("1")
else:
price = None
if price is None or price <= 0:
continue
value = amount * price
total += value
positions.append({
"source": "flux-direct-fallback", "symbol": ft["symbol"], "fToken": ftoken,
"underlying": underlying, "amount": amount, "price_usd": str(price),
"supply_usd": str(value), "borrow_usd": "0", "price_source": "coingecko/yfinance",
})
return total, positions
def _fetch_venus_flux_supply_usd(wallet, bnb_usd=None):
"""Resolver-first Flux reader, with direct-fToken fallback only on resolver failure."""
try:
total, positions, all_rows = _fetch_venus_flux_supply_resolver_usd(wallet, bnb_usd=bnb_usd)
raw_nonzero = sum(1 for r in all_rows if int(r.get("underlying_assets_raw", 0)) > 0)
return total, positions, "resolver", len(all_rows), raw_nonzero
except Exception as e:
_audit_log(f"[WARN] Venus Flux resolver failed; trying direct fToken fallback: {e}", file=sys.stderr)
total, positions = _fetch_venus_flux_supply_direct_usd(wallet, bnb_usd=bnb_usd)
return total, positions, "direct-fallback", len(VENUS_FLUX_FTOKENS), len(positions)
ZERO_EVM_ADDRESSES = {
"0x0000000000000000000000000000000000000000",
"0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
}
def _fluid_primary_asset(pair):
"""Return token0 plus whether a token1 leg is present."""
pair = pair or {}
token0 = pair.get("token0") or {}
token1 = pair.get("token1") or {}
a1 = (token1.get("address") or "").lower()
has_token1 = bool(a1 and a1 not in ZERO_EVM_ADDRESSES)
return token0, has_token1
def _fluid_raw_asset_value(raw_amount, token):
"""Value a Fluid amount from our CoinGecko/yfinance/cash price source."""
if not token:
return Decimal("0"), Decimal("0"), "?"
decimals = int(D(token.get("decimals"), "18"))
amount = D(raw_amount) / (Decimal(10) ** decimals)
symbol = token.get("symbol") or token.get("name") or "?"
price = _portfolio_raw_token_price_usd(symbol)
if price is None or price <= 0:
raise RuntimeError(f"No configured NAV price for Fluid token {symbol}")
return amount, amount * price, symbol
def _fetch_venus_flux_vault_nfts_usd(wallet):
"""
Read Fluid/Venus Flux borrow-vault NFTs on BNB Chain.
The lending resolver used above only reports fToken lending. Borrow/multiply
positions are ERC-721 vault positions and are exposed separately by Fluid's
official /v2/{chainId}/users/{wallet}/nfts API.
T1/single-asset vaults can be valued directly from supply/borrow amounts.
T2-T4 smart collateral/debt vaults use DEX shares, so we fail closed rather
than pretending the share count is a token amount.
"""
data = _json_get(f"{FLUID_API}/v2/56/users/{wallet}/nfts", timeout=25)
if isinstance(data, dict):
rows = data.get("data") or data.get("nfts") or data.get("positions") or []
elif isinstance(data, list):
rows = data
else:
rows = []
assets = Decimal("0")
liabilities = Decimal("0")
positions = []
unsupported = []
# ALEF's canonical Venus exposure is the known Flux vault NFT below.
# The API is already scoped to ALEF's wallet; filtering by NFT id avoids
# accidentally pulling unrelated/legacy Fluid positions into NAV.
wanted_id = str(VENUS_FLUX_VAULT_NFT_ID)
matching_rows = [
row for row in rows
if isinstance(row, dict) and str(row.get("id") or "") == wanted_id
]
if not matching_rows:
raise RuntimeError(
f"Venus/Fluid vault NFT #{wanted_id} was not found for wallet {wallet}"
)
for row in matching_rows:
supply_raw = D(row.get("supply"))
borrow_raw = D(row.get("borrow"))
dust_raw = D(row.get("dustBorrow"))
# Fully empty/liquidated records do not contribute to current NAV.
if supply_raw <= 0 and borrow_raw <= 0 and dust_raw <= 0:
continue
vault = row.get("vault") or {}
supply_token, supply_has_second = _fluid_primary_asset(vault.get("supplyToken"))
borrow_token, borrow_has_second = _fluid_primary_asset(vault.get("borrowToken"))
vault_type = str(vault.get("type") or "?")
# For T2/T3/T4, supply/borrow can be DEX-share amounts. Directly
# multiplying them by token0 price would materially misstate NAV.
if vault_type != "1" or supply_has_second or borrow_has_second:
unsupported.append({
"id": str(row.get("id") or "?"),
"vault": vault.get("address"),
"type": vault_type,
"supply_raw": str(supply_raw),
"borrow_raw": str(borrow_raw),
})
continue
s_amount, s_usd, s_symbol = _fluid_raw_asset_value(supply_raw, supply_token)
b_amount, b_usd, b_symbol = _fluid_raw_asset_value(borrow_raw, borrow_token)
# dustBorrow is protocol accounting dust. Include it only if the main
# borrow is zero; adding it unconditionally may double-count the same
# debt depending on API version.
if borrow_raw <= 0 and dust_raw > 0:
d_amount, d_usd, _ = _fluid_raw_asset_value(dust_raw, borrow_token)
b_amount += d_amount
b_usd += d_usd
assets += s_usd
liabilities += b_usd
positions.append({
"source": "flux-vault-nft",
"nft_id": str(row.get("id") or "?"),
"vault": vault.get("address"),
"vault_type": vault_type,
"supply_symbol": s_symbol,
"supply_amount": str(s_amount),
"supply_usd": str(s_usd),
"borrow_symbol": b_symbol,
"borrow_amount": str(b_amount),
"borrow_usd": str(b_usd),
"owner": row.get("ownerAddress"),
})
if unsupported:
detail = ", ".join(f"NFT {x['id']} type={x['type']}" for x in unsupported)
raise RuntimeError(
"Venus/Fluid found smart-vault Flux position(s) that require DEX-share "
f"conversion before NAV can be published: {detail}"
)
return assets, liabilities, positions, len(matching_rows)
def fetch_venus_position_usd(wallet, bnb_usd=None):
"""Read ALEF's known Venus/Fluid Flux vault directly.
ALEF currently has no Venus Core/isolated-pool or Flux fToken lending
exposure that needs discovery. Scanning ~87 Venus markets was slow and
caused noisy public-RPC batch failures. The canonical Venus position is
Fluid/Flux vault NFT #800, so NAV reads only the wallet-scoped Fluid NFT
endpoint and selects that NFT.
"""
flux_vault_assets, flux_vault_liabilities, flux_vault_positions, flux_vault_rows = (
_fetch_venus_flux_vault_nfts_usd(wallet)
)
return {
"protocol": "Venus",
"assets_usd": flux_vault_assets,
"liabilities_usd": flux_vault_liabilities,
"core_assets_usd": Decimal("0"),
"core_liabilities_usd": Decimal("0"),
"flux_assets_usd": flux_vault_assets,
"flux_liabilities_usd": flux_vault_liabilities,
"positions": flux_vault_positions,
"core_position_count": 0,
"flux_position_count": len(flux_vault_positions),
"flux_lend_position_count": 0,
"flux_vault_position_count": len(flux_vault_positions),
"flux_source": f"fluid-nft-api:nft-{VENUS_FLUX_VAULT_NFT_ID}",
"flux_rows_seen": 0,
"flux_raw_position_count": 0,
"flux_vault_rows_seen": flux_vault_rows,
"market_count_scanned": 0,
"venus_snapshot_rpc_batches": 0,
"venus_vai_rpc_batches": 0,
"receipt_token_addresses": [x["address"] for x in VENUS_FLUX_FTOKENS],
}
# ===============================================================
# Raw wallet holdings: EVM, TRON, Solana and TON/GRAM
# ===============================================================
# Symbols that are protocol accounting/receipt tokens rather than extra
# portfolio assets. Their underlying value is already counted above.
def _looks_like_protocol_receipt(symbol):
s = (symbol or "").strip().lower()
return (
s.startswith("variabledebt") or
s.startswith("stabledebt") or
s.startswith("aeth") or
s.startswith("aave") and "debt" in s or
s.startswith("vbnb") or
s.startswith("vbusd") or
s.startswith("vusdc") or
s.startswith("vusdt") or
s.startswith("jtrx") or
s.startswith("jusdc") or
s.startswith("jusdt")
)
def _coingecko_simple_id_prices(ids):
ids = [x for x in dict.fromkeys(ids) if x]
if not ids:
return {}
data = _json_get(
"https://api.coingecko.com/api/v3/simple/price",
params={"ids": ",".join(ids), "vs_currencies": "usd"},
)
return {i: D((data.get(i) or {}).get("usd")) for i in ids if (data.get(i) or {}).get("usd") is not None}
def _coingecko_contract_prices(platform, contracts):
"""Return {lowercase_contract: USD Decimal} for contracts CoinGecko knows."""
contracts = [x for x in dict.fromkeys(contracts) if x]
out = {}
for i in range(0, len(contracts), 80):
chunk = contracts[i:i + 80]
try:
data = _json_get(
f"https://api.coingecko.com/api/v3/simple/token_price/{platform}",
params={"contract_addresses": ",".join(chunk), "vs_currencies": "usd"},
)
for addr, row in (data or {}).items():
if isinstance(row, dict) and row.get("usd") is not None:
out[addr.lower()] = D(row["usd"])
except Exception as e:
_audit_log(f"[WARN] CoinGecko {platform} contract pricing failed: {e}", file=sys.stderr)
return out
def _dexscreener_prices(chain, contracts):
"""Best-effort USD pricing; supports requested token on either side of a pair."""
contracts = [x for x in dict.fromkeys(contracts) if x]
wanted = {x.lower() for x in contracts}
out = {}
best_liq = {}
for i in range(0, len(contracts), 30):
chunk = contracts[i:i + 30]
try:
data = _json_get(
f"https://api.dexscreener.com/tokens/v1/{chain}/" + ",".join(chunk),
timeout=20,
)
for pair in data if isinstance(data, list) else []:
base = ((pair.get("baseToken") or {}).get("address") or "").lower()
quote = ((pair.get("quoteToken") or {}).get("address") or "").lower()
base_usd = D(pair.get("priceUsd"))
base_in_quote = D(pair.get("priceNative"))
liq = D(((pair.get("liquidity") or {}).get("usd")))
candidates = []
if base in wanted and base_usd > 0:
candidates.append((base, base_usd))
# DexScreener priceUsd is the base token USD price and
# priceNative is base units per one quote unit. Therefore
# quote USD = base USD / (base/quote).
if quote in wanted and base_usd > 0 and base_in_quote > 0:
candidates.append((quote, base_usd / base_in_quote))
for token, price in candidates:
if price > 0 and liq >= best_liq.get(token, Decimal("-1")):
best_liq[token] = liq
out[token] = price
except Exception as e:
_audit_log(f"[WARN] DexScreener {chain} pricing failed: {e}", file=sys.stderr)
return out
def _yf_price_for_token_symbol(symbol):
"""Price tokenized listed securities such as TQQQon from the underlying Yahoo ticker."""
if not symbol:
return None
s = symbol.strip()
upper = s.upper()
# Ondo tokenized securities commonly append 'on' to the listed ticker.
if upper.endswith("ON") and len(upper) > 2:
ticker = upper[:-2]
else:
return None
try:
p = fetch_stock_prices([ticker])[0]
return D(p) if p is not None else None
except Exception:
return None
def _eth_get_native_balance(rpc_url, wallet):
payload = {"jsonrpc": "2.0", "id": 1, "method": "eth_getBalance", "params": [wallet, "latest"]}
data = _json_post(rpc_url, payload, timeout=15)
if data.get("error"):
raise RuntimeError(data["error"])
return Decimal(int(data.get("result", "0x0"), 16)) / (Decimal(10) ** 18)
def _etherscan_token_balances(chain, wallet):
"""Optional Etherscan V2 holdings reader (needs a key; holdings endpoint may be plan-gated)."""
if not ETHERSCAN_API_KEY:
raise RuntimeError("no ETHERSCAN_API_KEY/BSCSCAN_API_KEY configured")
chainid = "1" if chain == "ethereum" else "56"
data = _json_get(
"https://api.etherscan.io/v2/api",
params={
"chainid": chainid,
"module": "account",
"action": "addresstokenbalance",
"address": wallet,
"page": 1,
"offset": 100,
"apikey": ETHERSCAN_API_KEY,
},
timeout=25,
)
if str(data.get("status")) != "1" or not isinstance(data.get("result"), list):
raise RuntimeError(str(data.get("result") or data.get("message") or data))
rows = []
for x in data["result"]:
rows.append({
"value": x.get("TokenQuantity"),
"token": {
"type": "ERC-20",
"address": x.get("TokenAddress"),
"symbol": x.get("TokenSymbol") or "",
"name": x.get("TokenName") or "",
"decimals": x.get("TokenDivisor") or "18",
"exchange_rate": x.get("TokenPriceUSD"),
},
})
return rows
def _blockscout_token_balances(chain, wallet):
errors = []
for base in EVM_TOKEN_INDEXERS.get(chain, []):
try:
data = _json_get(f"{base}/api/v2/addresses/{wallet}/token-balances", timeout=20)
if isinstance(data, list):
return data
errors.append(f"{base}: unexpected response")
except Exception as e:
errors.append(f"{base}: {e}")
# Blockscout's BSC instances have changed/vanished more than once. If an
# Etherscan-family API key is available, use its multichain holdings API.
try:
return _etherscan_token_balances(chain, wallet)
except Exception as e:
errors.append(f"Etherscan V2 fallback: {e}")
raise RuntimeError("; ".join(errors) if errors else f"No token indexer configured for {chain}")
def _evm_batch_eth_call(rpc_url, calls, batch_size=100):
"""Run eth_call in JSON-RPC batches. Returns {key: result_hex}."""
out = {}
calls = list(calls)
for start in range(0, len(calls), batch_size):
chunk = calls[start:start + batch_size]
payload = []
id_to_key = {}
for j, (key, to, data) in enumerate(chunk, 1):
req_id = start + j
id_to_key[req_id] = key
payload.append({
"jsonrpc": "2.0", "id": req_id, "method": "eth_call",
"params": [{"to": to, "data": data}, "latest"],
})
response = _json_post(rpc_url, payload, timeout=30)
if not isinstance(response, list):
raise RuntimeError(f"EVM batch RPC returned non-list response: {response}")
returned = set()
for row in response:
rid = row.get("id")
key = id_to_key.get(rid)
if key is None:
continue
returned.add(key)
if row.get("error"):
# A single xStock deployment should not poison the whole batch.
out[key] = None
else:
out[key] = row.get("result")
for key, _, _ in chunk:
if key not in returned:
out[key] = None
return out
# ===============================================================
# Tokenized securities registries: Binance bStocks + Ondo
# ===============================================================
_BSTOCK_REGISTRY_CACHE = None
_ONDO_EVM_REGISTRY_CACHE = None
_ONDO_SOLANA_REGISTRY_CACHE = None
_ONDO_BINANCE_META_CACHE = None
# Official Binance-published contracts used as a fallback if current registry
# discovery is temporarily unavailable. Dynamic discovery is still attempted
# on every fresh process so newly launched bStocks can be picked up.
_BSTOCK_STATIC_FALLBACK = {
"TSLAB": {"address": "0x5b1910eAaD6450E50f816082Aa078C41F10C292f", "ticker": "TSLA"},
"NVDAB": {"address": "0x02Fca66C1D1aFB4E2A7884261eB00F63598a7436", "ticker": "NVDA"},
"SNDKB": {"address": "0x3eE4dF61bd4F867E349BEaE8bFE07bc31b4850fb", "ticker": "SNDK"},
"CRCLB": {"address": "0x80f3D493EBCe97e343c53D29a137942416B4ffC0", "ticker": "CRCL"},
"MUB": {"address": "0xcdf2f3e0fa43C47A6662a91C9E4a7C5f69762699", "ticker": "MU"},
"LITEB": {"address": "0x64748bea17b6d19e242adf20425de2440c656142", "ticker": "LITE"},
"METAB": {"address": "0x7425889fe94f9d693e8daefe88bcced6acfef4c0", "ticker": "META"},
"MSFTB": {"address": "0x80106cb3ead06659a5ad19df39d9b4733863b9b0", "ticker": "MSFT"},
"PLTRB": {"address": "0x0ca5d51d0277bd006fd9607d3e560785ebad8222", "ticker": "PLTR"},
"QQQB": {"address": "0x205812cdbed920aff76c6580abd681a46d11efc7", "ticker": "QQQ"},
"SPCXB": {"address": "0xbe9D156892E55e7154BcD3cB0FEA677F9D3103E1", "ticker": "SPCX"},
"GOOGLB": {"address": "0x3f53de71c126bdabae20f9cd64848d317f6c3238", "ticker": "GOOGL"},
"AAPLB": {"address": "0x431a3bee82e2ca41e49895cbece5bb0f76a89b7a", "ticker": "AAPL"},
}
def _binance_headers():
return {"Accept-Encoding": "identity", "User-Agent": "binance-web3/1.1 (Alef NAV)"}
def _walk_dicts(obj):
if isinstance(obj, dict):
yield obj
for v in obj.values():
yield from _walk_dicts(v)
elif isinstance(obj, list):
for v in obj:
yield from _walk_dicts(v)
def _bstocks_registry():
"""Return BNB bStock contracts relevant to ALEF's portfolio.
v11 searched the wider bStock universe. v12 asks Binance only about
underlying tickers that can actually enter ALEF NAV.
"""
global _BSTOCK_REGISTRY_CACHE
if _BSTOCK_REGISTRY_CACHE is not None:
return _BSTOCK_REGISTRY_CACHE
out = {}
for sym, meta in _BSTOCK_STATIC_FALLBACK.items():
if not _portfolio_security_allowed(ticker=meta.get("ticker"), symbol=sym):
continue
row = dict(meta)
row.update({"symbol": sym, "provider": "bstocks", "source": "binance-official-fallback"})
out[row["address"].lower()] = row
candidates = set()
for ticker in PORTFOLIO_SECURITY_TICKERS:
clean = ticker.replace(".", "").replace("-", "")
if clean.isalnum():
candidates.add(clean + "B")
def lookup(symbol):
try:
data = _json_get(
"https://web3.binance.com/api/v1/dex/market/rwa/search",
params={"keyword": symbol, "platformId": "bstock"},
headers=_binance_headers(), timeout=6,
)
found = []
for d in _walk_dicts(data):
platform = str(d.get("platformId") or "").lower()
chain_id = str(d.get("binanceChainId") or d.get("chainId") or "")
addr = d.get("tokenContractAddress") or d.get("contractAddress")
token_symbol = str(d.get("tokenSymbol") or d.get("symbol") or symbol).upper()
ticker = str(d.get("ticker") or "").upper()
if not ticker and token_symbol.endswith("B"):
ticker = token_symbol[:-1]
if (platform == "bstock" and chain_id == "56" and isinstance(addr, str)
and addr.startswith("0x") and _portfolio_security_allowed(ticker=ticker, symbol=token_symbol)):
found.append((addr.lower(), {
"address": addr, "symbol": token_symbol, "ticker": ticker,
"provider": "bstocks", "source": "binance-rwa-search",
}))
return found
except Exception:
return []
try:
from concurrent.futures import ThreadPoolExecutor, as_completed
with ThreadPoolExecutor(max_workers=6) as pool:
futures = [pool.submit(lookup, sym) for sym in sorted(candidates)]
for fut in as_completed(futures):
for addr, row in fut.result():
out[addr] = row
except Exception as e:
_audit_log(f"[WARN] Binance bStocks portfolio discovery failed; using verified fallback contracts: {e}", file=sys.stderr)
_BSTOCK_REGISTRY_CACHE = out
return out
def _bstock_price_usd(symbol, ticker=None):
"""Value bStocks from the underlying Yahoo Finance ticker only."""
symbol = str(symbol or "").upper()
ticker = str(ticker or "").upper()
if not ticker and symbol.endswith("B"):
ticker = symbol[:-1]
return _nav_stock_price_usd(ticker)
def _ondo_binance_meta_index():
"""Current Ondo EVM stock metadata incl. ticker + shares multiplier."""
global _ONDO_BINANCE_META_CACHE
if _ONDO_BINANCE_META_CACHE is not None:
return _ONDO_BINANCE_META_CACHE
out = {"ethereum": {}, "bsc": {}}
try:
data = _json_get(
"https://www.binance.com/bapi/defi/v1/public/wallet-direct/buw/wallet/market/token/rwa/stock/detail/list/ai",
params={"type": 1}, headers=_binance_headers(), timeout=30,
)
for row in data.get("data") or []:
cid = str(row.get("chainId") or "")
chain = "ethereum" if cid == "1" else "bsc" if cid == "56" else None
addr = row.get("contractAddress")
if chain and isinstance(addr, str) and addr.startswith("0x"):
meta = dict(row)
meta["provider"] = "ondo"
meta["source"] = "binance-ondo-public-list"
out[chain][addr.lower()] = meta
except Exception as e:
_audit_log(f"[WARN] Ondo/Binance live stock metadata lookup failed: {e}", file=sys.stderr)
_ONDO_BINANCE_META_CACHE = out
return out
# Separate Ondo products that are not Global Markets equities/ETFs.
_ONDO_EXTRA_EVM = {
"ethereum": {
"0x1b19c19393e2d034d8ff31ff34c81252fcbbee92": {"symbol": "OUSG", "name": "Ondo Short-Term US Government Bond Fund", "decimals": 18, "kind": "ondo-fund"},
"0x96f6ef951840721adbf46ac996b59e0235cb985c": {"symbol": "USDY", "name": "US Dollar Yield", "decimals": 18, "kind": "ondo-yield"},
"0xaf37c1167910ebc994e266949387d2c7c326b879": {"symbol": "rUSDY", "name": "Rebasing USDY", "decimals": 18, "kind": "ondo-yield"},
"0xace8e719899f6e91831b18ae746c9a965c2119f1": {"symbol": "USDon", "name": "Ondo U.S. Dollar Token", "decimals": 18, "kind": "ondo-cash"},
},
"bsc": {
"0x608593d17a2decbbc4399e4185be4922f97ed32e": {"symbol": "USDY", "name": "US Dollar Yield", "decimals": 18, "kind": "ondo-yield"},
"0x1f8955e640cbd9abc3c3bb408c9e2e1f5f20dfe6": {"symbol": "USDon", "name": "Ondo U.S. Dollar Token", "decimals": 18, "kind": "ondo-cash"},
},
}
def _ondo_evm_registry(chain):
"""Official Ondo Global Markets token list + separate Ondo products."""
global _ONDO_EVM_REGISTRY_CACHE
if _ONDO_EVM_REGISTRY_CACHE is None:
out = {"ethereum": {}, "bsc": {}}
try:
data = _json_get(
"https://raw.githubusercontent.com/ondoprotocol/ondo-global-markets-token-list/main/tokenlist.json",
timeout=30,
)
for row in data.get("tokens") or []:
cid = str(row.get("chainId") or "")
ch = "ethereum" if cid == "1" else "bsc" if cid == "56" else None
addr = row.get("address")
if ch and isinstance(addr, str) and addr.startswith("0x"):
symbol = row.get("symbol") or "Ondo"
ticker = symbol[:-2] if str(symbol).lower().endswith("on") else ""
out[ch][addr.lower()] = {
"address": addr, "symbol": symbol, "name": row.get("name") or "",
"decimals": row.get("decimals"), "ticker": ticker,
"provider": "ondo", "kind": "ondo-stock",
"source": "ondo-official-tokenlist",
}
except Exception as e:
_audit_log(f"[WARN] Ondo official Global Markets token list failed: {e}", file=sys.stderr)
# Binance mirrors current Ondo EVM metadata and supplies multipliers.
live_meta = _ondo_binance_meta_index()
for ch in ("ethereum", "bsc"):
for addr, bm in live_meta.get(ch, {}).items():
meta = out[ch].setdefault(addr, {
"address": bm.get("contractAddress"), "symbol": bm.get("symbol") or "Ondo",
"name": "", "decimals": 18, "provider": "ondo", "kind": "ondo-stock",
"source": "binance-ondo-public-list",
})
if bm.get("ticker"):
meta["ticker"] = bm.get("ticker")
if bm.get("multiplier") is not None:
meta["multiplier"] = bm.get("multiplier")
for addr, extra in _ONDO_EXTRA_EVM.get(ch, {}).items():
meta = dict(extra)
meta.update({"address": addr, "provider": "ondo", "source": "ondo-official-address"})
out[ch][addr.lower()] = meta
_ONDO_EVM_REGISTRY_CACHE = out
return _ONDO_EVM_REGISTRY_CACHE.get(chain, {})
def _ondo_dynamic_evm_price(chain, address, meta):
"""
Value an Ondo Global Markets stock from the underlying Yahoo ticker.
Any Ondo `multiplier` is a share-conversion factor, not a USD price source.
"""
ticker = str(meta.get("ticker") or "").upper()
underlying = _nav_stock_price_usd(ticker)
if underlying is None or underlying <= 0:
return None
try:
mult = D(meta.get("multiplier"), "1")
if mult <= 0:
mult = Decimal("1")
except Exception:
mult = Decimal("1")
return underlying * mult
def _ondo_nonstock_evm_price(chain, address, meta):
"""
Non-equity Ondo products are not security prices from Yahoo. Dollar-like
reserve tokens are marked at $1; other fund/yield products are excluded
unless an explicit price rule is added here.
"""
symbol = str(meta.get("symbol") or "").upper()
if symbol in ("USDON", "RUSDY", "USDY"):
return Decimal("1")
return None
def _scan_evm_provider(wallet, chain, rpc_url, registry, provider):
"""Direct balanceOf scan of portfolio-relevant provider contracts only."""
full_count = len(registry or {})
registry = _portfolio_registry_subset(registry, provider)
if not registry:
return {
"assets_usd": Decimal("0"), "items": [], "contracts": set(),
"deployments_scanned": 0, "registry_candidates": full_count, "positions": 0,
}
arg = _pad_evm_addr(wallet)
calls = [(addr, meta.get("address") or addr, "0x70a08231" + arg) for addr, meta in registry.items()]
balances = _evm_batch_eth_call(rpc_url, calls)
positives = []
for key, ret in balances.items():
if not ret or ret == "0x":
continue
try:
raw = int(ret, 16)
except Exception:
continue
if raw > 0:
positives.append((key, raw, registry[key]))
total = Decimal("0")
items = []
failures = []
for key, raw, meta in positives:
addr = meta.get("address") or key
try:
decimals = int(meta.get("decimals")) if meta.get("decimals") is not None else int(_evm_uint_call(rpc_url, addr, "313ce567"))
except Exception as e:
failures.append(f"{meta.get('symbol') or addr}: decimals {e}")
continue
amount = Decimal(raw) / (Decimal(10) ** decimals)
symbol = meta.get("symbol") or provider
ticker = meta.get("ticker") or ""
if provider == "bstocks" or meta.get("kind") == "ondo-stock":
include = _portfolio_security_allowed(ticker=ticker, symbol=symbol)
else:
include = str(symbol).upper() in PORTFOLIO_CASH_SYMBOLS
_audit_log(
f"[ALEF] {chain} {provider}: {symbol} amount={amount} underlying={ticker or '?'} portfolio_include={include}",
file=sys.stderr,
)
if not include:
continue
if provider == "bstocks":
price = _bstock_price_usd(symbol, ticker)
elif meta.get("kind") == "ondo-stock":
price = _ondo_dynamic_evm_price(chain, addr, meta)
else:
price = _ondo_nonstock_evm_price(chain, addr, meta)
if price is None or price <= 0:
failures.append(f"{meta.get('symbol') or addr}: no USD price")
continue
value = amount * price
total += value
items.append({
"symbol": meta.get("symbol") or provider, "address": addr,
"amount": str(amount), "price_usd": str(price), "value_usd": str(value),
"underlying_symbol": meta.get("ticker") or "", "provider": provider,
"asset_type": meta.get("kind") or ("bstock" if provider == "bstocks" else "ondo"),
"identity_source": meta.get("source") or provider,
})
if failures:
raise RuntimeError(f"{provider} holdings found but could not be fully valued: " + "; ".join(failures[:12]))
return {
"assets_usd": total, "items": items,
"contracts": {str(x.get("address") or "").lower() for x in items if x.get("address")},
"deployments_scanned": len(registry), "registry_candidates": full_count,
"positions": len(items),
}
def _fetch_bstocks_bnb_usd(wallet, rpc_url):
return _scan_evm_provider(wallet, "bsc", rpc_url, _bstocks_registry(), "bstocks")
def _fetch_ondo_evm_usd(wallet, chain, rpc_url):
return _scan_evm_provider(wallet, chain, rpc_url, _ondo_evm_registry(chain), "ondo")
def _ondo_solana_registry():
"""Official Ondo Solana GM mint list from Ondo's simulator repository."""
global _ONDO_SOLANA_REGISTRY_CACHE
if _ONDO_SOLANA_REGISTRY_CACHE is not None:
return _ONDO_SOLANA_REGISTRY_CACHE
out = {}
try:
sess = _http_session()
r = sess.get(
"https://raw.githubusercontent.com/ondoprotocol/gm-solana-simulator/main/constants.rs",
timeout=30,
)
r.raise_for_status()
text = r.text
for symbol, mint in re.findall(r'\("([A-Za-z0-9.\-]+on)",\s*"([1-9A-HJ-NP-Za-km-z]{32,48})"\)', text):
out[mint.lower()] = {
"symbol": symbol, "mint": mint, "ticker": symbol[:-2],
"provider": "ondo", "kind": "ondo-stock", "source": "ondo-official-solana-list",
}
except Exception as e:
_audit_log(f"[WARN] Ondo official Solana GM registry failed: {e}", file=sys.stderr)
# Separate Ondo products on Solana.
extras = {
"A1KLoBrKBde8Ty9qtNQUtq3C2ortoC3u7twggz7sEto6": ("USDY", "ondo-yield"),
"i7u4r16TcsJTgq1kAG8opmVZyVnAKBwLKu6ZPMwzxNc": ("OUSG", "ondo-fund"),
"ZPFtoCe7WWqG4N3ZFRccS8T9SMBeHsd1Vmgv2i7ondo": ("USDon", "ondo-cash"),
}
for mint, (symbol, kind) in extras.items():
out[mint.lower()] = {"symbol": symbol, "mint": mint, "ticker": "", "provider": "ondo", "kind": kind, "source": "ondo-official-address"}
_ONDO_SOLANA_REGISTRY_CACHE = out
return out
def _fetch_evm_xstocks_usd(wallet, chain, rpc_url):
"""
Directly scan official EVM xStocks. balanceOf() supplies the adjusted
economic share quantity; Yahoo Finance supplies the USD price.
"""
full_registry = _xstocks_chain_index(chain)
full_count = len(full_registry)
registry = _portfolio_registry_subset(full_registry, "xstocks")
if not registry:
return {
"assets_usd": Decimal("0"), "items": [], "contracts": set(),
"deployments_scanned": 0, "registry_candidates": full_count, "positions": 0,
}
arg = _pad_evm_addr(wallet)
calls = [(addr, meta.get("address") or addr, "0x70a08231" + arg) for addr, meta in registry.items()]
balances = _evm_batch_eth_call(rpc_url, calls)
positives = []
for key, ret in balances.items():
if not ret or ret == "0x":
continue
try:
raw = int(ret, 16)
except Exception:
continue
if raw > 0:
positives.append((key, raw, registry[key]))
total = Decimal("0")
items = []
failures = []
for key, raw, meta in positives:
addr = meta.get("address") or key
decimals = meta.get("decimals")
try:
decimals = int(decimals) if decimals is not None else int(_evm_uint_call(rpc_url, addr, "313ce567"))
except Exception as e:
failures.append(f"{meta.get('symbol') or addr}: decimals: {e}")
continue
amount = Decimal(raw) / (Decimal(10) ** decimals)
symbol = meta.get("symbol") or "xStock"
ticker = meta.get("underlyingSymbol") or (symbol[:-1] if symbol.lower().endswith("x") else "")
include = _portfolio_security_allowed(ticker=ticker, symbol=symbol)
_audit_log(
f"[ALEF] {chain} xStock: {symbol} amount={amount} underlying={ticker} portfolio_include={include}",
file=sys.stderr,
)
if not include:
continue
price = _nav_stock_price_usd(ticker)
if price is None or price <= 0:
failures.append(f"{symbol}: Yahoo Finance has no price for {ticker}")
continue
value = amount * price
total += value
items.append({
"symbol": symbol, "address": addr, "amount": str(amount),
"price_usd": str(price), "value_usd": str(value),
"underlying_symbol": ticker, "provider": "xstocks", "asset_type": "xstock",
"identity_source": "xstocks-official-registry", "price_source": "yfinance",
})
if failures:
raise RuntimeError("xStocks EVM holdings found but could not be fully valued: " + "; ".join(failures[:10]))
return {
"assets_usd": total,
"items": items,
"contracts": {str(x.get("address") or "").lower() for x in items if x.get("address")},
"deployments_scanned": len(registry),
"registry_candidates": full_count,
"positions": len(items),
}
def fetch_evm_wallet_assets_usd(wallet, *, chain, rpc_url, native_symbol, native_usd, exclude_contracts=None):
"""
Value native coin + raw ERC-20 holdings plus issuer-authenticated tokenized assets.
Ethereum: xStocks + Ondo.
BNB Chain: bStocks + Ondo (NOT xStocks for Alef's BNB holdings).
Provider contracts are queried directly with balanceOf() and then excluded
from generic enumeration to prevent double-counting.
"""
exclude_contracts = {x.lower() for x in (exclude_contracts or [])}
native_amount = _eth_get_native_balance(rpc_url, wallet)
total = native_amount * D(native_usd)
items = [{"symbol": native_symbol, "amount": str(native_amount), "price_usd": str(native_usd), "value_usd": str(total)}]
provider_scans = []
if chain == "ethereum":
xs = _fetch_evm_xstocks_usd(wallet, chain, rpc_url)
provider_scans.append(("xStocks", xs))
elif chain == "bsc":
bs = _fetch_bstocks_bnb_usd(wallet, rpc_url)
provider_scans.append(("bStocks", bs))
ondo = _fetch_ondo_evm_usd(wallet, chain, rpc_url)
provider_scans.append(("Ondo", ondo))
provider_contracts = set()
stock_items = []
provider_diagnostics = {}
for label, scan in provider_scans:
total += scan["assets_usd"]
items.extend(scan["items"])
provider_contracts.update(scan.get("contracts") or set())
provider_diagnostics[label] = {
"deployments_scanned": scan.get("deployments_scanned", 0),
"registry_candidates": scan.get("registry_candidates", scan.get("deployments_scanned", 0)),
"positions": scan.get("positions", 0),
}
for it in scan.get("items") or []:
if it.get("asset_type") in ("xstock", "bstock", "ondo-stock"):
stock_items.append(it)
try:
if chain == "bsc" and not ETHERSCAN_API_KEY:
raise RuntimeError("generic BNB token indexer skipped (no Etherscan/BscScan API key)")
rows = _blockscout_token_balances(chain, wallet)
except Exception as e:
if chain != "bsc":
_audit_log(f"[WARN] {chain} generic ERC-20 wallet enumeration failed: {e}", file=sys.stderr)
else:
_audit_log("[ALEF] BNB generic token enumeration skipped; portfolio bStocks/Ondo contracts were probed directly.", file=sys.stderr)
return {
"chain": chain, "assets_usd": total, "items": items,
"token_scan_complete": False, "tokenized_scan_complete": True,
"stock_items": stock_items, "provider_diagnostics": provider_diagnostics,
}
token_rows = []
contracts = []
for row in rows:
tok = row.get("token") or {}
if (tok.get("type") or "ERC-20").upper() not in ("ERC-20", "ERC20"):
continue
symbol = tok.get("symbol") or ""
if _looks_like_protocol_receipt(symbol):
continue
addr = tok.get("address") or tok.get("contract_address")
if not addr:
continue
al = addr.lower()
if al in exclude_contracts or al in provider_contracts:
continue
decimals = int(D(tok.get("decimals"), "18"))
raw = D(row.get("value"))
amount = raw / (Decimal(10) ** decimals)
if amount <= 0:
continue
token_rows.append((addr, symbol, amount, tok))
contracts.append(addr)
for addr, symbol, amount, tok in token_rows:
price = _portfolio_raw_token_price_usd(symbol)
include = price is not None and price > 0
_audit_log(
f"[ALEF] {chain} raw token: {symbol or '?'} amount={amount} contract={addr} "
f"portfolio_include={include}", file=sys.stderr,
)
if not include:
continue
value = amount * price
total += value
items.append({
"symbol": symbol, "address": addr, "amount": str(amount),
"price_usd": str(price), "value_usd": str(value),
"price_source": "coingecko/yfinance",
})
return {
"chain": chain, "assets_usd": total, "items": items,
"token_scan_complete": True, "tokenized_scan_complete": True,
"stock_items": stock_items, "provider_diagnostics": provider_diagnostics,
}
def _tron_constant_uint(wallet, contract, selector):
payload = {
"owner_address": wallet,
"contract_address": contract,
"function_selector": selector,
"parameter": "",
"visible": True,
}
data = _json_post(f"{TRONGRID_API}/wallet/triggerconstantcontract", payload, timeout=15)
vals = data.get("constant_result") or []
if not vals:
raise RuntimeError(f"TRON constant call failed for {contract} {selector}")
return int(vals[0], 16)
def fetch_tron_wallet_assets_usd(wallet, trx_usd, exclude_contracts=None):
"""
Enumerate every non-zero TRC-20 first. Protocol receipts are diagnosed but
excluded because their underlying is already counted by JustLend. All other
raw tokens are then filtered by portfolio identity; USD prices come only
from the original CoinGecko/yfinance crypto price function (or $1 cash).
"""
exclude = {x.lower() for x in (exclude_contracts or [])}
data = _json_get(f"{TRONGRID_API}/v1/accounts/{wallet}", timeout=20)
rows = data.get("data") or []
if not rows:
return {"chain": "tron", "assets_usd": Decimal("0"), "items": [], "token_scan_complete": True}
acct = rows[0]
trx = D(acct.get("balance")) / Decimal(1_000_000)
total = trx * D(trx_usd)
items = [{"symbol": "TRX", "amount": str(trx), "price_usd": str(trx_usd), "value_usd": str(total), "price_source": "coingecko/yfinance"}]
discovered = {}
for entry in acct.get("trc20") or []:
if isinstance(entry, dict):
for contract, raw in entry.items():
if contract:
discovered[contract] = D(raw)
all_tokens = []
for contract, raw in discovered.items():
known = TRON_TRUSTED_ASSETS.get(contract, {})
symbol = known.get("symbol") or "?"
is_receipt = contract.lower() in exclude
decimals = None
try:
decimals = int(_tron_constant_uint(wallet, contract, "decimals()"))
except Exception:
pass
amount = None if decimals is None else raw / (Decimal(10) ** decimals)
# The account endpoint does not reliably return token metadata. Known
# portfolio contracts are identified by address; unknown contracts are
# still printed and never silently discarded.
include = (not is_receipt) and symbol != "?" and _portfolio_raw_token_price_usd(symbol) is not None
diag = {
"contract": contract, "symbol": symbol,
"amount": str(amount) if amount is not None else "?",
"protocol_receipt": is_receipt, "portfolio_include": include,
}
all_tokens.append(diag)
_audit_log(
f"[ALEF] TRON token: {symbol} amount={diag['amount']} contract={contract} "
f"protocol_receipt={is_receipt} portfolio_include={include}", file=sys.stderr,
)
if not include or amount is None or amount <= 0:
continue
price = _portfolio_raw_token_price_usd(symbol)
if price is None or price <= 0:
continue
value = amount * price
total += value
items.append({
"symbol": symbol, "address": contract, "amount": str(amount),
"price_usd": str(price), "value_usd": str(value), "price_source": "coingecko/yfinance",
})
return {
"chain": "tron", "assets_usd": total, "items": items,
"token_scan_complete": True, "all_tokens": all_tokens,
}
def _solana_rpc(method, params):
# Public Solana RPCs can stall. v11 used the global 3-retry / 25-second
# path; one bad call could therefore dominate the entire NAV update.
payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
sess = _http_session(max_retries=1, backoff_factor=0.25)
r = sess.post(SOLANA_RPC, json=payload, timeout=10, headers={"Content-Type": "application/json"})
r.raise_for_status()
data = r.json()
if data.get("error"):
raise RuntimeError(f"Solana {method} error: {data['error']}")
return data.get("result")
def _solana_metaplex_metadata(mint):
"""Best-effort name/symbol discovery from the canonical Metaplex metadata program."""
try:
rows = _solana_rpc("getProgramAccounts", [
METAPLEX_METADATA_PROGRAM,
{
"encoding": "base64",
"filters": [{"memcmp": {"offset": 33, "bytes": mint}}],
},
]) or []
if not rows:
return None
encoded = ((rows[0].get("account") or {}).get("data") or [None])[0]
if not encoded:
return None
raw = base64.b64decode(encoded)
# Metadata V1 begins: key(1), updateAuthority(32), mint(32), then
# Borsh strings name, symbol, uri (u32 little-endian length + bytes).
pos = 65
strings = []
for _ in range(3):
if pos + 4 > len(raw):
return None
n = int.from_bytes(raw[pos:pos + 4], "little")
pos += 4
if n < 0 or n > 2000 or pos + n > len(raw):
return None
strings.append(raw[pos:pos + n].decode("utf-8", errors="replace").rstrip("\x00").strip())
pos += n
return {"name": strings[0], "symbol": strings[1], "uri": strings[2]}
except Exception as e:
_audit_log(f"[WARN] Solana metadata lookup failed for {mint}: {e}", file=sys.stderr)
return None
def _xstock_yahoo_ticker(metadata):
"""Infer the listed ticker only for metadata that explicitly identifies an xStock."""
if not metadata:
return None
symbol = (metadata.get("symbol") or "").strip()
name = (metadata.get("name") or "").strip()
# Official xStocks symbols conventionally append lowercase x (AAPLx, NVDAx,
# PPLTx, QQQx...). Require either xStock in the name or a CoinGecko ID/name
# that explicitly identifies xStock; this avoids classifying arbitrary SPL
# tokens ending in x as securities.
cg_id = (metadata.get("coingecko_id") or "").lower()
identified_xstock = "xstock" in name.lower() or "xstock" in cg_id
if not symbol.lower().endswith("x") or not identified_xstock:
return None
ticker = symbol[:-1].upper().replace(".", "-")
return ticker or None
def _price_xstock_from_metadata(metadata):
ticker = _xstock_yahoo_ticker(metadata)
if not ticker:
return None
try:
p = fetch_stock_prices([ticker])[0]
if p is not None and p > 0:
return D(p)
except Exception:
pass
return None
def _jupiter_solana_prices(mint_decimals):
"""Optional Jupiter Price API v3 lookup when JUPITER_API_KEY is configured."""
if not JUPITER_API_KEY or not mint_decimals:
return {}
out = {}
mints = list(mint_decimals)
for i in range(0, len(mints), 50):
chunk = mints[i:i+50]
try:
data = _json_get(
"https://api.jup.ag/price/v3",
params={"ids": ",".join(chunk)},
headers={"x-api-key": JUPITER_API_KEY},
timeout=20,
)
for mint, row in (data or {}).items():
if isinstance(row, dict) and row.get("usdPrice") is not None:
out[mint.lower()] = D(row["usdPrice"])
except TypeError:
# Older local copy of _json_get without a headers kwarg: use session directly.
try:
r = _http_session().get(
"https://api.jup.ag/price/v3",
params={"ids": ",".join(chunk)},
headers={"x-api-key": JUPITER_API_KEY}, timeout=20)
r.raise_for_status()
for mint, row in (r.json() or {}).items():
if isinstance(row, dict) and row.get("usdPrice") is not None:
out[mint.lower()] = D(row["usdPrice"])
except Exception as e:
_audit_log(f"[WARN] Jupiter price lookup failed: {e}", file=sys.stderr)
except Exception as e:
_audit_log(f"[WARN] Jupiter price lookup failed: {e}", file=sys.stderr)
return out
_XSTOCKS_REGISTRY_CACHE = None
def _xstocks_chain_key(network):
"""Normalize xStocks API network labels to the chains used by this script."""
if isinstance(network, dict):
network = (network.get("name") or network.get("slug") or network.get("id") or
network.get("identifier") or network.get("network"))
n = str(network or "").strip().lower().replace("_", " ").replace("-", " ")
if "solana" in n:
return "solana"
if n == "ton" or "open network" in n:
return "ton"
if "bnb" in n or "binance smart" in n or n == "bsc":
return "bsc"
# Keep Ethereum separate from Ethereum L2s such as Arbitrum/Ink/Mantle.
if n in ("ethereum", "ethereum mainnet", "eth", "mainnet"):
return "ethereum"
return None
def _xstocks_all_chain_index():
"""
Return {chain: {contract_or_mint: metadata}} from xStocks' public Assets API.
Unlike the old Solana-only index, this is authoritative discovery for
Ethereum, BNB Smart Chain, Solana and TON. It lets us query xStocks
directly even when a generic wallet token indexer is unavailable.
"""
global _XSTOCKS_REGISTRY_CACHE
if _XSTOCKS_REGISTRY_CACHE is not None:
return _XSTOCKS_REGISTRY_CACHE
bases = (
"https://api.xstocks.fi/api/v2",
"https://api.backed.fi/api/v2",
)
last_error = None
for base in bases:
try:
out = {"ethereum": {}, "bsc": {}, "solana": {}, "ton": {}}
page = 1
while page <= 25:
data = _json_get(
f"{base}/public/assets",
params={"page": page, "pageSize": 100}, timeout=30,
)
payload = data.get("data") if isinstance(data, dict) and isinstance(data.get("data"), dict) else data
nodes = ((payload or {}).get("nodes") or (payload or {}).get("assets") or
(payload or {}).get("items") or [])
for asset in nodes:
if not isinstance(asset, dict):
continue
deployments = (asset.get("deployments") or asset.get("contracts") or
asset.get("tokens") or [])
for dep in deployments:
if not isinstance(dep, dict):
continue
contract = dep.get("contract") if isinstance(dep.get("contract"), dict) else {}
network = (dep.get("network") or dep.get("chain") or dep.get("blockchain") or
contract.get("network") or contract.get("chain"))
chain = _xstocks_chain_key(network)
if not chain:
continue
address = (dep.get("address") or dep.get("contractAddress") or dep.get("tokenAddress") or
dep.get("mint") or contract.get("address") or contract.get("contractAddress"))
if not address:
continue
meta = {
"id": asset.get("id"),
"name": asset.get("name") or "",
"symbol": asset.get("symbol") or asset.get("identifier") or "",
"underlyingSymbol": asset.get("underlyingSymbol") or asset.get("underlyingTicker") or "",
"decimals": (dep.get("decimals") if dep.get("decimals") is not None else
contract.get("decimals") if contract.get("decimals") is not None else
asset.get("decimals")),
"multiplier": asset.get("currentMultiplier") or asset.get("multiplier"),
"network": network,
"address": address,
"source": "xstocks-official-registry",
"api_base": base,
}
out[chain][address.lower()] = meta
page_info = (payload or {}).get("page") or (payload or {}).get("pagination") or {}
has_next = page_info.get("hasNextPage")
if has_next is None:
# Some API variants expose totalPages/currentPage instead.
total_pages = page_info.get("totalPages")
current_page = page_info.get("currentPage") or page
has_next = bool(total_pages and int(current_page) < int(total_pages))
if not has_next:
break
page += 1
if any(out.values()):
_XSTOCKS_REGISTRY_CACHE = out
return out
except Exception as e:
last_error = e
continue
if last_error:
_audit_log(f"[WARN] xStocks cross-chain registry lookup failed: {last_error}", file=sys.stderr)
_XSTOCKS_REGISTRY_CACHE = {"ethereum": {}, "bsc": {}, "solana": {}, "ton": {}}
return _XSTOCKS_REGISTRY_CACHE
def _xstocks_chain_index(chain):
return _xstocks_all_chain_index().get(chain, {})
def _xstocks_solana_mint_index():
# Backward-compatible wrapper used by the Solana scanner below.
return _xstocks_chain_index("solana")
def _xstocks_official_price(symbol):
"""Current per-share USD quote from the issuer's public price endpoint."""
if not symbol:
return None
for base in ("https://api.xstocks.fi/api/v2", "https://api.backed.fi/api/v2"):
try:
data = _json_get(f"{base}/public/assets/{symbol}/price-data", timeout=20)
payload = data.get("data") if isinstance(data, dict) and isinstance(data.get("data"), dict) else data
q = (payload or {}).get("quote") if isinstance(payload, dict) else None
if q is not None and D(q) > 0:
return D(q)
except Exception:
continue
return None
def _extract_multiplier_value(obj):
"""Best-effort parser for current multiplier across xStocks API versions."""
if isinstance(obj, dict):
# Prefer explicitly-current fields before walking the structure.
for key in ("currentMultiplier", "current_multiplier", "activeMultiplier", "active_multiplier"):
if key in obj:
v = obj[key]
if isinstance(v, dict):
for kk in ("value", "multiplier", "amount"):
if kk in v:
try:
d = D(v[kk])
if d > 0:
return d
except Exception:
pass
else:
try:
d = D(v)
if d > 0:
return d
except Exception:
pass
# A top-level multiplier/value is common on the public endpoint.
for key in ("multiplier", "value"):
if key in obj and not isinstance(obj[key], (dict, list)):
try:
d = D(obj[key])
if d > 0:
return d
except Exception:
pass
for v in obj.values():
found = _extract_multiplier_value(v)
if found is not None:
return found
elif isinstance(obj, list):
for v in obj:
found = _extract_multiplier_value(v)
if found is not None:
return found
return None
def _xstocks_official_multiplier(symbol, network=None):
"""Current xStock quantity multiplier. This is NOT a USD price source."""
if not symbol:
return None
n = str(network or "")
variants = [n] if n else [""]
if n.upper() == "TON":
variants += ["ton", "The Open Network"]
elif n.upper() == "SOLANA":
variants += ["Solana", "solana"]
# preserve order, remove duplicates
variants = list(dict.fromkeys(variants))
for base in ("https://api.backed.fi/api/v2", "https://api.xstocks.fi/api/v2"):
for net in variants:
params = {"network": net} if net else {}
try:
data = _json_get(f"{base}/public/assets/{symbol}/multiplier", params=params, timeout=20)
payload = data.get("data") if isinstance(data, dict) and isinstance(data.get("data"), dict) else data
m = _extract_multiplier_value(payload)
if m is not None and m > 0:
return m
except Exception:
pass
for base in ("https://api.backed.fi/api/v1", "https://api.xstocks.fi/api/v1"):
for net in variants:
try:
data = _json_get(
f"{base}/token/{symbol}/multiplier",
params=({"network": net} if net else None), timeout=20,
)
m = _extract_multiplier_value(data)
if m is not None and m > 0:
return m
except Exception:
pass
return None
def _solana_scaled_ui_multiplier(mint):
"""
Read Token-2022 scaledUiAmountConfig from the mint and return the multiplier
that is active *now*. Returns 1 for a mint with no scaled-UI extension.
"""
result = _solana_rpc("getAccountInfo", [
mint, {"commitment": "confirmed", "encoding": "jsonParsed"},
]) or {}
value = result.get("value") or {}
data = value.get("data") or {}
parsed = data.get("parsed") if isinstance(data, dict) else None
info = (parsed or {}).get("info") if isinstance(parsed, dict) else {}
extensions = (info or {}).get("extensions") or []
for ext in extensions:
if not isinstance(ext, dict):
continue
name = str(ext.get("extension") or ext.get("extensionType") or "").lower()
if "scaleduiamount" not in name.replace("_", "").replace("-", ""):
continue
state = ext.get("state") or ext
current = D(state.get("multiplier"), "1")
new = D(state.get("newMultiplier"), str(current))
ts_raw = state.get("newMultiplierEffectiveTimestamp")
try:
ts = float(ts_raw)
except Exception:
ts = float("inf")
now_ts = datetime.now(timezone.utc).timestamp()
active = new if now_ts >= ts else current
if active <= 0:
raise RuntimeError(f"invalid scaled UI multiplier {active} for {mint}")
return active
return Decimal("1")
_CG_SOLANA_INDEX_CACHE = None
def _coingecko_solana_mint_index():
"""
Build mint -> CoinGecko identity from /coins/list?include_platform=true.
This is more reliable for Solana than the legacy simple/token_price/solana
route, and discovers xStocks without hard-coding every mint.
"""
global _CG_SOLANA_INDEX_CACHE
if _CG_SOLANA_INDEX_CACHE is not None:
return _CG_SOLANA_INDEX_CACHE
try:
rows = _json_get(
"https://api.coingecko.com/api/v3/coins/list",
params={"include_platform": "true"}, timeout=30,
)
out = {}
for row in rows if isinstance(rows, list) else []:
platforms = row.get("platforms") or {}
mint = platforms.get("solana")
if mint:
out[mint.lower()] = {
"id": row.get("id"),
"symbol": row.get("symbol"),
"name": row.get("name"),
}
_CG_SOLANA_INDEX_CACHE = out
return out
except Exception as e:
_audit_log(f"[WARN] CoinGecko Solana mint registry lookup failed: {e}", file=sys.stderr)
_CG_SOLANA_INDEX_CACHE = {}
return {}
def _coingecko_solana_registry_prices(mints):
index = _coingecko_solana_mint_index()
identities = {}
ids = []
for mint in mints:
meta = index.get(mint.lower())
if meta and meta.get("id"):
identities[mint] = meta
ids.append(meta["id"])
id_prices = _coingecko_simple_id_prices(ids)
prices = {}
for mint, meta in identities.items():
p = id_prices.get(meta["id"])
if p is not None and p > 0:
prices[mint.lower()] = p
return prices, identities
def _known_solana_asset_prices(mints):
out = {}
ids = []
id_to_mints = {}
for mint in mints:
meta = SOLANA_KNOWN_ASSETS.get(mint)
if meta and meta.get("coingecko_id"):
cid = meta["coingecko_id"]
ids.append(cid)
id_to_mints.setdefault(cid, []).append(mint)
cg = _coingecko_simple_id_prices(ids)
for cid, price in cg.items():
for mint in id_to_mints.get(cid, []):
if price and price > 0:
out[mint.lower()] = price
# yfinance fallback is useful for tokenized listed securities if the
# CoinGecko coin endpoint is rate-limited.
for mint in mints:
if mint.lower() in out:
continue
meta = SOLANA_KNOWN_ASSETS.get(mint) or {}
ticker = meta.get("yahoo_ticker")
if ticker:
try:
p = fetch_stock_prices([ticker])[0]
if p is not None and p > 0:
out[mint.lower()] = D(p)
except Exception:
pass
return out
def fetch_solana_wallet_assets_usd(wallet, sol_usd, alef_mint, extra_excluded_mints=None):
"""
Enumerate all SPL/Token-2022 balances first. Portfolio securities are priced
from Yahoo Finance; recognized portfolio crypto/cash uses the original
CoinGecko/yfinance path. Chain/issuer calls are used only for identity and
xStock/Ondo quantity multipliers, never for USD prices.
"""
bal = _solana_rpc("getBalance", [wallet, {"commitment": "confirmed"}]) or {}
sol = D(bal.get("value")) / Decimal(1_000_000_000)
# SOL is gas, not one of Alef's declared crypto holdings. Diagnose it but do
# not add it to NAV unless you intentionally add SOL to PORTFOLIO_CRYPTO_SYMBOLS.
total = Decimal("0")
items = []
if "SOL" in PORTFOLIO_CRYPTO_SYMBOLS:
sol_price = _nav_crypto_price_usd("SOL") or D(sol_usd)
sol_value = sol * sol_price
total += sol_value
items.append({"symbol": "SOL", "amount": str(sol), "price_usd": str(sol_price), "value_usd": str(sol_value), "price_source": "coingecko/yfinance"})
else:
_audit_log(f"[ALEF] Solana native SOL gas excluded from NAV: amount={sol}", file=sys.stderr)
excluded_mints = {alef_mint, *ALEF_LEGACY_MINTS, *(extra_excluded_mints or set())}
amounts, ui_amounts, decimals_by_mint, token_program_by_mint = {}, {}, {}, {}
successful_program_scans = 0
for program in SOLANA_TOKEN_PROGRAMS:
try:
result = _solana_rpc("getTokenAccountsByOwner", [
wallet, {"programId": program}, {"commitment": "confirmed", "encoding": "jsonParsed"},
]) or {}
successful_program_scans += 1
except Exception as e:
_audit_log(f"[WARN] Solana token-program scan failed for {program}: {e}", file=sys.stderr)
continue
for row in result.get("value") or []:
try:
info = row["account"]["data"]["parsed"]["info"]
mint = info["mint"]
if mint in excluded_mints:
continue
ta = info["tokenAmount"]
decimals = int(ta.get("decimals", 0))
raw_integer = D(ta.get("amount"))
amount = raw_integer / (Decimal(10) ** decimals)
# For Token-2022 Scaled UI Amount tokens, Solana RPC already
# returns multiplier-adjusted uiAmountString. This avoids a
# second mint RPC call (the source of the PPLTx timeouts).
ui_raw = ta.get("uiAmountString")
ui_amount = D(ui_raw) if ui_raw not in (None, "") else amount
decimals_by_mint[mint] = decimals
token_program_by_mint[mint] = program
if amount > 0:
amounts[mint] = amounts.get(mint, Decimal("0")) + amount
ui_amounts[mint] = ui_amounts.get(mint, Decimal("0")) + ui_amount
except Exception:
continue
if successful_program_scans == 0:
raise RuntimeError("both Solana token-program scans failed")
xstock_registry = _xstocks_solana_mint_index()
ondo_registry = _ondo_solana_registry()
cg_identity = _coingecko_solana_mint_index() # identity only, never its price
all_tokens, stock_items = [], []
for mint, raw_amount in amounts.items():
low = mint.lower()
xs = xstock_registry.get(low)
od = ondo_registry.get(low)
known = SOLANA_KNOWN_ASSETS.get(mint) or {}
cg_meta = cg_identity.get(low) or {}
meta = dict(xs or od or known or {})
if not meta and int(decimals_by_mint.get(mint, 0) or 0) > 0:
# Skip expensive Metaplex program scans for obvious 0-decimal
# NFTs/position receipts; fungible unknowns still get metadata.
meta = _solana_metaplex_metadata(mint) or {}
symbol = meta.get("symbol") or cg_meta.get("symbol") or "?"
name = meta.get("name") or cg_meta.get("name") or ""
economic_amount = raw_amount
multiplier = Decimal("1")
multiplier_source = "none"
provider = None
asset_type = None
ticker = None
price = None
include = False
if xs or (known and str(known.get("symbol") or "").lower().endswith("x")):
xsmeta = xs or known
symbol = xsmeta.get("symbol") or symbol
ticker = xsmeta.get("underlyingSymbol") or xsmeta.get("yahoo_ticker") or (symbol[:-1] if symbol.lower().endswith("x") else "")
economic_amount = ui_amounts.get(mint, raw_amount)
multiplier = (economic_amount / raw_amount) if raw_amount > 0 else Decimal("1")
multiplier_source = "solana-rpc-uiAmountString"
include = _portfolio_security_allowed(ticker=ticker, symbol=symbol)
price = _nav_stock_price_usd(ticker) if include else None
provider, asset_type = "xstocks", "xstock"
elif od:
symbol = od.get("symbol") or symbol
ticker = od.get("ticker") or ""
provider, asset_type = "ondo", od.get("kind")
if od.get("kind") == "ondo-stock":
# Ondo Token-2022 security quantities can also use scaled UI.
economic_amount = ui_amounts.get(mint, raw_amount)
multiplier = (economic_amount / raw_amount) if raw_amount > 0 else Decimal("1")
multiplier_source = "solana-rpc-uiAmountString"
include = _portfolio_security_allowed(ticker=ticker, symbol=symbol)
price = _nav_stock_price_usd(ticker) if include else None
else:
include = str(symbol or "").upper() in PORTFOLIO_STABLE_SYMBOLS
price = _portfolio_raw_token_price_usd(symbol) if include else None
else:
# Unknown tokens are enumerated and diagnosed first, then filtered.
ticker_guess = _xstock_yahoo_ticker({**meta, "coingecko_id": cg_meta.get("id")})
if ticker_guess and _portfolio_security_allowed(ticker=ticker_guess, symbol=symbol):
ticker = ticker_guess
include = True
price = _nav_stock_price_usd(ticker)
provider, asset_type = "identified-by-metadata", "security"
else:
include = _portfolio_raw_token_price_usd(symbol) is not None
price = _portfolio_raw_token_price_usd(symbol) if include else None
diag = {
"mint": mint, "symbol": symbol, "name": name,
"raw_amount": str(raw_amount), "amount": str(economic_amount),
"decimals": decimals_by_mint.get(mint), "program": token_program_by_mint.get(mint),
"provider": provider, "underlying_ticker": ticker,
"portfolio_include": include, "multiplier": str(multiplier),
"multiplier_source": multiplier_source,
}
all_tokens.append(diag)
_audit_log(
f"[ALEF] Solana token: {symbol} raw={raw_amount} economic={economic_amount} mint={mint} "
f"provider={provider or '?'} portfolio_include={include}", file=sys.stderr,
)
if not include or price is None or price <= 0:
continue
value = economic_amount * price
total += value
item = {
"symbol": symbol, "mint": mint, "raw_amount": str(raw_amount),
"multiplier": str(multiplier), "multiplier_source": multiplier_source,
"amount": str(economic_amount), "price_usd": str(price), "value_usd": str(value),
"underlying_ticker": ticker or "", "provider": provider or "raw",
"asset_type": asset_type or "portfolio-token", "price_source": "yfinance/coingecko",
}
items.append(item)
if ticker:
stock_items.append(item)
return {
"chain": "solana", "assets_usd": total, "items": items,
"token_scan_complete": True, "tokenized_scan_complete": True,
"all_tokens": all_tokens, "stock_items": stock_items,
"provider_diagnostics": {
"xStocks": {"deployments_scanned": len(xstock_registry), "positions": sum(1 for x in stock_items if x.get('provider') == 'xstocks')},
"Ondo": {"deployments_scanned": len(ondo_registry), "positions": sum(1 for x in stock_items if x.get('provider') == 'ondo')},
},
# Raydium CLMM positions are represented by 1-of-1 position NFTs.
"candidate_position_nfts": [
mint for mint, amt in amounts.items()
if amt == Decimal("1") and int(decimals_by_mint.get(mint, -1)) == 0
],
}
# ===============================================================
# Raydium CLMM positions (position NFT -> PersonalPositionState PDA)
# ===============================================================
_B58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
_B58_MAP = {c: i for i, c in enumerate(_B58_ALPHABET)}
def _b58decode(s):
n = 0
for ch in str(s):
n = n * 58 + _B58_MAP[ch]
body = n.to_bytes((n.bit_length() + 7) // 8, "big") if n else b""
zeros = len(str(s)) - len(str(s).lstrip("1"))
return b"\x00" * zeros + body
def _b58encode(b):
b = bytes(b)
zeros = len(b) - len(b.lstrip(b"\x00"))
n = int.from_bytes(b, "big")
chars = []
while n:
n, r = divmod(n, 58)
chars.append(_B58_ALPHABET[r])
return "1" * zeros + ("".join(reversed(chars)) if chars else "")
def _ed25519_point_is_on_curve(pubkey32):
# Solana PDAs must fall OFF the Ed25519 curve. This is the standard
# compressed-point validity test used only for PDA derivation.
if len(pubkey32) != 32:
return False
p = 2**255 - 19
d = (-121665 * pow(121666, p - 2, p)) % p
y = int.from_bytes(pubkey32, "little") & ((1 << 255) - 1)
if y >= p:
return False
y2 = (y * y) % p
u = (y2 - 1) % p
v = (d * y2 + 1) % p
x2 = (u * pow(v, p - 2, p)) % p
x = pow(x2, (p + 3) // 8, p)
if (x * x - x2) % p != 0:
x = (x * pow(2, (p - 1) // 4, p)) % p
return (x * x - x2) % p == 0
def _solana_find_program_address(seeds, program_id):
program = _b58decode(program_id)
if len(program) != 32:
raise ValueError("invalid Solana program id")
for bump in range(255, -1, -1):
h = hashlib.sha256()
for seed in seeds:
if len(seed) > 32:
raise ValueError("PDA seed too long")
h.update(seed)
h.update(bytes([bump]))
h.update(program)
h.update(b"ProgramDerivedAddress")
digest = h.digest()
if not _ed25519_point_is_on_curve(digest):
return _b58encode(digest), bump
raise RuntimeError("unable to derive Solana PDA")
def fetch_raydium_clmm_assets_usd(candidate_nft_mints):
"""
Count ONLY the USDC side of ALEF's Raydium CLMM positions.
Raydium's aggregate positionInfo.usdValue includes both sides of the LP.
That is inappropriate for ALEF NAV when the other side is ALEF itself,
because ALEF must not count its own token as backing. Therefore this
function uses the token-specific amountA/amountB fields and the matching
unclaimedFee amountA/amountB field, counting only canonical Solana USDC.
Non-USDC principal, ALEF principal, non-USDC fees, and rewards are all
deliberately excluded from NAV.
"""
items = []
total = Decimal("0")
seen = set()
for nft_mint in candidate_nft_mints or []:
try:
mint_bytes = _b58decode(nft_mint)
if len(mint_bytes) != 32:
continue
position_pda, _ = _solana_find_program_address(
[b"position", mint_bytes], RAYDIUM_CLMM_PROGRAM_ID
)
data = _json_get(
"https://dynamic-ipfs.raydium.io/clmm/position",
params={"id": position_pda}, timeout=25,
)
pinfo = (data or {}).get("positionInfo") or {}
pool = (data or {}).get("poolInfo") or {}
if not pinfo or position_pda in seen:
continue
seen.add(position_pda)
mint_a = pool.get("mintA") or {}
mint_b = pool.get("mintB") or {}
sym_a = (mint_a.get("symbol") or "?").upper()
sym_b = (mint_b.get("symbol") or "?").upper()
addr_a = mint_a.get("address") or ""
addr_b = mint_b.get("address") or ""
amount_a = D(pinfo.get("amountA"))
amount_b = D(pinfo.get("amountB"))
uf = pinfo.get("unclaimedFee") or {}
fee_a = D(uf.get("amountA"))
fee_b = D(uf.get("amountB"))
# Match the canonical Solana USDC mint first; symbol is retained as
# a compatibility fallback in case Raydium omits/changes formatting.
a_is_usdc = (addr_a == SOLANA_USDC_MINT) or (sym_a == "USDC")
b_is_usdc = (addr_b == SOLANA_USDC_MINT) or (sym_b == "USDC")
if a_is_usdc:
usdc_principal = amount_a
usdc_fees = fee_a
usdc_side = "A"
elif b_is_usdc:
usdc_principal = amount_b
usdc_fees = fee_b
usdc_side = "B"
else:
# It may be a genuine Raydium position, but it contributes no
# Raydium value to ALEF NAV unless one side is USDC.
continue
# USDC is treated at $1.00, consistent with the rest of the NAV
# engine's cash/stablecoin accounting.
value = usdc_principal + usdc_fees
if value <= 0:
continue
item = {
"symbol": f"Raydium USDC only ({sym_a}/{sym_b})",
"provider": "raydium-clmm-usdc-only",
"position_nft_mint": nft_mint,
"position_pda": position_pda,
"pool_id": pool.get("id"),
"usdc_side": usdc_side,
"usdc_principal": str(usdc_principal),
"usdc_unclaimed_fees": str(usdc_fees),
"excluded_non_usdc_amount_a": str(Decimal("0") if a_is_usdc else amount_a),
"excluded_non_usdc_amount_b": str(Decimal("0") if b_is_usdc else amount_b),
"value_usd": str(value),
}
items.append(item)
total += value
except Exception:
# Most 1-of-1 NFTs are not Raydium positions. Silence those misses.
continue
live = bool(items)
if not live and RAYDIUM_MANUAL_FALLBACK_USD > 0:
total = D(RAYDIUM_MANUAL_FALLBACK_USD)
items = [{
"symbol": "Raydium USDC only (manual fallback)",
"provider": "raydium-manual-fallback-usdc-only",
"value_usd": str(total),
}]
_audit_log(
f"[WARN] Live Raydium USDC-side discovery returned no value; "
f"using configured USDC-only fallback ${total:.2f}.", file=sys.stderr,
)
return {
"chain": "raydium", "assets_usd": total, "items": items,
"token_scan_complete": True, "tokenized_scan_complete": True,
"stock_items": [], "live_position_count": len(items) if live else 0,
"used_manual_fallback": not live and total > 0,
}
def _ton_raw_address(address):
"""Normalize raw or user-friendly TON address to '<workchain>:<64 hex>'."""
a = str(address or "").strip()
if not a:
return None
if ":" in a:
wc, h = a.split(":", 1)
h = h.lower()
if len(h) == 64:
try:
int(h, 16)
return f"{int(wc)}:{h}"
except Exception:
pass
try:
padded = a + "=" * ((4 - len(a) % 4) % 4)
raw = base64.urlsafe_b64decode(padded.encode("ascii"))
# User-friendly address = tag, signed workchain byte, 32-byte hash, CRC16.
if len(raw) == 36:
wc = int.from_bytes(raw[1:2], "big", signed=True)
return f"{wc}:{raw[2:34].hex()}"
except Exception:
pass
return a
def _toncenter_owner_jettons(wallet):
"""Enumerate all non-zero Jetton wallets owned by a TON address."""
all_rows = []
metadata = {}
address_book = {}
offset = 0
while True:
data = _json_get(
"https://toncenter.com/api/v3/jetton/wallets",
params={
"owner_address": wallet,
"exclude_zero_balance": "true",
"limit": 1000,
"offset": offset,
},
timeout=30,
)
rows = data.get("jetton_wallets") or []
if not isinstance(rows, list):
raise RuntimeError(f"TON Center jetton-wallet response malformed: {data}")
all_rows.extend(rows)
metadata.update(data.get("metadata") or {})
address_book.update(data.get("address_book") or {})
if len(rows) < 1000:
break
offset += len(rows)
if offset > 10000:
raise RuntimeError("TON jetton pagination exceeded safety limit")
return {"jetton_wallets": all_rows, "metadata": metadata, "address_book": address_book}
def _ton_jetton_decimals(master, ton_data, xmeta=None):
"""Read jetton decimals from xStocks deployment metadata or TON Center metadata."""
xmeta = xmeta or {}
if xmeta.get("decimals") is not None:
try:
return int(xmeta["decimals"])
except Exception:
pass
target = _ton_raw_address(master)
metadata = ton_data.get("metadata") or {}
for key, entry in metadata.items():
if _ton_raw_address(key) != target or not isinstance(entry, dict):
continue
infos = entry.get("token_info") or []
for info in infos if isinstance(infos, list) else []:
if not isinstance(info, dict):
continue
for candidate in (info.get("decimals"), (info.get("extra") or {}).get("decimals")):
if candidate is not None:
try:
return int(candidate)
except Exception:
pass
return None
def _ton_metadata_identity(master, ton_data):
"""Best-effort symbol/name for a TON jetton master from Toncenter metadata."""
target = _ton_raw_address(master)
metadata = ton_data.get("metadata") or {}
for key, entry in metadata.items():
if _ton_raw_address(key) != target or not isinstance(entry, dict):
continue
out = {"symbol": "", "name": ""}
infos = entry.get("token_info") or []
if isinstance(infos, list):
for info in infos:
if not isinstance(info, dict):
continue
if not out["symbol"]:
out["symbol"] = str(info.get("symbol") or (info.get("extra") or {}).get("symbol") or "")
if not out["name"]:
out["name"] = str(info.get("name") or (info.get("extra") or {}).get("name") or "")
return out
return {"symbol": "", "name": ""}
def _ton_stack_numbers(obj):
"""Extract integer TVM stack values from Toncenter's several response shapes."""
vals = []
def walk(x):
if isinstance(x, dict):
# Common Toncenter forms: {"number":"123"}, {"value":"0x..."}.
for k in ("number", "value"):
v = x.get(k)
if isinstance(v, str):
try:
vals.append(int(v, 0))
break
except Exception:
pass
for v in x.values():
if isinstance(v, (dict, list, tuple)):
walk(v)
elif isinstance(x, (list, tuple)):
# Classic stack entry: ["num", "0x123"] or ["num", "123"].
if len(x) >= 2 and isinstance(x[0], str) and x[0].lower() in ("num", "int"):
try:
vals.append(int(str(x[1]), 0))
return
except Exception:
pass
for v in x:
walk(v)
walk(obj)
return vals
def _ton_display_multiplier(master):
"""Read TEP-526 get_display_multiplier() = numerator / denominator on-chain."""
try:
data = _json_get(
f"{TONCENTER_API}/runGetMethod",
params={"address": master, "method": "get_display_multiplier", "stack": "[]"},
timeout=20,
)
if data.get("ok") is False:
return None
stack = (data.get("result") or {}).get("stack") or data.get("stack") or []
nums = _ton_stack_numbers(stack)
if len(nums) >= 2 and nums[0] > 0 and nums[1] > 0:
return Decimal(nums[0]) / Decimal(nums[1])
except Exception:
pass
return None
def fetch_ton_stock_assets_usd(wallet):
"""
Enumerate ALL non-zero TON jettons first. Native TON/GRAM is excluded.
Recognized portfolio xStocks are valued with their Yahoo Finance underlying
price. TON/issuer calls are used only for the display multiplier (quantity).
"""
ton_data = _toncenter_owner_jettons(wallet)
registry_raw = _xstocks_chain_index("ton")
registry = {}
for _, meta in registry_raw.items():
raw = _ton_raw_address(meta.get("address"))
if raw:
registry[raw] = meta
all_tokens = []
stock_items = []
failures = []
total = Decimal("0")
for row in ton_data.get("jetton_wallets") or []:
master = row.get("jetton")
raw_master = _ton_raw_address(master)
identity = _ton_metadata_identity(master, ton_data)
meta = registry.get(raw_master)
if not meta:
book = (ton_data.get("address_book") or {}).get(master) or {}
friendly = book.get("user_friendly") if isinstance(book, dict) else None
meta = registry.get(_ton_raw_address(friendly)) if friendly else None
raw_balance = D(row.get("balance"))
if raw_balance <= 0:
raw_balance = D((row.get("mintless_info") or {}).get("amount"))
if raw_balance <= 0:
continue
decimals = _ton_jetton_decimals(master, ton_data, meta or {})
raw_amount = None if decimals is None else raw_balance / (Decimal(10) ** decimals)
symbol = (meta or {}).get("symbol") or identity.get("symbol") or "?"
name = (meta or {}).get("name") or identity.get("name") or ""
ticker = (meta or {}).get("underlyingSymbol") or (symbol[:-1] if symbol.lower().endswith("x") else "")
include = bool(meta) and _portfolio_security_allowed(ticker=ticker, symbol=symbol)
token_diag = {
"master": master, "symbol": symbol, "name": name,
"raw_amount": str(raw_amount) if raw_amount is not None else "?",
"decimals": decimals, "recognized_xstock": bool(meta),
"portfolio_included": include, "underlying_ticker": ticker,
}
all_tokens.append(token_diag)
_audit_log(
f"[ALEF] TON jetton: {symbol} amount={token_diag['raw_amount']} "
f"master={master} xstock={bool(meta)} portfolio_include={include}", file=sys.stderr,
)
if not include:
continue
if raw_amount is None:
failures.append(f"{symbol}: unknown decimals")
continue
onchain_mult = _ton_display_multiplier(master)
multiplier, mult_source = _resolve_xstock_multiplier(
symbol, "TON", onchain=onchain_mult, registry_meta=(meta or {}),
)
amount = raw_amount * multiplier
price = _nav_stock_price_usd(ticker)
if price is None or price <= 0:
failures.append(f"{symbol}: Yahoo Finance has no price for {ticker}")
continue
value = amount * price
total += value
item = {
"symbol": symbol, "address": (meta or {}).get("address") or master,
"raw_amount": str(raw_amount), "multiplier": str(multiplier),
"multiplier_source": mult_source, "amount": str(amount),
"price_usd": str(price), "value_usd": str(value),
"underlying_symbol": ticker, "provider": "xstocks", "asset_type": "xstock",
"identity_source": "xstocks-official-registry", "price_source": "yfinance",
}
stock_items.append(item)
_audit_log(
f" {symbol}: {amount} {ticker}-equivalent Γ Yahoo ${price} = ${value:.6f} "
f"(multiplier={multiplier} via {mult_source})", file=sys.stderr,
)
if failures:
raise RuntimeError("TON portfolio stock holdings found but could not be fully valued: " + "; ".join(failures[:10]))
return {
"chain": "ton", "assets_usd": total, "items": stock_items,
"token_scan_complete": True, "tokenized_scan_complete": True,
"all_tokens": all_tokens, "stock_items": stock_items,
"xstocks_positions": len(stock_items),
"xstocks_deployments_scanned": len(registry),
"native_gram_included": False,
}
# ===============================================================
# ALEF SPL token supply from Solana
# ===============================================================
def fetch_solana_token_supply(mint):
payload = {
"jsonrpc": "2.0",
"id": 1,
"method": "getTokenSupply",
"params": [mint, {"commitment": "confirmed"}],
}
data = _json_post(SOLANA_RPC, payload, timeout=20)
if data.get("error"):
raise RuntimeError(f"Solana getTokenSupply error: {data['error']}")
value = ((data.get("result") or {}).get("value") or {})
ui = value.get("uiAmountString")
if ui is not None:
supply = D(ui)
else:
amount = D(value.get("amount"))
decimals = int(value.get("decimals", 0))
supply = amount / (Decimal(10) ** decimals)
if supply <= 0:
raise RuntimeError("ALEF token supply is zero or unavailable")
return supply
# ===============================================================
# Continuous fee state / anchor
# ===============================================================
def _load_fee_state():
if not os.path.exists(ALEF_STATE_FILE):
return None
with open(ALEF_STATE_FILE, "r", encoding="utf-8") as f:
return json.load(f)
def _save_fee_state(state):
tmp = ALEF_STATE_FILE + ".tmp"
with open(tmp, "w", encoding="utf-8") as f:
json.dump(state, f, indent=2)
os.replace(tmp, ALEF_STATE_FILE)
def _parse_utc(s):
if s.endswith("Z"):
s = s[:-1] + "+00:00"
dt = datetime.fromisoformat(s)
if dt.tzinfo is None:
dt = dt.replace(tzinfo=timezone.utc)
return dt.astimezone(timezone.utc)
def fetch_last_alef_supply_event():
"""
Return the datetime of the most recent mintTo/mintToChecked/burn/burnChecked
instruction on the ALEF mint, fetched directly from the Solana blockchain.
This makes the fee accrual start self-enforcing: every time ALEF is minted
or burned, the fee clock automatically resets to that transaction's timestamp.
That means newly issued supply is never over-charged, and no manual settlement
call is required β the blockchain IS the settlement record.
Falls back to FEE_ACCRUAL_START (ALEF deployment date) if the query fails or
returns no matching transactions.
"""
_ALEF_MINT = "FBHd9upXFkeWSwe9qEdcgRLa4Y6uzsLCSawrpfPkQZGg"
_SUPPLY_TYPES = {"mintTo", "mintToChecked", "burn", "burnChecked"}
# Reliable public endpoints tried in order; the official mainnet-beta RPC
# frequently times out on getSignaturesForAddress under load.
_RPCS = [
"https://solana-rpc.publicnode.com",
"https://rpc.ankr.com/solana",
"https://api.mainnet-beta.solana.com",
]
def _rpc(url, method, params):
payload = {"jsonrpc": "2.0", "id": 1, "method": method, "params": params}
r = requests.post(url, json=payload, timeout=25,
headers={"Content-Type": "application/json"})
r.raise_for_status()
data = r.json()
if data.get("error"):
raise RuntimeError(f"Solana RPC error: {data['error']}")
return data.get("result")
for rpc_url in _RPCS:
try:
before = None
# Scan up to 100 transactions in pages of 20; most transfers cluster
# near the top so a mint/burn is usually found within the first page.
for _page in range(5):
params = {"limit": 20, "commitment": "finalized"}
if before:
params["before"] = before
sigs = _rpc(rpc_url, "getSignaturesForAddress", [_ALEF_MINT, params]) or []
if not sigs:
break
before = sigs[-1].get("signature")
for sig_info in sigs:
sig = sig_info.get("signature")
if not sig:
continue
tx = _rpc(rpc_url, "getTransaction",
[sig, {"encoding": "jsonParsed",
"maxSupportedTransactionVersion": 0,
"commitment": "finalized"}])
if not tx:
continue
block_time = tx.get("blockTime")
if not block_time:
continue
msg = (tx.get("transaction") or {}).get("message") or {}
all_ixs = list(msg.get("instructions") or [])
for inner in (tx.get("meta") or {}).get("innerInstructions") or []:
all_ixs.extend(inner.get("instructions") or [])
for ix in all_ixs:
parsed = ix.get("parsed")
if not isinstance(parsed, dict):
continue
if parsed.get("type") not in _SUPPLY_TYPES:
continue
info = parsed.get("info") or {}
if info.get("mint") != _ALEF_MINT:
continue
return datetime.fromtimestamp(block_time, tz=timezone.utc)
# Exhausted 100 txs with no mint/burn found on this RPC β fall through
break
except Exception as exc:
_progress(f"fetch_last_alef_supply_event: {rpc_url} failed ({exc}), trying next RPC")
_progress("fetch_last_alef_supply_event: all RPCs failed or no event found β using FEE_ACCRUAL_START fallback")
return FEE_ACCRUAL_START
def settle_fees():
"""
With blockchain-derived fee accrual, settlement is automatic: every mint or
burn of ALEF resets the fee clock on-chain. No manual action is needed.
If you want to manually record a settlement (e.g. for accounting purposes),
call this function to update FEE_ACCRUAL_START in the script. Otherwise the
fee start date is always fetched live from the blockchain by apply_continuous_fee().
"""
now = datetime.now(timezone.utc)
new_line = (
f"FEE_ACCRUAL_START = datetime({now.year}, {now.month}, {now.day}, "
f"{now.hour}, {now.minute}, {now.second}, tzinfo=timezone.utc)"
f" # settled {now.strftime('%Y-%m-%d')}"
)
try:
script = os.path.abspath(__file__)
except NameError:
print("settle_fees(): cannot locate script file in notebook context β "
f"update FEE_ACCRUAL_START manually to: {new_line}")
return
with open(script, "r", encoding="utf-8") as f:
src = f.read()
import re as _re
new_src = _re.sub(r"^FEE_ACCRUAL_START = datetime\(.*$", new_line, src, flags=_re.MULTILINE)
if new_src == src:
print("settle_fees(): FEE_ACCRUAL_START line not found β update manually.")
return
with open(script, "w", encoding="utf-8") as f:
f.write(new_src)
print(f"Fee settlement recorded. FEE_ACCRUAL_START updated to {now.strftime('%Y-%m-%d %H:%M:%S')} UTC.")
def apply_continuous_fee(gross_nav_usd, token_supply):
"""
Apply the continuously accrued annual management fee.
The fee start date is the most recent ALEF mint-or-burn event on Solana,
fetched live from the blockchain. This makes fee accounting self-enforcing:
every supply change automatically resets the fee clock, so newly issued ALEF
is never over-charged with fees it hasn't accrued.
accrual_start = last mintTo/mintToChecked/burn/burnChecked on ALEF mint
elapsed_years = (now - accrual_start) in years
fee_factor = exp(-ANNUAL_FEE_RATE * elapsed_years)
fee_adjusted_nav = gross_nav * fee_factor
ALEF price = fee_adjusted_nav / token_supply
FEE_ACCRUAL_START (ALEF deployment date) is used only as a fallback if the
blockchain query fails.
"""
gross_nav_usd = D(gross_nav_usd)
token_supply = D(token_supply)
if gross_nav_usd <= 0 or token_supply <= 0:
raise RuntimeError("Gross NAV and ALEF token supply must be positive")
_progress("Fetching last ALEF mint/burn event for fee accrual start...")
accrual_start = fetch_last_alef_supply_event()
_progress(f"Fee accrual start: {accrual_start.strftime('%Y-%m-%d %H:%M:%S')} UTC")
now = datetime.now(timezone.utc)
elapsed_seconds = max(0.0, (now - accrual_start).total_seconds())
elapsed_years = elapsed_seconds / (365.25 * 24 * 60 * 60)
fee_factor = Decimal(str(math.exp(-float(ANNUAL_FEE_RATE) * elapsed_years)))
fee_adjusted_nav = gross_nav_usd * fee_factor
fee_drag_usd = gross_nav_usd - fee_adjusted_nav
alef_price = fee_adjusted_nav / token_supply
return {
"gross_nav_per_token": gross_nav_usd / token_supply,
"fee_adjusted_gross_nav_usd": fee_adjusted_nav,
"fee_adjusted_nav_usd": fee_adjusted_nav,
"fee_drag_usd": fee_drag_usd,
"alef_price_usd": alef_price,
"fee_multiplier": fee_factor,
"fee_accrual_start_utc": accrual_start.isoformat(),
"fee_elapsed_days": elapsed_seconds / 86400,
"newly_initialized": False,
}
# ===============================================================
# Aggregate ALEF NAV
# ===============================================================
def fetch_alef_nav_and_price(trx_usd, eth_usd=None, bnb_usd=None, sol_usd=None):
_progress("Loading published ALEF reserve addresses...")
addresses = fetch_alef_addresses()
_progress("Reserve addresses loaded.")
evm_wallet = addresses["evm"]
tron_wallet = addresses["tron"]
ton_wallet = addresses["ton"]
solana_wallet = addresses["solana"]
alef_mint = addresses["alef_mint"]
# Fill native-chain prices if they were not already fetched by main().
missing_ids = []
if eth_usd is None: missing_ids.append("ethereum")
if bnb_usd is None: missing_ids.append("binancecoin")
if sol_usd is None: missing_ids.append("solana")
if missing_ids:
_progress("Fetching missing native-chain reference prices...")
native = _coingecko_simple_id_prices(missing_ids)
eth_usd = D(eth_usd) if eth_usd is not None else native.get("ethereum")
bnb_usd = D(bnb_usd) if bnb_usd is not None else native.get("binancecoin")
sol_usd = D(sol_usd) if sol_usd is not None else native.get("solana")
if None in (eth_usd, bnb_usd, sol_usd):
raise RuntimeError("Missing one or more native prices required for ETH/BNB/SOL wallet NAV")
protocol_fetches = [
("Aave", lambda: fetch_aave_position_usd(evm_wallet)),
("Morpho", lambda: fetch_morpho_position_usd(evm_wallet)),
("JustLend", lambda: fetch_justlend_position_usd(tron_wallet, trx_usd)),
("Venus", lambda: fetch_venus_position_usd(evm_wallet, bnb_usd=bnb_usd)),
]
results = []
failures = []
for name, fn in protocol_fetches:
_progress(f"Reading {name} positions...")
try:
r = fn()
results.append(r)
_progress(f"{name} complete.")
extra = ""
if name == "Aave":
extra = f" version={r.get('version', '?')} positions={r.get('position_count', '?')}"
elif name == "Venus":
extra = (f" core_positions={r.get('core_position_count', 0)} "
f"flux_positions={r.get('flux_position_count', 0)} "
f"flux_lend_positions={r.get('flux_lend_position_count', 0)} "
f"flux_vault_positions={r.get('flux_vault_position_count', 0)} "
f"flux_source={r.get('flux_source', '?')} "
f"flux_rows={r.get('flux_rows_seen', 0)} "
f"flux_raw_positions={r.get('flux_raw_position_count', 0)} "
f"flux_vault_rows={r.get('flux_vault_rows_seen', 0)} "
f"markets_scanned={r.get('market_count_scanned', 0)} "
f"snapshot_batches={r.get('venus_snapshot_rpc_batches', 0)} "
f"vai_batches={r.get('venus_vai_rpc_batches', 0)}")
_audit_log(
f"[ALEF] {name}: assets=${r['assets_usd']:.6f} "
f"liabilities=${r['liabilities_usd']:.6f}{extra}",
file=sys.stderr,
)
if name == "Venus":
for pos in r.get("positions", []):
if str(pos.get("source", "")).startswith("flux"):
if pos.get("source") == "flux-vault-nft":
_audit_log(
f" Flux vault NFT #{pos.get('nft_id','?')}: "
f"supply {pos.get('supply_amount','?')} {pos.get('supply_symbol','?')} "
f"=${D(pos.get('supply_usd')):.6f}; "
f"borrow {pos.get('borrow_amount','?')} {pos.get('borrow_symbol','?')} "
f"=${D(pos.get('borrow_usd')):.6f}",
file=sys.stderr,
)
else:
_audit_log(
f" Flux {pos.get('symbol','?')}: amount={pos.get('amount','?')} "
f"value=${D(pos.get('supply_usd')):.6f}",
file=sys.stderr,
)
except Exception as e:
_progress(f"{name} failed.")
failures.append((name, e))
_audit_log(f"[WARN] ALEF {name} read failed: {e}", file=sys.stderr)
if failures and ALEF_STRICT_MODE:
names = ", ".join(name for name, _ in failures)
raise RuntimeError(
f"ALEF NAV aborted because these protocol reads failed: {names}. "
"Strict mode prevents publishing a partial NAV."
)
# Exclude JustLend receipt tokens from the raw TRON wallet so the supplied
# underlying is not counted twice.
justlend = next((r for r in results if r.get("protocol") == "JustLend"), {})
jtokens = justlend.get("jtoken_addresses") or []
# Morpho vault shares are already included by the Morpho API. Exclude
# their ERC-20 share tokens from the raw Ethereum wallet scan.
morpho = next((r for r in results if r.get("protocol") == "Morpho"), {})
morpho_receipts = []
for vp in (morpho.get("vault_positions") or []) + (morpho.get("vault_v2_positions") or []):
addr = (vp.get("vault") or {}).get("address")
if addr:
morpho_receipts.append(addr)
venus = next((r for r in results if r.get("protocol") == "Venus"), {})
venus_receipts = venus.get("receipt_token_addresses") or []
wallet_fetches = [
("Ethereum wallet", lambda: fetch_evm_wallet_assets_usd(
evm_wallet, chain="ethereum", rpc_url=ETHEREUM_RPC,
native_symbol="ETH", native_usd=eth_usd, exclude_contracts=morpho_receipts)),
("BNB wallet", lambda: fetch_evm_wallet_assets_usd(
evm_wallet, chain="bsc", rpc_url=BNB_RPC,
native_symbol="BNB", native_usd=bnb_usd, exclude_contracts=venus_receipts)),
("TRON wallet", lambda: fetch_tron_wallet_assets_usd(
tron_wallet, trx_usd, exclude_contracts=jtokens)),
("Solana wallet", lambda: fetch_solana_wallet_assets_usd(
solana_wallet, sol_usd, alef_mint,
extra_excluded_mints={SOLANA_USDC_MINT})),
("TON stock wallet", lambda: fetch_ton_stock_assets_usd(ton_wallet)),
]
wallet_results = []
wallet_failures = []
for name, fn in wallet_fetches:
_progress(f"Reading {name}...")
try:
r = fn()
if ALEF_STRICT_MODE and not r.get("tokenized_scan_complete", True):
raise RuntimeError("tokenized-asset cross-chain scan was incomplete")
if ALEF_STRICT_MODE and not r.get("token_scan_complete", True):
if r.get("chain") == "bsc":
_audit_log(
"[ALEF] BNB raw BEP-20 enumeration unavailable; native BNB and Venus "
"Core/isolated/Flux positions are still included. Optional ETHERSCAN_API_KEY enables raw-token scanning.",
file=sys.stderr,
)
else:
raise RuntimeError("token enumeration was incomplete")
wallet_results.append(r)
_progress(f"{name} complete.")
stock_items = r.get("stock_items") or []
if stock_items:
chain_label = {"ethereum": "Ethereum", "bsc": "BNB", "solana": "Solana", "ton": "TON"}.get(r.get("chain"), r.get("chain", "chain"))
summary = ", ".join(
f"{x.get('symbol','token')}[{x.get('provider','?')}]=${D(x.get('value_usd')):.2f}"
for x in stock_items
)
_audit_log(
f"[ALEF] {chain_label} tokenized stocks/ETFs ({len(stock_items)}): {summary}",
file=sys.stderr,
)
provider_diag = r.get("provider_diagnostics") or {}
for provider, pd in provider_diag.items():
_audit_log(
f"[ALEF] {name}: {provider} portfolio_contracts_probed={pd.get('deployments_scanned', 0)} "
f"registry_candidates={pd.get('registry_candidates', pd.get('deployments_scanned', 0))} "
f"positions_found={pd.get('positions', 0)}", file=sys.stderr,
)
if r.get("xstocks_deployments_scanned") is not None and not provider_diag:
_audit_log(
f"[ALEF] {name}: xStocks deployments scanned={r.get('xstocks_deployments_scanned')} "
f"positions_found={len(stock_items)}", file=sys.stderr,
)
_audit_log(f"[ALEF] {name}: raw assets=${r['assets_usd']:.6f}", file=sys.stderr)
for item in r.get("items") or []:
if D(item.get("value_usd")) >= Decimal("0.01"):
label = item.get("symbol") or item.get("mint") or item.get("address") or "asset"
_audit_log(f" {label}: ${D(item.get('value_usd')):.6f}", file=sys.stderr)
except Exception as e:
_progress(f"{name} failed.")
wallet_failures.append((name, e))
_audit_log(f"[WARN] ALEF {name} read failed: {e}", file=sys.stderr)
# USDC reserve: live Raydium pool USDC, fully on-chain.
# ALEF tokens on the other side of the pool are excluded β only USDC counts.
_progress("Fetching Raydium ALEF/USDC pool USDC for NAV (wallet LP share only)...")
_ray_usdc, _ray_pools = fetch_raydium_alef_usdc_reserves(solana_wallet)
if _ray_usdc is None:
_progress("WARNING: Raydium API unavailable β USDC reserve excluded from this NAV run.")
_ray_usdc = Decimal("0")
usdc_reserve = {
"chain": "usdc_reserve",
"assets_usd": _ray_usdc,
"liabilities_usd": Decimal("0"),
"items": [{
"symbol": "USDC (Raydium pool, on-chain)",
"amount": str(_ray_usdc),
"value_usd": str(_ray_usdc),
"source": "raydium-live",
}],
"raydium_usdc": str(_ray_usdc),
"token_scan_complete": True,
"tokenized_scan_complete": True,
}
_progress(f"Raydium USDC: ${_ray_usdc:,.2f}")
wallet_results.append(usdc_reserve)
_audit_log(f"[ALEF] Raydium USDC={_ray_usdc:.6f}", file=sys.stderr)
# A failed chain-level wallet read makes NAV materially incomplete. In
# strict mode, fail closed rather than silently publishing a low ALEF price.
if wallet_failures and ALEF_STRICT_MODE:
names = ", ".join(name for name, _ in wallet_failures)
raise RuntimeError(
f"ALEF NAV aborted because these wallet reads failed: {names}. "
"Strict mode prevents publishing a partial NAV."
)
protocol_assets = sum((r["assets_usd"] for r in results), Decimal("0"))
raw_wallet_assets = sum((r["assets_usd"] for r in wallet_results), Decimal("0"))
total_assets = protocol_assets + raw_wallet_assets + MANUAL_ASSETS_USD
total_liabilities = sum((r["liabilities_usd"] for r in results), Decimal("0")) + MANUAL_LIABILITIES_USD
gross_nav = total_assets - total_liabilities
_progress("Reading live ALEF token supply from Solana...")
token_supply = fetch_solana_token_supply(alef_mint)
_progress("Token supply loaded; applying continuous management fee...")
fee_data = apply_continuous_fee(gross_nav, token_supply)
_progress("NAV calculation complete.")
# Human-auditable accounting table. Protocol rows are aggregate where the
# upstream API provides aggregate USD values; wallet/issuer rows are shown
# asset-by-asset.
_audit_log("\n[ALEF] ================= NAV ACCOUNTING TABLE =================", file=sys.stderr)
_audit_log(f"{'TYPE':<10} {'LOCATION':<18} {'ASSET / POSITION':<34} {'USD':>16}", file=sys.stderr)
_audit_log("-" * 82, file=sys.stderr)
for r in results:
name = r.get("protocol", "protocol")
av = D(r.get("assets_usd"))
lv = D(r.get("liabilities_usd"))
if av:
_audit_log(f"{'ASSET':<10} {name:<18} {'supplied/collateral':<34} {av:>16,.2f}", file=sys.stderr)
if lv:
_audit_log(f"{'LIABILITY':<10} {name:<18} {'borrow/debt':<34} {lv:>16,.2f}", file=sys.stderr)
for r in wallet_results:
loc = {
'ethereum':'Ethereum', 'bsc':'BNB Chain', 'tron':'TRON',
'solana':'Solana', 'ton':'TON', 'raydium':'Raydium', 'usdc_reserve':'USDC Reserve'
}.get(r.get('chain'), str(r.get('chain') or 'wallet'))
for item in r.get("items") or []:
v = D(item.get("value_usd"))
if v <= 0:
continue
label = item.get("symbol") or item.get("mint") or item.get("address") or "asset"
_audit_log(f"{'ASSET':<10} {loc:<18} {str(label)[:34]:<34} {v:>16,.2f}", file=sys.stderr)
if MANUAL_ASSETS_USD:
_audit_log(f"{'ASSET':<10} {'Manual':<18} {'manual adjustment':<34} {MANUAL_ASSETS_USD:>16,.2f}", file=sys.stderr)
if MANUAL_LIABILITIES_USD:
_audit_log(f"{'LIABILITY':<10} {'Manual':<18} {'manual adjustment':<34} {MANUAL_LIABILITIES_USD:>16,.2f}", file=sys.stderr)
_audit_log("-" * 82, file=sys.stderr)
_audit_log(f"{'TOTAL':<10} {'':<18} {'ASSETS':<34} {total_assets:>16,.2f}", file=sys.stderr)
_audit_log(f"{'TOTAL':<10} {'':<18} {'LIABILITIES':<34} {total_liabilities:>16,.2f}", file=sys.stderr)
_audit_log(f"{'NAV':<10} {'':<18} {'GROSS NAV':<34} {gross_nav:>16,.2f}", file=sys.stderr)
_audit_log("=" * 82 + "\n", file=sys.stderr)
_audit_log(
f"[ALEF] PROTOCOL assets=${protocol_assets:.6f} | "
f"RAW WALLETS=${raw_wallet_assets:.6f}", file=sys.stderr)
if fee_data.get("newly_initialized"):
_audit_log(
"[ALEF] Fee clock initialized. No opening-price anchor is used; "
"ALEF price is always fee_adjusted_NAV / supply.",
file=sys.stderr,
)
_audit_log(
f"[ALEF] TOTAL assets=${total_assets:.6f} liabilities=${total_liabilities:.6f} "
f"gross_NAV=${gross_nav:.6f} supply={token_supply} "
f"fee_rate={float(ANNUAL_FEE_RATE) * 100:.4f}%/yr "
f"fee_multiplier={fee_data['fee_multiplier']:.10f} "
f"fee_drag=${fee_data['fee_drag_usd']:.6f} "
f"fee_adjusted_NAV=${fee_data['fee_adjusted_nav_usd']:.6f} "
f"NAV_per_ALEF=${fee_data['alef_price_usd']:.8f} "
f"ALEF=${fee_data['alef_price_usd']:.8f}",
file=sys.stderr,
)
return {
"addresses": addresses,
"protocols": results,
"wallets": wallet_results,
"protocol_assets_usd": protocol_assets,
"raw_wallet_assets_usd": raw_wallet_assets,
"assets_usd": total_assets,
"liabilities_usd": total_liabilities,
"gross_nav_usd": gross_nav,
"token_supply": token_supply,
**fee_data,
"raydium_usdc": next((w.get("raydium_usdc") for w in wallet_results if "raydium_usdc" in w), None),
}
def fetch_raydium_alef_usdc_reserves(solana_wallet):
"""
Return the USDC in all ALEF/USDC Raydium pools by reading the pool state
directly from the Raydium API. The reserve address is the sole liquidity
provider in these pools, so total pool USDC equals the reserve's USDC.
Returns (Decimal, int) = (usdc_amount, pool_count).
"""
_ALEF_MINT = "FBHd9upXFkeWSwe9qEdcgRLa4Y6uzsLCSawrpfPkQZGg"
try:
resp = _json_get(
"https://api-v3.raydium.io/pools/info/mint"
f"?mint1={_ALEF_MINT}&mint2={SOLANA_USDC_MINT}"
"&poolType=all&poolSortField=liquidity&sortType=desc&pageSize=20&page=1",
timeout=20,
)
pools = (resp or {}).get("data", {}).get("data") or []
total_usdc = Decimal("0")
pool_count = 0
for pool in pools:
mint_a = (pool.get("mintA") or {}).get("address", "")
mint_b = (pool.get("mintB") or {}).get("address", "")
if mint_a == SOLANA_USDC_MINT:
total_usdc += D(pool.get("mintAmountA") or 0)
pool_count += 1
elif mint_b == SOLANA_USDC_MINT:
total_usdc += D(pool.get("mintAmountB") or 0)
pool_count += 1
return total_usdc, pool_count
except Exception as exc:
_progress(f"Raydium pool query failed: {exc}")
return None, 0
# ===============================================================
# Public auditor entry point
# ===============================================================
def calculate_alef_nav():
"""
Calculate ALEF NAV/share and return the complete structured audit result.
Only non-financial progress is printed; no prices/balances are printed and
nothing is copied to the clipboard. USD marks use
the same Yahoo Finance / CoinGecko price functions as the operational build.
The returned result includes:
addresses, protocols, wallets, protocol_assets_usd, raw_wallet_assets_usd,
assets_usd, liabilities_usd, gross_nav_usd, token_supply, fee_multiplier,
fee_drag_usd, fee_adjusted_nav_usd, and alef_price_usd.
"""
_progress("Starting live NAV audit.")
crypto_symbols = ['BTC', 'BNB', 'XRP', 'TRX', 'ETH', 'LEO', '', 'XAUT', 'PAXG']
stock_tickers = ['SLV', '3GOL.L', 'UGL', 'PLTM', '', 'TQQQ', 'MAGX', 'AAPL',
'NVDA', 'AMZN', 'GOOG', 'TSLA', 'MSFT', 'META']
_progress("Fetching CoinGecko/Yahoo reference prices...")
crypto_prices = fetch_crypto_prices(crypto_symbols)
stock_prices = fetch_stock_prices(stock_tickers)
_progress("Reference prices loaded.")
_seed_nav_price_caches(crypto_symbols, crypto_prices, stock_tickers, stock_prices)
crypto_price_map = {
sym: price for sym, price in zip(crypto_symbols, crypto_prices)
if sym and price is not None
}
trx_usd = crypto_price_map.get("TRX")
if trx_usd is None:
raise RuntimeError("Cannot calculate ALEF NAV without TRX/USD for JustLend")
return fetch_alef_nav_and_price(
trx_usd,
eth_usd=crypto_price_map.get("ETH"),
bnb_usd=crypto_price_map.get("BNB"),
sol_usd=crypto_price_map.get("SOL"),
)
def _money(x):
return f"${D(x):,.2f}"
def _qty(x, places=8):
d = D(x)
s = f"{d:,.{places}f}".rstrip("0").rstrip(".")
return s if s else "0"
def _wallet_location(chain):
return {
"ethereum": "Ethereum",
"bsc": "BNB Chain",
"tron": "TRON",
"solana": "Solana",
"ton": "TON",
"usdc_reserve": "USDC Reserve",
}.get(str(chain or ""), str(chain or "Wallet"))
def print_audit_report(result):
"""Print a human-readable financial audit report from calculate_alef_nav()."""
line = "=" * 88
thin = "-" * 88
print("\n" + line)
print("ALEF NAV AUDIT REPORT")
print(line)
print(f"Generated (UTC): {datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M:%S')}")
print(f"Audit build: {AUDIT_BUILD}")
print(f"Management fee: {float(ANNUAL_FEE_RATE) * 100:.4f}% per year, continuous")
print("Price sources: Yahoo Finance / CoinGecko")
print("ALEF pricing rule: fee-adjusted NAV / live ALEF supply")
addresses = result.get("addresses") or {}
if addresses:
print("\nRESERVE ADDRESSES")
print(thin)
labels = [
("EVM (Ethereum/BNB)", "evm"),
("TRON", "tron"),
("Solana", "solana"),
("TON", "ton"),
("ALEF mint", "alef_mint"),
]
for label, key in labels:
if addresses.get(key):
print(f"{label:<22} {addresses[key]}")
print("\nPROTOCOL POSITIONS")
print(thin)
print(f"{'Protocol':<18}{'Assets':>20}{'Liabilities':>20}{'Net':>20}")
print(thin)
for row in result.get("protocols") or []:
name = str(row.get("protocol") or "Protocol")
assets = D(row.get("assets_usd"))
liabilities = D(row.get("liabilities_usd"))
print(f"{name:<18}{_money(assets):>20}{_money(liabilities):>20}{_money(assets-liabilities):>20}")
print("\nWALLET / RESERVE ASSETS")
print(thin)
print(f"{'Location':<18}{'Asset':<34}{'Amount':>16}{'USD value':>20}")
print(thin)
item_count = 0
for wallet in result.get("wallets") or []:
location = _wallet_location(wallet.get("chain"))
items = wallet.get("items") or []
for item in items:
value = D(item.get("value_usd"))
if value <= 0:
continue
label = str(item.get("symbol") or item.get("ticker") or item.get("mint") or item.get("address") or "asset")
amount = item.get("amount")
amount_text = _qty(amount) if amount not in (None, "") else "β"
print(f"{location:<18}{label[:34]:<34}{amount_text:>16}{_money(value):>20}")
item_count += 1
# Some wallet readers expose only an aggregate value. Show it if there
# are no item rows so the report still reconciles to the returned NAV.
if not items and D(wallet.get("assets_usd")) > 0:
value = D(wallet.get("assets_usd"))
print(f"{location:<18}{'(aggregate wallet assets)':<34}{'β':>16}{_money(value):>20}")
item_count += 1
if item_count == 0:
print("(No positive wallet/reserve asset rows returned.)")
print("\nNAV SUMMARY")
print(thin)
summary_rows = [
("Protocol assets", result.get("protocol_assets_usd")),
("Wallet/reserve assets", result.get("raw_wallet_assets_usd")),
("TOTAL ASSETS", result.get("assets_usd")),
("TOTAL LIABILITIES", result.get("liabilities_usd")),
("GROSS NAV", result.get("gross_nav_usd")),
("Management-fee drag", result.get("fee_drag_usd")),
("FEE-ADJUSTED NAV", result.get("fee_adjusted_nav_usd")),
]
for label, value in summary_rows:
print(f"{label:<36}{_money(value):>22}")
supply = D(result.get("token_supply"))
fee_mult = D(result.get("fee_multiplier"), "1")
price = D(result.get("alef_price_usd"))
recomputed = D(result.get("fee_adjusted_nav_usd")) / supply if supply > 0 else Decimal("0")
fee_start = result.get("fee_accrual_start_utc", "β")
fee_days = result.get("fee_elapsed_days")
print(f"{'Fee accrual since':<36}{fee_start:>22}")
if fee_days is not None:
print(f"{'Elapsed':<36}{f'{fee_days:.2f} days':>22}")
print(f"{'Live ALEF supply':<36}{_qty(supply, 8):>22}")
print(thin)
print(f"{'ALEF NAV PER TOKEN':<36}{('$' + _qty(price, 8)):>22}")
print(line)
diff = abs(price - recomputed)
print(
"Accounting check: "
+ ("PASS" if diff <= Decimal("0.000000000001") else "FAIL")
+ " β reported ALEF price "
+ ("equals" if diff <= Decimal("0.000000000001") else "does not equal")
+ " fee-adjusted NAV / live supply."
)
print(line)
print()
_ray_nav = result.get("raydium_usdc")
if _ray_nav is not None:
print("\nRAYDIUM POOL USDC (used in NAV)")
print(thin)
print(f"{'Raydium pool USDC (on-chain)':<36}{_money(_ray_nav):>22}")
print(line + "\n")
# Familiar alias for users who expect a main() entry point.
def main():
result = calculate_alef_nav()
print_audit_report(result)
return result
# ===============================================================
# Automatic auditor execution
# ===============================================================
# Running the full source in Jupyter or as a script performs one live audit,
# shows progress while network calls run, prints the human-readable report,
# and stores the complete structured result in ALEF_RESULT for inspection.
if __name__ == "__main__":
ALEF_RESULT = calculate_alef_nav()
_progress("Live calculation complete; rendering audit report.")
print_audit_report(ALEF_RESULT)
_progress("Complete. Structured result stored in ALEF_RESULT.")
0x34292E356Def567bA65a2351ec1DD2b738ec4A57
Send a small token amount to the reserve address you want verified, then submit this form. We will send a transaction back from that same address to confirm control. The token amount will be returned.
Telegram: @AlefMoney Β· WhatsApp / Phone: +1 321 448 3108
Pause halts all minting and transfers. Requires admin majority.