Skip to content

refactor(loyalty): compute via std::u128::mul_div; drop unused openzeppelin_math#33

Open
bidzyyys wants to merge 3 commits into
mainfrom
feat/loyalty-mul-div
Open

refactor(loyalty): compute via std::u128::mul_div; drop unused openzeppelin_math#33
bidzyyys wants to merge 3 commits into
mainfrom
feat/loyalty-mul-div

Conversation

@bidzyyys

@bidzyyys bidzyyys commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Draft. Supersedes #32 (the UD30x9 approach): same goal — tidy the loyalty math — but without adopting fixed-point-math, so no Sui-framework/CLI bump.

What & why

compute_loyalty hand-rolled (amount * coefficient) / denom in u128 (its own comments flagged the intermediate as near-overflow), and the @openzeppelin-move/integer-math dependency was declared but never used.

  • compute_loyalty now uses std::u128::mul_div, which upcasts the product to u256 internally (no intermediate overflow) and rounds down — same result, no external math dependency. The result is clamped to max_loyalty_per_payment and only cast to u64 once <= max.
  • Dependency: the unused openzeppelin_math is removed from Move.toml.

std::u64::mul_div can't be used here: the divisor LOYALTY_FLOAT_SCALING * 10^payment_decimals reaches 1e27 at the 18-decimal cap — well over u64::MAX.

Scope

  • No public API change (coefficient stays a scaled u64), so no frontend change.
  • No toolchain change — stays on the repo's pinned Sui 1.73.2 (no new framework pulled).

Tests

sui move test --build-env testnet: 97/97. Formatter + lint clean.

Summary by CodeRabbit

  • Bug Fixes
    • Improved loyalty point calculations to be more consistent and accurate when converting payment amounts.
    • Loyalty rewards still respect the maximum per payment cap.
  • Chores
    • Updated the build environment to use a newer Sui CLI version.
    • Removed an unused payment contract dependency.

…ppelin_math

compute_loyalty used a hand-rolled `(amount * coefficient) / denom` in u128
(the comments flagged the intermediate as near-overflow), and the
`@openzeppelin-move/integer-math` dependency was declared but never used.

Replace the arithmetic with `std::u128::mul_div`, which upcasts to u256
internally (no intermediate overflow) and rounds down - same result, and no
external math dependency. u64::mul_div is unusable here: the divisor
LOYALTY_FLOAT_SCALING * 10^payment_decimals reaches 1e27 at the 18-decimal
cap, well over u64::MAX. The now-unused openzeppelin_math dep is removed.

No public API or frontend change (coefficient stays a scaled u64). Tests: 97/97
(sui move test --build-env testnet).
@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

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

Run ID: cd324f12-7ee2-4972-a2cc-2a37340139a9

📥 Commits

Reviewing files that changed from the base of the PR and between a81cb25 and 92b7681.

⛔ Files ignored due to path filters (1)
  • contracts/payments/Move.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • .github/workflows/move.yml
  • contracts/payments/Move.toml
  • contracts/payments/sources/config.move
💤 Files with no reviewable changes (1)
  • contracts/payments/Move.toml

Walkthrough

This PR bumps the SUI CLI version used in CI from 1.73.2 to 1.74.1, removes the openzeppelin_math dependency from the payments contract's Move.toml, and refactors compute_loyalty in config.move to compute a denominator and use u128::mul_div instead of manual multiplication/division.

Changes

CI and Loyalty Calculation Updates

Layer / File(s) Summary
CI workflow version bump
.github/workflows/move.yml
SUI_CLI_VERSION environment variable bumped from 1.73.2 to 1.74.1.
Loyalty calculation refactor
contracts/payments/sources/config.move, contracts/payments/Move.toml
compute_loyalty now computes denom from LOYALTY_FLOAT_SCALING * 10^payment_decimals and uses u128::mul_div for the calculation, clamping to max_loyalty_per_payment; the unused openzeppelin_math dependency is removed from Move.toml.

Estimated code review effort: 2 (Simple) | ~10 minutes

Poem

A version hop, a math rewrite so neat,
mul_div now handles the loyalty feat.
One less dependency to carry along,
Clamped and correct, nothing gone wrong.
🐇✨ hop, compute, done!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main changes: loyalty math refactor plus removal of the unused math dependency.
Description check ✅ Passed The description covers the changes, rationale, scope, and tests, but it omits the issue reference and PR checklist items from the template.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/loyalty-mul-div

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

bidzyyys added 2 commits July 3, 2026 15:20
The resolved Sui framework requires a newer verifier than 1.73.2
(sui::rangeproofs UNEXPECTED_VERIFIER_ERROR under the old CLI).
Use `value.min(max)` instead of an explicit if/else; the cast to u64 is safe
because the value is <= max <= u64::MAX after clamping.
@bidzyyys bidzyyys marked this pull request as ready for review July 3, 2026 13:30
@bidzyyys bidzyyys requested a review from qalisander as a code owner July 3, 2026 13:30
Comment on lines +193 to 194
let value = (payment_amount as u128).mul_div(self.loyalty_coefficient as u128, denom);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

In the previous approach I was avoiding u256 multiplication (it’s usually 4x time more expensive than u128 multiplication). But tbh I don't now how that difference works on sui and what performance benefit will be in our smart contract.. Probably minuscule. May be ask auditing team?

manifest_digest = "5745706258F61D6CE210904B3E6AE87A73CE9D31A6F93BE4718C442529332A87"
deps = { std = "MoveStdlib", sui = "Sui" }

[pinned.testnet.openzeppelin_math]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thnx!

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.

2 participants