Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions admin_panel/api/census_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
# cashwalletmigrations statuses that count as a completed migration.
MIGRATED_STATUSES = {"completed", "complete", "succeeded", "success", "done"}

# Balances at or below this are zero for classification. IBEX emits
# sub-nanodollar dust (~1e-10 observed on prod, first full census): those rows
# classified as "funded" pre-rounding but displayed $0.00. Matches the cutover
# verifier's dust tolerance.
FUNDED_EPSILON = 1e-6


class PageLimitExceeded(Exception):
"""sweep_pages hit max_pages — the API is paging forever or the cap is too low."""
Expand Down Expand Up @@ -94,8 +100,10 @@ def build_census(ibex_accounts, wallets, accounts, migrations) -> dict:
migration = migrations.get(account_id)

raw_balance = account.get("balance")
balance = float(raw_balance) if raw_balance else 0.0
funded = balance > 0
# Round to stored precision BEFORE classifying so "funded" and the
# displayed balance can never disagree.
balance = round(float(raw_balance), 8) if raw_balance else 0.0
funded = balance > FUNDED_EPSILON

currency = wallet.get("currency") or CURRENCY_BY_ID.get(account.get("currencyId"))
status = acct.get("status")
Expand Down Expand Up @@ -146,7 +154,7 @@ def build_census(ibex_accounts, wallets, accounts, migrations) -> dict:
"account_id": account_id,
"wallet_id": wallet_id,
"currency": currency,
"balance": round(balance, 8),
"balance": balance,
"status": status,
"level": acct.get("level"),
"role": role,
Expand All @@ -162,7 +170,7 @@ def build_census(ibex_accounts, wallets, accounts, migrations) -> dict:
)

rows.sort(key=lambda r: r["balance"], reverse=True)
funded_count = sum(1 for r in rows if r["balance"] > 0)
funded_count = sum(1 for r in rows if r["balance"] > FUNDED_EPSILON)

# BTC wallets exist in mongo but hold no IBEX balance — report the count so
# operators know it's intentional, not a gap.
Expand Down
23 changes: 23 additions & 0 deletions admin_panel/tests/test_census_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,3 +215,26 @@ def test_sweep_pages_empty_first_page():
from admin_panel.api.census_core import sweep_pages

assert list(sweep_pages(lambda p: [], max_pages=5)) == []


def test_micro_dust_is_not_funded():
"""IBEX emits sub-nanodollar dust (~1e-10 seen on prod): it must classify
as zero — 91 rows showed 'active_funded' at $0.00 on the first full census."""
ibex = [{"id": "w-dust", "name": "acc-dust", "currencyId": 29, "balance": 3e-10}]
accounts = {
"acc-dust": {
"username": "dusty",
"role": "user",
"status": "active",
"default_wallet_id": "w-dust",
}
}
wallets = {"w-dust": {"account_id": "acc-dust", "currency": "Usdt", "type": "Checking"}}

result = build_census(ibex, wallets, accounts, {})
row = result["rows"][0]

assert row["balance"] == 0.0
assert row["buckets"] == ["active_zero"]
assert result["totals"]["funded"] == 0
assert result["bucket_counts"]["active_funded"] == 0
Loading