Skip to content

fix(dao): correct APC math for withdrawing cells (Telegram 9)#276

Merged
RaheemJnr merged 2 commits into
mainfrom
fix/dao-apc-withdrawing-cell-math
May 22, 2026
Merged

fix(dao): correct APC math for withdrawing cells (Telegram 9)#276
RaheemJnr merged 2 commits into
mainfrom
fix/dao-apc-withdrawing-cell-math

Conversation

@RaheemJnr
Copy link
Copy Markdown
Owner

@RaheemJnr RaheemJnr commented May 22, 2026

Summary

Telegram bug 9: user (georgiev) reported "dao apc is wrong i think, it's says 1.55%". Investigation found a real bug in the math for withdrawing cells.

Root cause

DaoDepositReader.kt:246 used `(now - depositTime)` as elapsed time for the APC calc:

```kotlin
val elapsedDays = (System.currentTimeMillis() - depositTimestampMs) / 86_400_000.0
apc = (compensation / capacity) / (elapsedDays / 365.25) * 100.0
```

For a deposited cell that's still accruing, `now` is the correct endpoint.

For a withdrawing cell, compensation stops accruing the moment the deposit transitions to withdrawing. Wall-clock keeps advancing past that point, so dividing by a longer window understates the realised APC.

Fix

Branch on `isWithdrawing`. For withdrawing cells, use the withdraw-block header timestamp as the endpoint (same source the compensation amount itself comes from). For deposited cells, keep wall-clock `now`.

```kotlin
val endTimeMs = if (isWithdrawing) {
cellBlockHeader?.timestamp?.removePrefix("0x")?.toLong(16)
?: System.currentTimeMillis()
} else {
System.currentTimeMillis()
}
val elapsedDays = (endTimeMs - depositTimestampMs) / 86_400_000.0
```

Also added DEBUG-level logging of `(compensation, capacity, elapsedDays, apc, isWithdrawing)` so a future APC report can be cross-checked against logcat without needing instrumentation.

Caveat

This may not move 1.55% all the way to the user's expected number — actual CKB DAO compensation has been declining as secondary issuance progresses, and values in the 1.5-2.0% range are within normal. But the math is now correct for the withdrawing case, which is the only branch with an identifiable error.

Test plan

  • `./gradlew :app:compileDebugKotlin -x cargoBuild` BUILD SUCCESSFUL
  • `./gradlew :app:testDebugUnitTest -x cargoBuild` BUILD SUCCESSFUL
  • Manual: deposit cell — verify APC reads from logcat match expected (~1.7-2%)
  • Manual: withdrawing cell — verify APC reads higher than before this PR (now correctly uses withdraw timestamp as endpoint)

Refs Telegram bug report (georgiev, 2026-05-21)

Summary by CodeRabbit

Bug Fixes

  • Improved accuracy of deposit calculations to properly account for different withdrawal states.

Review Change Stack

…g cells (Telegram 9)

User reported the DAO APC reading 1.55% which felt low. Investigation found
that for cells in the withdrawing state, the APC calculation used wall-clock
`now` as the upper bound on elapsed time, but compensation stops accruing
the moment a deposit moves to withdrawing. Wall-clock keeps ticking past
that point, so dividing by a longer window understates the APC.

Fix: for withdrawing cells, use the withdraw-block header timestamp as the
endpoint. For deposited (still-accruing) cells, keep wall-clock `now`. The
deposit-side timestamp is unchanged in both branches.

Also added INFO-level logging of (compensation, capacity, elapsedDays, apc,
isWithdrawing) so the next time a user reports an APC value we have the
raw inputs to verify in logcat without instrumentation.

This may not move 1.55% all the way to the user's expected number — actual
CKB DAO compensation has been declining as secondary issuance progresses,
so values in the 1.5-2% range are within normal — but the math is now
correct for the withdrawing case which is the only branch with an
identifiable error.

Builds clean. Unit tests pass.

Refs Telegram bug report (georgiev, 2026-05-21)
@vercel
Copy link
Copy Markdown

vercel Bot commented May 22, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
pocket-node Ready Ready Preview, Comment May 22, 2026 4:12pm

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 22, 2026

📝 Walkthrough

Walkthrough

DaoDepositReader.resolveOneDeposit updates its APC calculation to apply a state-dependent elapsed-time upper bound: when a DAO deposit is withdrawing, APC uses the withdraw block header timestamp (falling back to wall-clock if unavailable); when deposited, it uses wall-clock time. The debug log output expands to report elapsed days, computed APC, and the withdrawing state.

Changes

APC state-dependent calculation

Layer / File(s) Summary
State-dependent APC elapsed-time calculation
android/app/src/main/java/com/rjnr/pocketnode/data/gateway/DaoDepositReader.kt
resolveOneDeposit now computes endTimeMs conditionally: withdraw block header timestamp (or wall-clock fallback) when withdrawing, wall-clock when deposited. APC is recomputed from the resulting elapsed window. Debug logging expanded to include formatted elapsed days, APC value, and withdrawing flag.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~8 minutes

Poem

A DAO's coin counts time in two ways,
Now withdrawn and held on separate days.
One reads the block when coins take flight,
The other counts now—both numbers right! 🐰⏳

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: correcting APC (Annual Percentage Compound) calculation for withdrawing cells in the DAO module, which is the core fix in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dao-apc-withdrawing-cell-math

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@android/app/src/main/java/com/rjnr/pocketnode/data/gateway/DaoDepositReader.kt`:
- Around line 259-263: For withdrawing cells, remove the wall-clock fallback
used when computing endTimeMs (the if branch using isWithdrawing and
cellBlockHeader?.timestamp) and instead detect missing or malformed timestamps:
attempt to parse cellBlockHeader.timestamp (reference symbols: endTimeMs,
isWithdrawing, cellBlockHeader.timestamp, compensation, apc) inside a safe parse
(try/catch or runCatching) and if parsing fails or timestamp is null do not
compute APC (leave apc as 0.0) and emit a warning log indicating the
missing/invalid withdraw block timestamp for that cell; only for non-withdrawing
cells continue using System.currentTimeMillis() as before. Ensure
NumberFormatException is caught so parsing errors don’t fall back to wall-clock
time.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a7a24705-611b-4a76-833f-06b9de3c8a49

📥 Commits

Reviewing files that changed from the base of the PR and between 3c4edd7 and c810ca6.

📒 Files selected for processing (1)
  • android/app/src/main/java/com/rjnr/pocketnode/data/gateway/DaoDepositReader.kt

Comment on lines +259 to +263
val endTimeMs = if (isWithdrawing) {
cellBlockHeader?.timestamp?.removePrefix("0x")?.toLong(16) ?: System.currentTimeMillis()
} else {
System.currentTimeMillis()
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

The wall-clock fallback for withdrawing cells defeats the purpose of the fix.

For withdrawing cells, if cellBlockHeader?.timestamp is null or cannot be parsed, falling back to System.currentTimeMillis() reintroduces the bug this PR aims to fix: the elapsed window would be overstated and APC understated.

If the withdraw block timestamp is unavailable for a withdrawing cell where compensation > 0, the data is inconsistent (compensation requires the header). Rather than silently producing incorrect APC with the wall-clock fallback, consider:

  1. Skipping APC calculation (apc remains 0.0)
  2. Logging a warning about the missing timestamp

Additionally, toLong(16) can throw NumberFormatException if the hex string is malformed; the safe-call operator only handles null, not exceptions.

🛡️ Proposed fix to handle missing timestamp safely
-        val endTimeMs = if (isWithdrawing) {
-            cellBlockHeader?.timestamp?.removePrefix("0x")?.toLong(16) ?: System.currentTimeMillis()
-        } else {
-            System.currentTimeMillis()
-        }
+        val endTimeMs = if (isWithdrawing) {
+            val timestampHex = cellBlockHeader?.timestamp?.removePrefix("0x")
+            if (timestampHex != null) {
+                try {
+                    timestampHex.toLong(16)
+                } catch (e: NumberFormatException) {
+                    Log.w(TAG, "Malformed timestamp for withdrawing cell $cellId, skipping APC calculation", e)
+                    -1L  // sentinel to skip APC calculation below
+                }
+            } else {
+                Log.w(TAG, "Missing timestamp for withdrawing cell $cellId, skipping APC calculation")
+                -1L  // sentinel to skip APC calculation below
+            }
+        } else {
+            System.currentTimeMillis()
+        }
         val elapsedDays = (endTimeMs - depositTimestampMs) / 86_400_000.0
-        if (elapsedDays >= 1.0) {
+        if (endTimeMs > 0 && elapsedDays >= 1.0) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@android/app/src/main/java/com/rjnr/pocketnode/data/gateway/DaoDepositReader.kt`
around lines 259 - 263, For withdrawing cells, remove the wall-clock fallback
used when computing endTimeMs (the if branch using isWithdrawing and
cellBlockHeader?.timestamp) and instead detect missing or malformed timestamps:
attempt to parse cellBlockHeader.timestamp (reference symbols: endTimeMs,
isWithdrawing, cellBlockHeader.timestamp, compensation, apc) inside a safe parse
(try/catch or runCatching) and if parsing fails or timestamp is null do not
compute APC (leave apc as 0.0) and emit a warning log indicating the
missing/invalid withdraw block timestamp for that cell; only for non-withdrawing
cells continue using System.currentTimeMillis() as before. Ensure
NumberFormatException is caught so parsing errors don’t fall back to wall-clock
time.

@RaheemJnr RaheemJnr merged commit d0b2297 into main May 22, 2026
6 of 7 checks passed
@RaheemJnr RaheemJnr deleted the fix/dao-apc-withdrawing-cell-math branch May 22, 2026 16:18
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