Skip to content

fix: don't fail settlement on empty reward_distribution_detailed - #86

Merged
oten91 merged 1 commit into
mainfrom
fix/claim-settled-empty-reward-distribution
Jul 26, 2026
Merged

fix: don't fail settlement on empty reward_distribution_detailed#86
oten91 merged 1 commit into
mainfrom
fix/claim-settled-empty-reward-distribution

Conversation

@oten91

@oten91 oten91 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Summary

The indexer halts on mainnet block 852613 with an uncaught worker exception:

Error: Missing TLM_GLOBAL_MINT_DAO_REWARD_DISTRIBUTION in reward_distribution_detailed for event 852613-finalize_block-4249
    at t.handleEventClaimSettled (/home/app/dist/index.js:88733:36)
Node.js v18.20.3

A fully overserviced claim settles to zero and the chain emits reward_distribution_detailed as an empty array. Because [] is truthy, _resolveSettlementParts still routes the claim into _buildSettlementFromDetailedDistribution, both global-mint sums stay at zero, and the invariant added in #78 throws. The throw escapes handleEventClaimSettled and kills the subql worker, so indexing stops.

Root cause, verified against chain data

The offending event, from block_results at height 852613:

type = pocket.tokenomics.EventClaimSettled
claimed_upokt                = "183093upokt"
settled_upokt                = "0upokt"
minted_upokt                 = "0upokt"
overservicing_loss_upokt     = "183093upokt"
mint_ratio                   = "0.975"
reward_distribution          = {}
reward_distribution_detailed = []
service_id                   = "opbnb"

Across all 3033 EventClaimSettled in that block:

count
empty reward_distribution_detailed 43
settled_upokt == "0upokt" 43
mismatch (empty XOR settled-zero) 0

Exactly 1:1 — an empty detailed distribution means a zero-settlement claim, nothing else. Global inflation is enabled on this block (the other 2990 claims all carry TLM_GLOBAL_MINT_DAO_REWARD_DISTRIBUTION and TLM_GLOBAL_MINT_REIMBURSEMENT_REQUEST_ESCROW_DAO_TRANSFER), so the assertion was not detecting a misconfigured chain.

The invariant is unsound for a second reason as well: in poktroll, tlm_global_mint.go early-returns when globalInflationPerClaim == 0 and skips the DAO append when daoAmount.IsZero(); tlm_reimbursement_requests.go has the same zero-inflation early return. Absence of these entries is a valid chain state.

Change

Drop both throws. Keep the base TLM_RELAY_BURN_EQUALS_MINT_TOKENOMICS_CLAIM_DISTRIBUTION_MINT entry unconditionally, and append the global-mint inflation and reimbursement mints only when their amounts are non-zero.

effectiveBurn already resolves to "0" for these claims (_handleEventClaimSettled prefers settled_upokt), so a zero-settlement claim now produces zero burn and zero mint rows rather than a dead worker.

Downstream impact

None. get_mint_breakdown_between_dates SUMs over jsonb_array_elements(t.mints), so an absent zero-amount row contributes exactly what a present one did.

Recovery

Restart the indexer — nothing was written for 852613 before the throw, so it re-indexes the block cleanly. The pattern recurs on any block containing a fully overserviced claim.

Follow-up (not in this PR)

inflationAmount sums only TLM_GLOBAL_MINT_DAO_REWARD_DISTRIBUTION, but global mint also pays other recipients — block 852613 contains 606 TLM_GLOBAL_MINT_SUPPLIER_SHAREHOLDER_REWARD_DISTRIBUTION and 69 TLM_GLOBAL_MINT_SOURCE_OWNER_REWARD_DISTRIBUTION entries. Since get_mint_breakdown_between_dates treats opReason 5 as total inflation, reported inflation undercounts by that share. Pre-existing and separate from this crash.

Test plan

  • Re-index from 852613 and confirm the worker survives the 43 zero-settlement claims
  • Confirm get_mint_breakdown_between_dates over a range covering 852613 matches pre-incident values

🤖 Generated with Claude Code

A fully overserviced claim settles to zero (settled_upokt == 0upokt) and
the chain emits reward_distribution_detailed as an empty array. Since []
is truthy, _resolveSettlementParts still routes these claims through
_buildSettlementFromDetailedDistribution, where both global-mint sums stay
at zero and the invariant added in #78 throws. The throw propagates out of
handleEventClaimSettled and kills the subql worker, halting the indexer:

  Error: Missing TLM_GLOBAL_MINT_DAO_REWARD_DISTRIBUTION in
  reward_distribution_detailed for event 852613-finalize_block-4249

Verified against mainnet block_results for height 852613: of 3033
EventClaimSettled, 43 carry an empty reward_distribution_detailed, and
those are exactly the 43 with settled_upokt == 0upokt (no mismatch in
either direction). Global inflation is enabled on that block, so the
assertion was not detecting a misconfigured chain.

The invariant is also wrong independently of overservicing: both global
mint TLMs early-return when global_inflation_per_claim is zero, and
tlm_global_mint skips the DAO append when its share truncates to zero.

Record the global-mint inflation and reimbursement mints only when their
amounts are non-zero instead of throwing. The base burn-equals-mint entry
is unchanged, and effectiveBurn already resolves to "0" for these claims
(it prefers settled_upokt), so a zero-settlement claim now yields zero
burn and zero mint rows. Downstream is unaffected:
get_mint_breakdown_between_dates SUMs over jsonb_array_elements(mints),
where an absent zero row contributes the same as a present one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@oten91
oten91 merged commit 7bcd314 into main Jul 26, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant