Skip to content

fix(index-dtf): bps-precise fee distribution during deployment - #1075

Open
lcamargof wants to merge 2 commits into
masterfrom
devin/1785878219-index-deploy-fee-bps-precision
Open

fix(index-dtf): bps-precise fee distribution during deployment#1075
lcamargof wants to merge 2 commits into
masterfrom
devin/1785878219-index-deploy-fee-bps-precision

Conversation

@lcamargof

@lcamargof lcamargof commented Aug 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

Deployment's Fees & Distribution table couldn't express precise shares, while governance proposals could. Two separate causes:

  1. The platform fee was read with integer division, so BSC's registry fee (333333333333333333 / 1e18 = 1/3) displayed and was used as 33% — with 33% reserved, the remaining shares could never total 100%:
-return Number((feeNumerator * 100n) / feeDenominator)   // 33
+return platformFeePercent(feeNumerator, feeDenominator) ?? fallback   // 33.33
  1. Deploy normalized shares into contract portions in floating point (parseEther(float.toString())), and its "Even distribution" floored to a base share and left the pot short.

Deploy and propose-dtf-settings now share bps-grid helpers in src/utils/fees.ts — every fee percentage lives on the same 0.01% grid the inputs accept, and the share→portion conversion is bigint-only:

// share of TOTAL revenue -> contract portion of the NON-platform pot, 18 decimals
revenuePortionFromShare(share, platformFee) =
  (bps(share) * 1e18) / (10_000n - bps(platformFee))

splitSharesEvenly(pot, n)   // last participant absorbs the remainder

So on BSC a 66.67% governance share is now exactly 1e18 (the whole pot) instead of drifting, and a 0.01% edit actually moves the encoded portion. Callers changed: usePlatformFee, calculateRevenueDistribution, dtfSettingsProposalDataAtom.calculateShare, both Even-distribution buttons, getPermissionlessDefaults, and the SDK fee written into indexDTFFeeAtom. Calldata shape, recipient sorting, the last-recipient correction to exactly 1e18, and governance's tokenJar routing are unchanged. The BSC fallback constant moves 33 → 33.33 to match the registry when it can't be read.

Engineer review requested on the fee math itself: the 1/3 → 33.33% quantization policy (round to the grid the inputs accept), and that both deploy and propose now go through the same helper.

Testing

  • 11 new unit tests: utils/tests/fees.test.ts, deploy revenue-distribution.test.ts (fractional fee gives the whole pot; hundredths stay distinct; 4 decimal shares total exactly 1e18), governance fee-recipients-atom.test.ts round-trip, permissionless defaults.
  • New e2e e2e/tests/general/deploy/index-dtf-revenue.spec.ts (strict mocks, desktop + mobile): a 1/3 registry fee renders 33.33 %, 0.01%-grid shares close the allocation, even distribution allocates the whole pot.
  • RED-verified: restoring the integer fee read renders 33 %; restoring the float even-split gives 33.33 where 33.34 is required.
  • Green: pnpm typecheck, pnpm lint, pnpm test:run (864), e2e helper units (72), pnpm e2e:smoke (59), governance-propose-dtf-settings 3/3, wiki-lint.

Link to Devin session: https://app.devin.ai/sessions/96884b34a06547fd834c57cdf9a88933
Requested by: @lcamargof

Summary by CodeRabbit

  • Bug Fixes

    • Improved platform-fee precision, including accurate 33.33% fee display and 66.67% governance shares.
    • Ensured revenue allocations total exactly 100%, including odd basis-point remainders.
    • Improved fractional fee handling across deployment and governance proposal flows.
  • Tests

    • Added coverage for fee precision, revenue distribution, mobile behavior, and deployment wizard allocations.

The deploy Fees & Distribution table truncated the DAO fee registry fee to an
integer (BSC's 1/3 became 33%) and normalized shares in floating point, so
precise shares could not be entered or summed. Deploy and propose-dtf-settings
now share bps helpers in utils/fees.

Co-Authored-By: luis.camargo@reserve.org <luis.camargo@reserve.org>
@lcamargof lcamargof self-assigned this Aug 4, 2026
@devin-ai-integration

Copy link
Copy Markdown
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI' or '@devin'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: c8fde634-ef8a-43e5-9b5e-51725efa0b82

📥 Commits

Reviewing files that changed from the base of the PR and between 0456cb5 and 803fa92.

📒 Files selected for processing (6)
  • src/utils/fees.ts
  • src/utils/tests/fees.test.ts
  • src/views/index-dtf/deploy/steps/revenue/revenue-distribution-settings.tsx
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-revenue-distribution.tsx
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-revenue-distribution.tsx
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts
  • src/utils/tests/fees.test.ts
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts
  • src/views/index-dtf/deploy/steps/revenue/revenue-distribution-settings.tsx

📝 Walkthrough

Walkthrough

Fee calculations now use hundredths-of-a-percent precision. Deployment and governance flows share conversion and even-split helpers. Tests cover fractional fees, exact allocations, remainder handling, and deployment UI behavior.

Changes

Fee precision and revenue allocation

Layer / File(s) Summary
Shared fee math and normalization
src/utils/fees.ts, src/utils/tests/fees.test.ts, src/hooks/use-platform-fee.ts, src/utils/constants.ts, src/views/index-dtf/index-dtf-container.tsx, src/views/index-dtf/deploy/permissionless-defaults.ts, src/views/index-dtf/deploy/tests/permissionless-defaults.test.ts, docs/wiki/progress.md
Fee percentages are quantized to hundredths of a percent. Revenue shares convert to 18-decimal contract portions. Even splits assign rounding remainders to the final participant. Platform fee sources and defaults use the shared precision rules.
Deployment revenue distribution
src/views/index-dtf/deploy/utils/index.ts, src/views/index-dtf/deploy/steps/revenue/revenue-distribution-settings.tsx, src/views/index-dtf/deploy/components/basic-input.tsx, src/views/index-dtf/deploy/components/deploy-accordion.tsx, src/views/index-dtf/deploy/utils/tests/revenue-distribution.test.ts, e2e/tests/general/deploy/index-dtf-revenue.spec.ts, e2e/TEST_MAP.md
The Index DTF deployment flow uses shared revenue conversion and even-split helpers. UI test selectors support allocation checks. Unit and end-to-end tests verify fractional fees and exact distribution.
Governance proposal distribution
src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts, src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-revenue-distribution.tsx, src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts, src/views/index-dtf/governance/CLAUDE.md
Proposal generation uses shared revenue conversion and even-split helpers. Tests verify fractional-fee round trips and exact whole-pot encoding. Governance guidance documents the precision rules.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant DeployWizard
  participant RevenueDistributionSettings
  participant FeeUtilities
  DeployWizard->>RevenueDistributionSettings: Open Fees & Distribution step
  RevenueDistributionSettings->>FeeUtilities: splitSharesEvenly(totalPercent, participants)
  RevenueDistributionSettings->>FeeUtilities: revenuePortionFromShare(sharePercent, platformFee)
  FeeUtilities-->>RevenueDistributionSettings: Quantized shares and contract portions
Loading
🚥 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 concisely describes the basis-point-precise fee distribution fix during Index DTF deployment.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ 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 devin/1785878219-index-deploy-fee-bps-precision

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.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Aug 4, 2026

Copy link
Copy Markdown

Deploying register-app with  Cloudflare Pages  Cloudflare Pages

Latest commit: 803fa92
Status: ✅  Deploy successful!
Preview URL: https://d4936d71.register-app.pages.dev
Branch Preview URL: https://devin-1785878219-index-deplo.register-app.pages.dev

View logs

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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 `@e2e/tests/general/deploy/index-dtf-revenue.spec.ts`:
- Around line 62-88: Extend the deploy route coverage around the existing fees
tests and run it for both desktop and `@mobile` projects. Add assertions for L0
blank, L1 skeleton, L2 independently resolving partial data, and L3 completed
content, including a check that no unexpected layout reflows occur; retain the
existing allocation behavior assertions.

In
`@src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts`:
- Around line 28-36: Update the test around makeStore and feeRecipientsAtom to
exercise dtfSettingsProposalDataAtom, configuring the store with a 33.33%
platform fee and recipient change. Decode the generated setFeeRecipients
calldata and assert that the 66.67% recipient encodes to a portion of exactly
parseEther('1'), rather than testing revenuePortionFromShare directly.
🪄 Autofix

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: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 3e31e992-1348-42fe-9e2a-d96c952e4782

📥 Commits

Reviewing files that changed from the base of the PR and between 76c0475 and 0456cb5.

📒 Files selected for processing (19)
  • docs/wiki/progress.md
  • e2e/TEST_MAP.md
  • e2e/tests/general/deploy/index-dtf-revenue.spec.ts
  • src/hooks/use-platform-fee.ts
  • src/utils/constants.ts
  • src/utils/fees.ts
  • src/utils/tests/fees.test.ts
  • src/views/index-dtf/deploy/components/basic-input.tsx
  • src/views/index-dtf/deploy/components/deploy-accordion.tsx
  • src/views/index-dtf/deploy/permissionless-defaults.ts
  • src/views/index-dtf/deploy/steps/revenue/revenue-distribution-settings.tsx
  • src/views/index-dtf/deploy/tests/permissionless-defaults.test.ts
  • src/views/index-dtf/deploy/utils/index.ts
  • src/views/index-dtf/deploy/utils/tests/revenue-distribution.test.ts
  • src/views/index-dtf/governance/CLAUDE.md
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/atoms.ts
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/components/sections/propose-revenue-distribution.tsx
  • src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts
  • src/views/index-dtf/index-dtf-container.tsx

Comment on lines +62 to +88
test('deploy fees: hundredths-of-a-percent shares close the allocation @smoke @mobile', async ({
harness,
}) => {
const page = await openFeesStep(harness)

await page.getByTestId('deploy-share-deployerShare').fill('0.01')
await page.getByTestId('deploy-share-governanceShare').fill('66.66')

await expect(page.getByTestId('deploy-remaining-allocation')).toHaveText('0%')
})

test('deploy fees: even distribution allocates the whole non-platform pot', async ({
harness,
}) => {
const page = await openFeesStep(harness)

await page.getByTestId('deploy-even-distribution').click()

// 66.67% over two participants — the last one absorbs the odd basis point.
await expect(page.getByTestId('deploy-share-deployerShare')).toHaveValue(
'33.33'
)
await expect(page.getByTestId('deploy-share-governanceShare')).toHaveValue(
'33.34'
)
await expect(page.getByTestId('deploy-remaining-allocation')).toHaveText('0%')
})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Add lifecycle assertions for the deploy route.

This spec only asserts loaded-state behavior. It does not cover L0 blank, L1 skeleton, L2 partial resolution, L3 completion, or unexpected reflows. Add these assertions for desktop and the @mobile project before treating this as route coverage. As per coding guidelines, “Test every page through loading phases L0 blank, L1 skeleton, L2 independently resolving partial data, and L3 fully loaded behavior; assert zero unexpected reflows.”

🤖 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 `@e2e/tests/general/deploy/index-dtf-revenue.spec.ts` around lines 62 - 88,
Extend the deploy route coverage around the existing fees tests and run it for
both desktop and `@mobile` projects. Add assertions for L0 blank, L1 skeleton, L2
independently resolving partial data, and L3 completed content, including a
check that no unexpected layout reflows occur; retain the existing allocation
behavior assertions.

Source: Coding guidelines

Comment on lines +28 to +36
it('round-trips a whole-pot share under a fractional platform fee', () => {
// BSC takes 1/3 of revenue: a contract 100% recipient owns 66.67% of total,
// and that share must encode back to the whole pot (1e18), never above it.
const result = makeStore(33.33, '100').get(feeRecipientsAtom)

expect(result?.governanceShare).toBe(66.67)
expect(revenuePortionFromShare(result!.governanceShare, 33.33)).toBe(
parseEther('1')
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Exercise the proposal-calldata seam.

This test reads feeRecipientsAtom and then calls revenuePortionFromShare directly. It does not execute the changed dtfSettingsProposalDataAtom path that builds setFeeRecipients calldata.

Set a fractional platform fee and recipient changes in the store. Then decode the generated calldata and assert that the recipient portion is exactly parseEther('1') for the 66.67% share with a 33.33% platform fee.

As per coding guidelines, “Tests for bug fixes and non-trivial behavior changes must exercise the real test seam rather than replacing it with a mock.”

🤖 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
`@src/views/index-dtf/governance/views/propose/views/propose-dtf-settings/tests/fee-recipients-atom.test.ts`
around lines 28 - 36, Update the test around makeStore and feeRecipientsAtom to
exercise dtfSettingsProposalDataAtom, configuring the store with a 33.33%
platform fee and recipient change. Decode the generated setFeeRecipients
calldata and assert that the 66.67% recipient encodes to a portion of exactly
parseEther('1'), rather than testing revenuePortionFromShare directly.

Source: Coding guidelines

…nd absorb read-back rounding drift

Writing the whole additionalRevenueRecipients array left the per-index share
inputs rendering their old values, and rounding each contract portion to the
0.01% grid independently could load an untouched propose form as
over-allocated.

Co-Authored-By: luis.camargo@reserve.org <luis.camargo@reserve.org>
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