Skip to content

feat(permissionless-groups): held-balance fields + migrateWithRetry#74

Open
leinss wants to merge 1 commit into
mainfrom
feature/balance-held-totals
Open

feat(permissionless-groups): held-balance fields + migrateWithRetry#74
leinss wants to merge 1 commit into
mainfrom
feature/balance-held-totals

Conversation

@leinss

@leinss leinss commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds deterministic held-balance figures to balance() and a resilient migration submit helper. The migration pathfinder returns a network max-flow that moves block-to-block as the trust graph changes, so any figure derived from it is inherently unstable. This change gives callers stable, holdings-only numbers (independent of the pathfinder) and hardens migration execution against that same staleness. All changes are additive / non-breaking.

What changed

balance()

  • personalHeldTotal + per-token heldTotal — raw held personal CRC, before the migration-outflow subtraction; deterministic for a given chain state.
  • { includeMigratable: false } option — skips the pathfinder probe entirely and returns held-only figures (faster, fully deterministic; scoreGroupTotal === scoreGroupHeldTotal).
  • migratableAtBlock, plus migratableProbe (ok|skipped|failed) and personalProbe (ok|failed) — surface which block the live estimate was computed at, and whether each fallible read succeeded, so a swallowed RPC failure is distinguishable from a genuine zero.
  • Single demurrage timestamp threaded through all conversions in the call.
  • Field docs now label each figure deterministic vs live estimate.

Migration

  • MigrationResult.edges — flow-edge count (path complexity / fragility gauge).
  • migrateWithRetry(params, submit, options) — re-queries the pathfinder fresh on each attempt and shrinks the edge cap on a retryable revert (shorter, less-stale route); rethrows non-retryable submit errors (e.g. user rejection) instead of re-prompting; returns a discriminated result with reason: 'empty' | 'exhausted'; validates options.

Misc

  • Normalize PathfindingResult.graphBlock to bigint at the RPC boundary.
  • Correct maxEdges docs (forwarded to the pathfinder's maxTransfers; not a hard cap on the number of returned edges).

Test plan

  • bun run build (typecheck) passes
  • bun test in packages/permissionless-groups passes (70 tests)
  • personalHeldTotal / scoreGroupHeldTotal are stable across repeated balance() calls while scoreGroupMigratable may vary
  • balance(avatar, { includeMigratable: false }) returns scoreGroupTotal === scoreGroupHeldTotal and issues no pathfinder call
  • migrateWithRetry retries with a shrinking edge cap on a retryable revert, rethrows a user-rejection error, and reports reason: 'exhausted' when all attempts revert

Add deterministic held-balance figures alongside the existing virtual
balance, and a resilient migration submit helper. The migration pathfinder
returns a network max-flow that moves block-to-block as the trust graph
changes, so figures derived from it are inherently unstable; this change
gives callers stable, holdings-only numbers and hardens migration execution
against that staleness. Additive / non-breaking.

balance():
- add personalHeldTotal + per-token heldTotal (raw held personal CRC, before
  the migration outflow subtraction) — deterministic for a given chain state
- add `{ includeMigratable: false }` option to skip the pathfinder probe and
  return held-only figures (faster, fully deterministic)
- surface migratableAtBlock, plus migratableProbe / personalProbe so a
  swallowed RPC failure is distinguishable from a genuine zero
- thread a single demurrage timestamp through all conversions in the call
- document each field as deterministic vs live estimate

migration():
- expose MigrationResult.edges (flow-edge count; fragility gauge)
- add migrateWithRetry(): re-queries the pathfinder fresh per attempt and
  shrinks the edge cap on a retryable revert (shorter, less-stale route);
  rethrows non-retryable submit errors; returns a discriminated result with
  reason 'empty' | 'exhausted'; validates options

misc:
- normalize PathfindingResult.graphBlock to bigint at the RPC boundary
- correct maxEdges docs (forwarded to the pathfinder's maxTransfers; not a
  hard cap on the number of returned edges)

tests:
- held-vs-live breakdown across ERC1155 / demurrage-ERC20 / inflationary-ERC20
  forms, incl. tokenAddress-keyed outflow subtraction
- migrateWithRetry: hop reduction, error classification, build-error retry,
  defaults, and option validation
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@leinss, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 37859480-8a6c-4e84-8dee-0346b7d469bc

📥 Commits

Reviewing files that changed from the base of the PR and between 8ba071e and efb1051.

📒 Files selected for processing (6)
  • packages/permissionless-groups/src/PermissionlessGroup.ts
  • packages/permissionless-groups/src/tests/migrateWithRetry.test.ts
  • packages/permissionless-groups/src/tests/personalBreakdown.test.ts
  • packages/permissionless-groups/src/types.ts
  • packages/rpc/src/methods/pathfinder.ts
  • packages/types/src/pathfinding.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/balance-held-totals

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.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Caution

Review failed

An error occurred during the review process. Please try again later.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/balance-held-totals

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.

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