Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions core/src/exchanges/limitless/client.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { HttpClient, OrderClient, OrderBuilder, OrderSigner, MarketFetcher, Side, OrderType } from '@limitless-exchange/sdk';
import { Wallet, providers, Contract, utils } from 'ethers';
import { Wallet, providers, Contract } from 'ethers';
import { LIMITLESS_RPC_URL } from './config';
import { scaledIntegerToNumber } from './utils';

const DEFAULT_LIMITLESS_API_URL = process.env.LIMITLESS_BASE_URL || 'https://api.limitless.exchange';

Expand Down Expand Up @@ -397,6 +398,6 @@ export class LimitlessClient {
const balance = await contract.balanceOf(this.signer.address);
const decimals = await contract.decimals(); // Should be 6

return parseFloat(utils.formatUnits(balance, decimals));
return scaledIntegerToNumber(balance, Number(decimals));
}
}
4 changes: 2 additions & 2 deletions core/src/exchanges/limitless/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { LIMITLESS_RPC_URL } from './config';
import { limitlessErrorMapper } from './errors';
import { LimitlessFetcher } from './fetcher';
import { LimitlessNormalizer } from './normalizer';
import { DEFAULT_LIMITLESS_API_URL } from './utils';
import { DEFAULT_LIMITLESS_API_URL, scaledIntegerToNumber } from './utils';
import { LimitlessWebSocket, LimitlessWebSocketConfig } from './websocket';
import { logger } from '../../utils/logger';

Expand Down Expand Up @@ -582,7 +582,7 @@ export class LimitlessExchange extends PredictionMarketExchange {
);
const rawBalance = await usdcContract.balanceOf(targetAddress);
const USDC_DECIMALS = 6;
const total = parseFloat(rawBalance.toString()) / Math.pow(10, USDC_DECIMALS);
const total = scaledIntegerToNumber(rawBalance, USDC_DECIMALS);

return [{
currency: 'USDC',
Expand Down
20 changes: 20 additions & 0 deletions core/src/exchanges/limitless/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,26 @@ import { addBinaryOutcomes } from '../../utils/market-utils';

export const DEFAULT_LIMITLESS_API_URL = 'https://api.limitless.exchange';

export function scaledIntegerToNumber(value: bigint | { toBigInt?: () => bigint; toString(): string }, decimals: number): number {
if (!Number.isInteger(decimals) || decimals < 0) {
throw new Error(`[limitless] Invalid token decimals: ${decimals}`);
}

const raw = typeof value === 'bigint'
? value
: typeof value.toBigInt === 'function'
? value.toBigInt()
: BigInt(value.toString());
const sign = raw < 0n ? -1 : 1;
const abs = raw < 0n ? -raw : raw;
const scale = 10n ** BigInt(decimals);
const whole = abs / scale;
const fraction = abs % scale;
const amount = Number(whole) + (Number(fraction) / Number(scale));

return sign * amount;
}

export interface LimitlessMarketContext {
eventId?: string;
eventTitle?: string;
Expand Down
18 changes: 18 additions & 0 deletions core/test/unit/limitless-balance.core.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { BigNumber } from 'ethers';
import { scaledIntegerToNumber } from '../../src/exchanges/limitless/utils';

describe('Limitless balance conversion', () => {
test('scales raw integer balances before converting to number', () => {
const raw = 9007199254740993n;
const legacy = parseFloat(raw.toString()) / 1_000_000;

expect(legacy).toBe(9007199254.740992);
expect(scaledIntegerToNumber(raw, 6)).toBe(9007199254.740993);
});

test('accepts ethers BigNumber balances without formatUnits parsing', () => {
const raw = BigNumber.from('1234567890123');

expect(scaledIntegerToNumber(raw, 6)).toBe(1234567.890123);
});
});