Skip to content
Draft
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
7 changes: 6 additions & 1 deletion core/src/exchanges/kalshi/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand Down
8 changes: 7 additions & 1 deletion core/src/exchanges/limitless/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
22 changes: 21 additions & 1 deletion core/src/exchanges/limitless/client.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<number> {
// 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));
}
}
32 changes: 11 additions & 21 deletions core/src/exchanges/limitless/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down Expand Up @@ -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),
Expand All @@ -233,30 +232,21 @@ export class LimitlessExchange extends PredictionMarketExchange {
}

async fetchBalance(): Promise<Balance[]> {
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;
}
}

Expand Down
7 changes: 5 additions & 2 deletions core/src/exchanges/limitless/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
8 changes: 7 additions & 1 deletion core/src/exchanges/polymarket/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
4 changes: 3 additions & 1 deletion core/src/exchanges/polymarket/fetchMarkets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ export async function fetchMarkets(params?: MarketFilterParams): Promise<Unified
} else if (params?.sort === 'liquidity') {
// queryParams.order = 'liquidity';
} else {
// Default: do not send order param to avoid 422
// Default to volume sort to ensure we get active markets
queryParams.order = 'volume';
queryParams.ascending = 'false';
}

try {
Expand Down
2 changes: 1 addition & 1 deletion sdks/typescript/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@
"unified"
],
"dependencies": {
"pmxt-core": "1.0.0-b4"
"pmxt-core": "1.2.0"
},
"devDependencies": {
"@types/jest": "^30.0.0",
Expand Down