Skip to content

Commit ba97f37

Browse files
committed
Fix critical bugs in leverage calculation
CRITICAL FIXES: 1. Simulation loop: Remove incorrect price multiplier (line 190) - Was: remainingUSDC = tokens * F * price - Now: remainingUSDC = tokens * F - Why: Protocol lends F per $1 of collateral (1 YES + 1 NO = $1), NOT per token price - Impact: Fixes 2.5x leverage underestimation at 0.40 price 2. Add runway validation (new method at line 397) - Prevents positions where debt > collateral value - Formula: runway = (1 - F) / (F * R) * year - Throws ValidationError if term >= 95% of runway 3. Clarify loop count calculation (line 438) - Was: Opaque formula - Now: Clear geometric series: F^N = 0.1 for 90% of max - Formula: N = log(0.1) / log(F), capped at 10 4. Add protocol mechanics documentation (line 10-30) - Explains collateral structure - Explains borrowing mechanics - Explains runway concept - Critical for integrator understanding Testing required: - Verify leverage calculation with F=0.9, price=0.40 - Verify runway validation with various R values - Integration test with real protocol
1 parent 2df82f8 commit ba97f37

File tree

1 file changed

+63
-19
lines changed

1 file changed

+63
-19
lines changed

src/sdk.ts

Lines changed: 63 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,30 @@ import { ethers, Wallet } from "ethers";
44
/**
55
* TURNKEY LEVERAGE SDK FOR POLYMARKET INTEGRATION
66
*
7-
* Enables integrators to add leverage to Polymarket trading with a simple API:
8-
* - User picks: "YES 40¢ → 44¢ in 1 hour with $1000"
9-
* - SDK executes full leverage loop
10-
* - Returns complete position with all fees calculated
7+
* Target-based UX: User picks "YES 40¢ → 44¢ in 1 hour with $1000"
8+
* SDK handles everything else.
119
*
12-
* Architecture:
13-
* 1. Calculate leverage parameters (F, loops, term)
14-
* 2. For each loop iteration:
15-
* - Buy tokens on Polymarket (FOK order)
16-
* - Wait for confirmation
17-
* - Open protocol leg
18-
* - Use borrowed USDC for next iteration
19-
* 3. Return position tracking all legs
10+
* PROTOCOL MECHANICS (Critical):
11+
*
12+
* 1. COLLATERAL = 1 YES + 1 NO = always $1 (CTF guarantee)
13+
* - Each "set" of collateral has guaranteed $1 value
14+
* - User deposits LONG tokens, protocol borrows SHORT from junior pool
15+
*
16+
* 2. BORROWING:
17+
* - Against $1 collateral, protocol lends F * $1 USDC
18+
* - F = capital efficiency (typically 0.85-0.95)
19+
* - Example: 1000 tokens → borrow 900 USDC (F=0.9)
20+
*
21+
* 3. LOOP LEVERAGE:
22+
* - Buy tokens with capital C
23+
* - Loop: Deposit → borrow C*F → buy more → deposit → borrow C*F² → ...
24+
* - Max leverage = 1 / (1 - F) [geometric series]
25+
*
26+
* 4. RUNWAY:
27+
* - Debt per set: F * (1 + R * time/year)
28+
* - RUNWAY = time until debt hits $1
29+
* - Formula: runway = (1 - F) / (F * R) * year
30+
* - Term MUST be < runway
2031
*/
2132

2233
// Protocol ABIs
@@ -170,12 +181,13 @@ export class ForecastLeverageSDK {
170181
let remainingUSDC = params.capitalUSDC;
171182

172183
for (let i = 0; i < leverageParams.loops; i++) {
173-
// Estimate tokens from current capital
184+
// Buy tokens at current price
174185
const tokensThisLoop = remainingUSDC / params.currentPrice;
175186
totalTokens += tokensThisLoop;
176187

177-
// Estimate borrowed USDC for next loop
178-
remainingUSDC = tokensThisLoop * leverageParams.F * params.currentPrice;
188+
// Protocol lends F per $1 of collateral (1 YES + 1 NO = $1)
189+
// CRITICAL: Each token pair is worth $1, NOT token price
190+
remainingUSDC = tokensThisLoop * leverageParams.F;
179191

180192
if (remainingUSDC < 1) break;
181193
}
@@ -376,6 +388,31 @@ export class ForecastLeverageSDK {
376388
}
377389
}
378390

391+
/**
392+
* Validate term doesn't exceed runway
393+
* Runway = time until debt reaches $1 per set
394+
* Debt formula: F * (1 + R * time/year)
395+
* Runway when debt = 1: (1 - F) / (F * R) * year
396+
*/
397+
private validateRunway(F: number, R: number, termSeconds: number): void {
398+
if (R === 0) return; // Zero rates = infinite runway
399+
400+
const YEAR_SECONDS = 365 * 24 * 3600;
401+
const runway = ((1 - F) / (F * R)) * YEAR_SECONDS;
402+
const safeRunway = runway * 0.95; // 95% safety margin
403+
404+
if (termSeconds >= safeRunway) {
405+
const termHours = termSeconds / 3600;
406+
const runwayHours = safeRunway / 3600;
407+
408+
throw new ValidationError(
409+
`Term ${termHours.toFixed(1)}h exceeds safe runway ${runwayHours.toFixed(1)}h. ` +
410+
`Debt would exceed collateral value. ` +
411+
`Try shorter timeframe or wait for lower rates.`
412+
);
413+
}
414+
}
415+
379416
/**
380417
* Calculate leverage parameters from target
381418
*/
@@ -391,10 +428,17 @@ export class ForecastLeverageSDK {
391428
const F = Number(quote.F) / 1e18;
392429
const R = (Number(quote.rS) + Number(quote.rJ)) / 1e18;
393430

394-
// Calculate loops needed based on capital and F
395-
// Geometric series: total_exposure = capital × 1/(1-F)
431+
// Validate term doesn't exceed runway
432+
this.validateRunway(F, R, params.timeframeSeconds);
433+
434+
// Calculate loops to reach ~90% of max leverage
435+
// Geometric series: (1 - F^N) / (1 - F) approaches 1 / (1 - F)
436+
// To reach 90%: F^N = 0.1, so N = log(0.1) / log(F)
396437
const maxLeverage = 1 / (1 - F);
397-
const loops = Math.floor(Math.log(1 - maxLeverage * (1 - F)) / Math.log(F)) + 1;
438+
const loops = Math.min(
439+
Math.ceil(Math.log(0.1) / Math.log(F)),
440+
10 // Safety cap
441+
);
398442

399443
// Get token ID from condition
400444
const tokenId = params.longYes
@@ -404,7 +448,7 @@ export class ForecastLeverageSDK {
404448
return {
405449
F,
406450
R,
407-
loops: Math.min(loops, 10), // Cap at 10 loops for safety
451+
loops, // Already capped above
408452
maxLeverage,
409453
tokenId,
410454
};

0 commit comments

Comments
 (0)