Summary
computeMaxLeverage (src/math/trading.ts line 269) computes Number(10000n / initialMarginBps). The division is performed in BigInt, which floors the result, and then converted to Number. For non-round margin values the fractional leverage is silently discarded.
Example
computeMaxLeverage(3000n)
// BigInt path: Number(10000n / 3000n) = Number(3n) = 3
// Correct: 10000 / 3000 = 3.333...
// Error: ~10% understatement of maximum leverage
For initialMarginBps = 1500n (15% margin = 6.67x), the function returns 6 instead of 6.67.
Impact
HIGH. The UI trade form uses computeMaxLeverage to set the slider maximum and to validate user leverage input. Understating max leverage means:
- Users cannot open positions up to the true on-chain limit, losing potential notional.
- A trader who is shown "max 6x" but is actually allowed 6.67x on-chain has an inaccurate picture of their risk capacity.
Fix
Use floating-point division: 10000 / Number(initialMarginBps).
initialMarginBps is always small (on-chain it is a u16 capped at 10_000), so Number() conversion is exact and precision is not a concern.
See the linked PR.
Severity
HIGH - incorrect max leverage calculation affects all non-round margin tiers
Summary
computeMaxLeverage(src/math/trading.tsline 269) computesNumber(10000n / initialMarginBps). The division is performed in BigInt, which floors the result, and then converted to Number. For non-round margin values the fractional leverage is silently discarded.Example
For
initialMarginBps = 1500n(15% margin = 6.67x), the function returns6instead of6.67.Impact
HIGH. The UI trade form uses
computeMaxLeverageto set the slider maximum and to validate user leverage input. Understating max leverage means:Fix
Use floating-point division:
10000 / Number(initialMarginBps).initialMarginBpsis always small (on-chain it is a u16 capped at 10_000), soNumber()conversion is exact and precision is not a concern.See the linked PR.
Severity
HIGH - incorrect max leverage calculation affects all non-round margin tiers