Risk Level
HIGH
Location
core/src/exchanges/gemini-titan/index.ts:134
Code
const payload: Record<string, unknown> = {
symbol: instrumentSymbol,
orderType: 'limit',
side: params.side,
quantity: String(params.amount),
price: params.price !== undefined ? params.price.toFixed(2) : '0.50', // ← line 134
outcome: side,
timeInForce: params.type === 'market' ? 'immediate-or-cancel' : 'good-til-cancel',
};
Problem
.toFixed(2) on a JavaScript number does not guarantee correct decimal rounding. .toFixed() is implemented using float arithmetic internally and will produce wrong results for any price that is not exactly representable in IEEE 754. The resulting string is sent directly in the order payload to the Gemini Titan API as the limit price.
The classic example: (1.005).toFixed(2) returns "1.00" in V8 (not "1.01") because 1.005 is stored as 1.00499999... in IEEE 754. Any prediction market price with the digit 5 in the third decimal place is at risk.
Example Failure
// Prices that round incorrectly with .toFixed(2):
(0.005).toFixed(2) = "0.00" // should be "0.01" — order at wrong cent
(0.015).toFixed(2) = "0.01" // should be "0.02"
(0.025).toFixed(2) = "0.02" // should be "0.03"
(0.575).toFixed(2) = "0.57" // should be "0.58" — 1 cent off in a 57¢ market
(0.995).toFixed(2) = "0.99" // should be "1.00"
// The pattern: any x.xx5 price is at risk
// Gemini Titan tick size is 0.01, so 2dp is the correct scale,
// but the rounding step itself is wrong for ~half of all .xx5 prices
A 1-cent error in a limit price means the order sits on the wrong side of a spread or matches at the wrong level.
Suggested Fix
import Decimal from 'decimal.js';
price: params.price !== undefined
? new Decimal(params.price).toDecimalPlaces(2, Decimal.ROUND_HALF_UP).toFixed(2)
: '0.50',
Found by automated float safety audit
Risk Level
HIGH
Location
core/src/exchanges/gemini-titan/index.ts:134Code
Problem
.toFixed(2)on a JavaScriptnumberdoes not guarantee correct decimal rounding..toFixed()is implemented using float arithmetic internally and will produce wrong results for any price that is not exactly representable in IEEE 754. The resulting string is sent directly in the order payload to the Gemini Titan API as the limit price.The classic example:
(1.005).toFixed(2)returns"1.00"in V8 (not"1.01") because1.005is stored as1.00499999...in IEEE 754. Any prediction market price with the digit5in the third decimal place is at risk.Example Failure
A 1-cent error in a limit price means the order sits on the wrong side of a spread or matches at the wrong level.
Suggested Fix
Found by automated float safety audit