Skip to content

fix: u64 overflow in decimal scaling (compute_*_value_to_*) - #12

Open
mehvetero wants to merge 1 commit into
Bucket-Protocol:mainfrom
mehvetero:fix/decimal-scaling-overflow
Open

fix: u64 overflow in decimal scaling (compute_*_value_to_*)#12
mehvetero wants to merge 1 commit into
Bucket-Protocol:mainfrom
mehvetero:fix/decimal-scaling-overflow

Conversation

@mehvetero

Copy link
Copy Markdown

Summary

compute_collateral_value_to_buck and compute_buck_value_to_collateral (in both bucket.move and bottle.move) scale a value by pow(10, decimal_diff) using raw u64 multiplication. When collateral_decimal > buck_decimal (9), this can overflow and abort.

The upstream mul_factor call on the line above already promotes to u128 internally — the scaling multiplication immediately after it undoes that protection.

Affected locations

File Line Expression
protocol/sources/bucket.move 704 collateral_raw_value * pow(10, buck_decimal - collateral_decimal)
protocol/sources/bucket.move 715 buck_raw_value * pow(10, collateral_decimal - buck_decimal)
protocol/sources/bottle.move 161 buck_raw_value * pow(10, collateral_decimal - buck_decimal)

Impact

These functions are called by is_in_recovery_mode, is_healthy_bottle, is_liquidatable, handle_redeem, and record_repay_capped. An overflow aborts all of them — meaning for a collateral type where decimal_diff > 0 and amounts are large enough:

  • Borrow health checks fail (new borrows blocked)
  • Liquidation reverts (undercollateralized positions cannot be cleared)
  • Recovery mode detection reverts (protocol cannot enter recovery mode)
  • Redemption reverts

Currently deployed buckets use SUI (9 decimals = buck_decimal), so decimal_diff = 0 and pow(10, 0) = 1 — no overflow possible on existing collateral. The issue surfaces if a future create_bucket call registers a collateral type with more than 9 decimals.

Fix

Cast both operands to u128 before multiplying, then downcast to u64. This matches the pattern mul_factor already uses internally (framework/sources/math.move L7-11).

// before
collateral_raw_value * pow(10, constants::buck_decimal() - collateral_decimal)

// after
(((collateral_raw_value as u128) * (pow(10, constants::buck_decimal() - collateral_decimal) as u128)) as u64)

Context

Found while running move-test-gen --lint (MOV-002 rule) against this repo. The rule flags u64 multiplications without u128 promotion. After filtering test modules and known-safe patterns (Coin params, Witness params, etc.), these three sites remained.

Reviewed the OtterSec V1 audit (OS-BKT-ADV-00, "Improper Conversion") — that finding addressed missing decimal adjustment in record_repay_capped, which led to compute_buck_value_to_collateral being introduced as the fix. The overflow in the fixed version is a separate issue.

No funds at risk on current deployments (SUI-only buckets). This is a preventive fix for future collateral types.

compute_collateral_value_to_buck (bucket.move L704) and
compute_buck_value_to_collateral (bucket.move L715, bottle.move L161)
multiply a u64 result from mul_factor by pow(10, decimal_diff).

mul_factor internally promotes to u128 for safe arithmetic, but the
subsequent scaling multiplication is raw u64. When
collateral_decimal > buck_decimal (9) and the raw value is large,
this overflows and aborts — blocking TCR calculation, health checks,
liquidation, and redemption for that collateral type.

Fix: cast both operands to u128 before multiplying, then downcast.
Same pattern mul_factor already uses internally.
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