diff --git a/core/src/exchanges/kalshi/auth.ts b/core/src/exchanges/kalshi/auth.ts index df1c483..86e94f4 100644 --- a/core/src/exchanges/kalshi/auth.ts +++ b/core/src/exchanges/kalshi/auth.ts @@ -56,7 +56,12 @@ export class KalshiAuth { // Allow input of private key in both raw string or PEM format // If it's a raw key without headers, accessing it might be tricky with implicit types, // but standard PEM is best. We assume the user provides a valid PEM. - const privateKey = this.credentials.privateKey!; + let privateKey = this.credentials.privateKey!; + + // Fix for common .env issue where newlines are escaped + if (privateKey.includes('\\n')) { + privateKey = privateKey.replace(/\\n/g, '\n'); + } // Kalshi uses RSA-PSS for signing const signature = signer.sign({ diff --git a/core/src/exchanges/limitless/auth.ts b/core/src/exchanges/limitless/auth.ts index 1ab74c0..2348f10 100644 --- a/core/src/exchanges/limitless/auth.ts +++ b/core/src/exchanges/limitless/auth.ts @@ -24,7 +24,13 @@ export class LimitlessAuth { } // Initialize the signer - this.signer = new Wallet(credentials.privateKey); + let privateKey = credentials.privateKey; + // Fix for common .env issue where newlines are escaped + if (privateKey.includes('\\n')) { + privateKey = privateKey.replace(/\\n/g, '\n'); + } + + this.signer = new Wallet(privateKey); } /** diff --git a/core/src/exchanges/limitless/client.ts b/core/src/exchanges/limitless/client.ts index 2422821..96759ae 100644 --- a/core/src/exchanges/limitless/client.ts +++ b/core/src/exchanges/limitless/client.ts @@ -1,6 +1,6 @@ import axios, { AxiosInstance } from 'axios'; -import { Wallet, utils } from 'ethers'; +import { Wallet, utils, providers, Contract } from 'ethers'; import { TypedDataDomain, TypedDataField } from '@ethersproject/abstract-signer'; const LIMITLESS_API_URL = 'https://api.limitless.exchange'; @@ -42,6 +42,11 @@ export class LimitlessClient { private userData: any; constructor(privateKey: string) { + // Fix for common .env issue where newlines are escaped + if (privateKey.includes('\\n')) { + privateKey = privateKey.replace(/\\n/g, '\n'); + } + this.signer = new Wallet(privateKey); this.api = axios.create({ baseURL: LIMITLESS_API_URL, @@ -210,4 +215,19 @@ export class LimitlessClient { const res = await this.api.get(`/markets/${marketSlug}/user-orders`, { params }); return res.data.orders || []; } + + async getBalance(): Promise { + // USDC on Base + const USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; + const ABI = ["function balanceOf(address) view returns (uint256)", "function decimals() view returns (uint8)"]; + + // Use a public RPC for Base + const provider = new providers.JsonRpcProvider('https://mainnet.base.org'); + const contract = new Contract(USDC_ADDRESS, ABI, provider); + + const balance = await contract.balanceOf(this.signer.address); + const decimals = await contract.decimals(); // Should be 6 + + return parseFloat(utils.formatUnits(balance, decimals)); + } } diff --git a/core/src/exchanges/limitless/index.ts b/core/src/exchanges/limitless/index.ts index c886713..700d815 100644 --- a/core/src/exchanges/limitless/index.ts +++ b/core/src/exchanges/limitless/index.ts @@ -122,15 +122,14 @@ export class LimitlessExchange extends PredictionMarketExchange { // See utils.ts mapMarketToUnified: id = market.slug const marketSlug = params.marketId; - if (!params.price) { - throw new Error("Limit orders require a price"); - } + // Limitless (USDC on Base) supports 6 decimals max. + const price = Math.round(params.price * 1_000_000) / 1_000_000; const response = await client.createOrder({ marketSlug: marketSlug, outcomeId: params.outcomeId, side: side, - price: params.price, + price: price, amount: params.amount, type: params.type }); @@ -210,7 +209,7 @@ export class LimitlessExchange extends PredictionMarketExchange { return orders.map((o: any) => ({ id: o.id, marketId: marketId, - outcomeId: "unknown", // API might not return this in the simplified list, need to check response + outcomeId: o.tokenId || "unknown", side: o.side.toLowerCase() as 'buy' | 'sell', type: 'limit', price: parseFloat(o.price), @@ -233,30 +232,21 @@ export class LimitlessExchange extends PredictionMarketExchange { } async fetchBalance(): Promise { - const auth = this.ensureAuth(); - const client = await auth.getClobClient(); + // Use LimitlessClient (Base chain) instead of ClobClient (Polygon) + const client = this.ensureClient(); try { - // 1. Fetch raw collateral balance (USDC) - // Limitless relies strictly on USDC (Polygon) which has 6 decimals. - // Note: This needs to be updated for Base chain! - const USDC_DECIMALS = 6; - const balRes = await client.getBalanceAllowance({ - asset_type: "COLLATERAL" as any - }); - const rawBalance = parseFloat(balRes.balance); - const total = rawBalance / Math.pow(10, USDC_DECIMALS); + const total = await client.getBalance(); return [{ currency: 'USDC', total: total, - available: total, // Approximate - locked: 0 + available: total, // Funds are in wallet, effectively available until filled + locked: 0 // Basic implementation: does not subtract open order value }]; } catch (error: any) { - // Fallback to 0 if fails, to avoid breaking everything - console.warn("fetchBalance failed via CLOB client", error.message); - return [{ currency: 'USDC', total: 0, available: 0, locked: 0 }]; + console.warn("fetchBalance failed:", error.message); + throw error; } } diff --git a/core/src/exchanges/limitless/utils.ts b/core/src/exchanges/limitless/utils.ts index 9ffab79..de371ee 100644 --- a/core/src/exchanges/limitless/utils.ts +++ b/core/src/exchanges/limitless/utils.ts @@ -11,10 +11,13 @@ export function mapMarketToUnified(market: any): UnifiedMarket | null { // The new API provides 'tokens' and 'prices' // tokens: { no: "...", yes: "..." } // prices: [noPrice, yesPrice] - if (market.tokens && market.prices) { + if (market.tokens) { const tokenEntries = Object.entries(market.tokens); + // Ensure prices array exists, otherwise default to empty + const prices = Array.isArray(market.prices) ? market.prices : []; + tokenEntries.forEach(([label, tokenId], index) => { - const outcomePrice = market.prices[index] || 0; + const outcomePrice = prices[index] || 0; outcomes.push({ id: tokenId as string, diff --git a/core/src/exchanges/polymarket/auth.ts b/core/src/exchanges/polymarket/auth.ts index 7103462..779b780 100644 --- a/core/src/exchanges/polymarket/auth.ts +++ b/core/src/exchanges/polymarket/auth.ts @@ -27,7 +27,13 @@ export class PolymarketAuth { } // Initialize the signer - this.signer = new Wallet(credentials.privateKey); + let privateKey = credentials.privateKey; + // Fix for common .env issue where newlines are escaped + if (privateKey.includes('\\n')) { + privateKey = privateKey.replace(/\\n/g, '\n'); + } + + this.signer = new Wallet(privateKey); } /** diff --git a/core/src/exchanges/polymarket/fetchMarkets.ts b/core/src/exchanges/polymarket/fetchMarkets.ts index 488da94..add9a51 100644 --- a/core/src/exchanges/polymarket/fetchMarkets.ts +++ b/core/src/exchanges/polymarket/fetchMarkets.ts @@ -25,7 +25,9 @@ export async function fetchMarkets(params?: MarketFilterParams): Promise