@dimes-dot-fi/sdk 1.5.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +88 -19
- package/dist/{aliases-ByZ2DcA5.d.cts → aliases-BJyM8ydu.d.cts} +54 -4
- package/dist/{aliases-ByZ2DcA5.d.ts → aliases-BJyM8ydu.d.ts} +54 -4
- package/dist/{chunk-UHQZSMUD.cjs → chunk-53U53KZ2.cjs} +5 -1
- package/dist/chunk-53U53KZ2.cjs.map +1 -0
- package/dist/chunk-5VHDXPRV.mjs +240 -0
- package/dist/chunk-5VHDXPRV.mjs.map +1 -0
- package/dist/{chunk-Y5WQ3UWF.cjs → chunk-7SAMK7CS.cjs} +18 -12
- package/dist/chunk-7SAMK7CS.cjs.map +1 -0
- package/dist/chunk-FAW2C5AM.cjs +240 -0
- package/dist/chunk-FAW2C5AM.cjs.map +1 -0
- package/dist/{chunk-4MO3HKMS.mjs → chunk-PK2PRTQW.mjs} +5 -1
- package/dist/chunk-PK2PRTQW.mjs.map +1 -0
- package/dist/{chunk-3OPRSKCY.mjs → chunk-T67Z74J3.mjs} +17 -11
- package/dist/chunk-T67Z74J3.mjs.map +1 -0
- package/dist/contract/index.cjs +5522 -293
- package/dist/contract/index.cjs.map +1 -1
- package/dist/contract/index.d.cts +68 -10
- package/dist/contract/index.d.ts +68 -10
- package/dist/contract/index.mjs +5523 -294
- package/dist/contract/index.mjs.map +1 -1
- package/dist/{dimes-client-B-thu7VQ.d.cts → dimes-client-DYFvnZUx.d.ts} +41 -9
- package/dist/{dimes-client-CerC9mTr.d.ts → dimes-client-DZPhU1CG.d.cts} +41 -9
- package/dist/{dimes-error-BY5qVGZC.d.cts → dimes-error-BlQWPqhQ.d.cts} +2 -2
- package/dist/{dimes-error-CrvPuFzB.d.ts → dimes-error-DBKV5bfr.d.ts} +2 -2
- package/dist/index.cjs +78 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +23 -14
- package/dist/index.d.ts +23 -14
- package/dist/index.mjs +78 -8
- package/dist/index.mjs.map +1 -1
- package/dist/quote-BFNgVsY-.d.ts +39 -0
- package/dist/quote-CanwfT9m.d.cts +39 -0
- package/dist/react/index.cjs +414 -16
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +165 -8
- package/dist/react/index.d.ts +165 -8
- package/dist/react/index.mjs +413 -15
- package/dist/react/index.mjs.map +1 -1
- package/dist/{types-2bGyrtqd.d.cts → types-ClHQbIcY.d.ts} +3 -3
- package/dist/{types-CAuPxEwO.d.ts → types-Co-2bQSi.d.cts} +3 -3
- package/dist/ws/index.cjs +4 -230
- package/dist/ws/index.cjs.map +1 -1
- package/dist/ws/index.d.cts +3 -3
- package/dist/ws/index.d.ts +3 -3
- package/dist/ws/index.mjs +5 -231
- package/dist/ws/index.mjs.map +1 -1
- package/package.json +43 -4
- package/dist/chunk-3OPRSKCY.mjs.map +0 -1
- package/dist/chunk-4MO3HKMS.mjs.map +0 -1
- package/dist/chunk-UHQZSMUD.cjs.map +0 -1
- package/dist/chunk-Y5WQ3UWF.cjs.map +0 -1
- package/dist/quote-Czq3Selh.d.ts +0 -30
- package/dist/quote-quIOosIv.d.cts +0 -30
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as Market, C as
|
|
1
|
+
import { M as Market, C as CreateQuoteParams, Q as Quote, P as Position, c as PositionTransactions, d as ContractInfo, e as CustomerLimit, F as FeeRates, f as FeeReportParams, g as FeeReport } from './aliases-BJyM8ydu.js';
|
|
2
2
|
|
|
3
3
|
declare const marketMovedCodes: Set<string>;
|
|
4
4
|
type QuoteHint = {
|
|
@@ -68,11 +68,15 @@ declare function hintAdjustment(hint: QuoteHint, current: {
|
|
|
68
68
|
interface Paginated<T> {
|
|
69
69
|
data: T[];
|
|
70
70
|
hasMore: boolean;
|
|
71
|
+
/** Total matching records. Present only when the request includes `expand: ["total_count"]`. */
|
|
72
|
+
totalCount?: number;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
interface AuthProvider {
|
|
74
76
|
getHeaders(): Promise<Record<string, string>>;
|
|
75
77
|
refresh?(): Promise<void>;
|
|
78
|
+
/** Synchronously return the current cached JWT, or null if not yet obtained. Used by the WebSocket clients. */
|
|
79
|
+
getToken?(): string | null;
|
|
76
80
|
}
|
|
77
81
|
interface ApiKeyAuthOptions {
|
|
78
82
|
apiKey: string;
|
|
@@ -90,6 +94,7 @@ declare class ApiKeyAuth implements AuthProvider {
|
|
|
90
94
|
private readonly fetchFn;
|
|
91
95
|
constructor(options: ApiKeyAuthOptions);
|
|
92
96
|
getHeaders(): Promise<Record<string, string>>;
|
|
97
|
+
getToken(): string | null;
|
|
93
98
|
refresh(): Promise<void>;
|
|
94
99
|
private fetchToken;
|
|
95
100
|
}
|
|
@@ -105,6 +110,7 @@ declare class JwtAuth implements AuthProvider {
|
|
|
105
110
|
private refreshPromise;
|
|
106
111
|
constructor(options: JwtAuthOptions);
|
|
107
112
|
getHeaders(): Promise<Record<string, string>>;
|
|
113
|
+
getToken(): string | null;
|
|
108
114
|
refresh(): Promise<void>;
|
|
109
115
|
private fetchToken;
|
|
110
116
|
}
|
|
@@ -117,37 +123,63 @@ interface DimesClientOptions {
|
|
|
117
123
|
maxRetryDelayMs?: number;
|
|
118
124
|
}
|
|
119
125
|
type MarketSort = "ticker_asc" | "depth_desc" | "discovered_at_desc";
|
|
126
|
+
type MarketExpand = "total_count" | "prices";
|
|
127
|
+
type PositionStatus = "open" | "unwinding" | "pending" | "closing" | "closed" | "settled" | "liquidated";
|
|
120
128
|
interface GetMarketsParams {
|
|
121
|
-
cursor?: string;
|
|
122
129
|
limit?: number;
|
|
130
|
+
startingAfter?: string;
|
|
131
|
+
endingBefore?: string;
|
|
132
|
+
expand?: MarketExpand[];
|
|
133
|
+
sort?: MarketSort;
|
|
134
|
+
acceptingNewPositions?: boolean;
|
|
123
135
|
category?: string;
|
|
124
136
|
status?: string;
|
|
125
137
|
provider?: "polymarket" | "kalshi";
|
|
126
|
-
|
|
138
|
+
}
|
|
139
|
+
interface SearchMarketsParams extends GetMarketsParams {
|
|
140
|
+
query: string;
|
|
127
141
|
}
|
|
128
142
|
interface GetPositionsParams {
|
|
129
|
-
cursor?: string;
|
|
130
143
|
limit?: number;
|
|
131
|
-
|
|
144
|
+
startingAfter?: string;
|
|
145
|
+
endingBefore?: string;
|
|
146
|
+
expand?: string[];
|
|
147
|
+
sortBy?: "created_at" | "closed_at";
|
|
148
|
+
sortDirection?: "asc" | "desc";
|
|
149
|
+
status?: PositionStatus;
|
|
150
|
+
state?: "active" | "inactive";
|
|
132
151
|
}
|
|
133
152
|
interface GetFeeRatesParams {
|
|
134
153
|
ticker?: string;
|
|
135
154
|
}
|
|
136
155
|
declare class DimesClient {
|
|
156
|
+
/** The resolved API base URL. Useful for constructing WebSocket clients against the same host. */
|
|
157
|
+
readonly baseUrl: string;
|
|
137
158
|
private readonly http;
|
|
159
|
+
private readonly auth;
|
|
138
160
|
constructor(options: DimesClientOptions);
|
|
161
|
+
/** Synchronously return the current cached JWT, or null. Returns null if the auth provider does not expose one. */
|
|
162
|
+
getToken(): string | null;
|
|
139
163
|
getMarkets(params?: GetMarketsParams): Promise<Paginated<Market>>;
|
|
140
164
|
getMarket(ticker: string): Promise<Market>;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
searchMarkets(params: SearchMarketsParams): Promise<Paginated<Market>>;
|
|
166
|
+
createDraftQuote(params: CreateQuoteParams): Promise<Quote>;
|
|
167
|
+
promoteDraftQuote(draftId: string): Promise<Quote>;
|
|
168
|
+
createQuote(params: CreateQuoteParams): Promise<Quote>;
|
|
144
169
|
getPositions(params?: GetPositionsParams): Promise<Position[]>;
|
|
145
170
|
getPositionTransactions(positionId: string): Promise<PositionTransactions>;
|
|
146
171
|
cancelPosition(positionId: string): Promise<void>;
|
|
147
172
|
getContractInfo(): Promise<ContractInfo>;
|
|
173
|
+
getUserLimits(): Promise<CustomerLimit>;
|
|
174
|
+
getPartnerLimits(): Promise<CustomerLimit>;
|
|
175
|
+
/**
|
|
176
|
+
* @deprecated The `/limits` path was never served by the API. Use {@link getUserLimits}
|
|
177
|
+
* (JWT/user context) or {@link getPartnerLimits} (API-key/partner context). This alias
|
|
178
|
+
* forwards to `getUserLimits` and will be removed in a future major.
|
|
179
|
+
*/
|
|
148
180
|
getLimits(): Promise<CustomerLimit>;
|
|
149
181
|
getFeeRates(params?: GetFeeRatesParams): Promise<FeeRates>;
|
|
150
182
|
getFeeReport(params: FeeReportParams): Promise<FeeReport>;
|
|
151
183
|
}
|
|
152
184
|
|
|
153
|
-
export { ApiKeyAuth as A, type CorrectedField as C, DimesClient as D, type GetFeeRatesParams as G, type HintAdjustment as H, JwtAuth as J, type
|
|
185
|
+
export { ApiKeyAuth as A, type CorrectedField as C, DimesClient as D, type GetFeeRatesParams as G, type HintAdjustment as H, JwtAuth as J, type MarketExpand as M, type Paginated as P, type QuoteHint as Q, type SearchMarketsParams as S, type ApiKeyAuthOptions as a, type AuthProvider as b, type DimesClientOptions as c, type GetMarketsParams as d, type GetPositionsParams as e, type JwtAuthOptions as f, type MarketSort as g, type PositionStatus as h, hintAdjustment as i, marketMovedCodes as m, quoteErrorHint as q };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { M as Market, C as
|
|
1
|
+
import { M as Market, C as CreateQuoteParams, Q as Quote, P as Position, c as PositionTransactions, d as ContractInfo, e as CustomerLimit, F as FeeRates, f as FeeReportParams, g as FeeReport } from './aliases-BJyM8ydu.cjs';
|
|
2
2
|
|
|
3
3
|
declare const marketMovedCodes: Set<string>;
|
|
4
4
|
type QuoteHint = {
|
|
@@ -68,11 +68,15 @@ declare function hintAdjustment(hint: QuoteHint, current: {
|
|
|
68
68
|
interface Paginated<T> {
|
|
69
69
|
data: T[];
|
|
70
70
|
hasMore: boolean;
|
|
71
|
+
/** Total matching records. Present only when the request includes `expand: ["total_count"]`. */
|
|
72
|
+
totalCount?: number;
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
interface AuthProvider {
|
|
74
76
|
getHeaders(): Promise<Record<string, string>>;
|
|
75
77
|
refresh?(): Promise<void>;
|
|
78
|
+
/** Synchronously return the current cached JWT, or null if not yet obtained. Used by the WebSocket clients. */
|
|
79
|
+
getToken?(): string | null;
|
|
76
80
|
}
|
|
77
81
|
interface ApiKeyAuthOptions {
|
|
78
82
|
apiKey: string;
|
|
@@ -90,6 +94,7 @@ declare class ApiKeyAuth implements AuthProvider {
|
|
|
90
94
|
private readonly fetchFn;
|
|
91
95
|
constructor(options: ApiKeyAuthOptions);
|
|
92
96
|
getHeaders(): Promise<Record<string, string>>;
|
|
97
|
+
getToken(): string | null;
|
|
93
98
|
refresh(): Promise<void>;
|
|
94
99
|
private fetchToken;
|
|
95
100
|
}
|
|
@@ -105,6 +110,7 @@ declare class JwtAuth implements AuthProvider {
|
|
|
105
110
|
private refreshPromise;
|
|
106
111
|
constructor(options: JwtAuthOptions);
|
|
107
112
|
getHeaders(): Promise<Record<string, string>>;
|
|
113
|
+
getToken(): string | null;
|
|
108
114
|
refresh(): Promise<void>;
|
|
109
115
|
private fetchToken;
|
|
110
116
|
}
|
|
@@ -117,37 +123,63 @@ interface DimesClientOptions {
|
|
|
117
123
|
maxRetryDelayMs?: number;
|
|
118
124
|
}
|
|
119
125
|
type MarketSort = "ticker_asc" | "depth_desc" | "discovered_at_desc";
|
|
126
|
+
type MarketExpand = "total_count" | "prices";
|
|
127
|
+
type PositionStatus = "open" | "unwinding" | "pending" | "closing" | "closed" | "settled" | "liquidated";
|
|
120
128
|
interface GetMarketsParams {
|
|
121
|
-
cursor?: string;
|
|
122
129
|
limit?: number;
|
|
130
|
+
startingAfter?: string;
|
|
131
|
+
endingBefore?: string;
|
|
132
|
+
expand?: MarketExpand[];
|
|
133
|
+
sort?: MarketSort;
|
|
134
|
+
acceptingNewPositions?: boolean;
|
|
123
135
|
category?: string;
|
|
124
136
|
status?: string;
|
|
125
137
|
provider?: "polymarket" | "kalshi";
|
|
126
|
-
|
|
138
|
+
}
|
|
139
|
+
interface SearchMarketsParams extends GetMarketsParams {
|
|
140
|
+
query: string;
|
|
127
141
|
}
|
|
128
142
|
interface GetPositionsParams {
|
|
129
|
-
cursor?: string;
|
|
130
143
|
limit?: number;
|
|
131
|
-
|
|
144
|
+
startingAfter?: string;
|
|
145
|
+
endingBefore?: string;
|
|
146
|
+
expand?: string[];
|
|
147
|
+
sortBy?: "created_at" | "closed_at";
|
|
148
|
+
sortDirection?: "asc" | "desc";
|
|
149
|
+
status?: PositionStatus;
|
|
150
|
+
state?: "active" | "inactive";
|
|
132
151
|
}
|
|
133
152
|
interface GetFeeRatesParams {
|
|
134
153
|
ticker?: string;
|
|
135
154
|
}
|
|
136
155
|
declare class DimesClient {
|
|
156
|
+
/** The resolved API base URL. Useful for constructing WebSocket clients against the same host. */
|
|
157
|
+
readonly baseUrl: string;
|
|
137
158
|
private readonly http;
|
|
159
|
+
private readonly auth;
|
|
138
160
|
constructor(options: DimesClientOptions);
|
|
161
|
+
/** Synchronously return the current cached JWT, or null. Returns null if the auth provider does not expose one. */
|
|
162
|
+
getToken(): string | null;
|
|
139
163
|
getMarkets(params?: GetMarketsParams): Promise<Paginated<Market>>;
|
|
140
164
|
getMarket(ticker: string): Promise<Market>;
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
165
|
+
searchMarkets(params: SearchMarketsParams): Promise<Paginated<Market>>;
|
|
166
|
+
createDraftQuote(params: CreateQuoteParams): Promise<Quote>;
|
|
167
|
+
promoteDraftQuote(draftId: string): Promise<Quote>;
|
|
168
|
+
createQuote(params: CreateQuoteParams): Promise<Quote>;
|
|
144
169
|
getPositions(params?: GetPositionsParams): Promise<Position[]>;
|
|
145
170
|
getPositionTransactions(positionId: string): Promise<PositionTransactions>;
|
|
146
171
|
cancelPosition(positionId: string): Promise<void>;
|
|
147
172
|
getContractInfo(): Promise<ContractInfo>;
|
|
173
|
+
getUserLimits(): Promise<CustomerLimit>;
|
|
174
|
+
getPartnerLimits(): Promise<CustomerLimit>;
|
|
175
|
+
/**
|
|
176
|
+
* @deprecated The `/limits` path was never served by the API. Use {@link getUserLimits}
|
|
177
|
+
* (JWT/user context) or {@link getPartnerLimits} (API-key/partner context). This alias
|
|
178
|
+
* forwards to `getUserLimits` and will be removed in a future major.
|
|
179
|
+
*/
|
|
148
180
|
getLimits(): Promise<CustomerLimit>;
|
|
149
181
|
getFeeRates(params?: GetFeeRatesParams): Promise<FeeRates>;
|
|
150
182
|
getFeeReport(params: FeeReportParams): Promise<FeeReport>;
|
|
151
183
|
}
|
|
152
184
|
|
|
153
|
-
export { ApiKeyAuth as A, type CorrectedField as C, DimesClient as D, type GetFeeRatesParams as G, type HintAdjustment as H, JwtAuth as J, type
|
|
185
|
+
export { ApiKeyAuth as A, type CorrectedField as C, DimesClient as D, type GetFeeRatesParams as G, type HintAdjustment as H, JwtAuth as J, type MarketExpand as M, type Paginated as P, type QuoteHint as Q, type SearchMarketsParams as S, type ApiKeyAuthOptions as a, type AuthProvider as b, type DimesClientOptions as c, type GetMarketsParams as d, type GetPositionsParams as e, type JwtAuthOptions as f, type MarketSort as g, type PositionStatus as h, hintAdjustment as i, marketMovedCodes as m, quoteErrorHint as q };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as QuoteHint } from './dimes-client-
|
|
1
|
+
import { Q as QuoteHint } from './dimes-client-DZPhU1CG.cjs';
|
|
2
2
|
|
|
3
3
|
declare class DimesError extends Error {
|
|
4
4
|
readonly code: string;
|
|
@@ -23,4 +23,4 @@ declare class DimesContractError extends DimesError {
|
|
|
23
23
|
constructor(code: string, message: string);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export {
|
|
26
|
+
export { DimesContractError as D, DimesApiError as a, DimesError as b };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Q as QuoteHint } from './dimes-client-
|
|
1
|
+
import { Q as QuoteHint } from './dimes-client-DYFvnZUx.js';
|
|
2
2
|
|
|
3
3
|
declare class DimesError extends Error {
|
|
4
4
|
readonly code: string;
|
|
@@ -23,4 +23,4 @@ declare class DimesContractError extends DimesError {
|
|
|
23
23
|
constructor(code: string, message: string);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
export {
|
|
26
|
+
export { DimesContractError as D, DimesApiError as a, DimesError as b };
|
package/dist/index.cjs
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
|
|
8
|
+
var _chunk7SAMK7CScjs = require('./chunk-7SAMK7CS.cjs');
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
|
|
@@ -14,7 +15,7 @@ var _chunkY5WQ3UWFcjs = require('./chunk-Y5WQ3UWF.cjs');
|
|
|
14
15
|
var _chunk72LTVPD2cjs = require('./chunk-72LTVPD2.cjs');
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
var
|
|
18
|
+
var _chunk53U53KZ2cjs = require('./chunk-53U53KZ2.cjs');
|
|
18
19
|
|
|
19
20
|
// src/client/dimes-client.ts
|
|
20
21
|
var _humps = require('humps');
|
|
@@ -132,16 +133,25 @@ function buildQueryFromEntries(entries) {
|
|
|
132
133
|
return `?${search.toString()}`;
|
|
133
134
|
}
|
|
134
135
|
var DimesClient = class {
|
|
136
|
+
/** The resolved API base URL. Useful for constructing WebSocket clients against the same host. */
|
|
137
|
+
|
|
138
|
+
|
|
135
139
|
|
|
136
140
|
constructor(options) {
|
|
141
|
+
this.baseUrl = _nullishCoalesce(options.baseUrl, () => ( "https://api.dimes.fi"));
|
|
142
|
+
this.auth = options.auth;
|
|
137
143
|
this.http = new HttpClient({
|
|
138
|
-
baseUrl:
|
|
144
|
+
baseUrl: this.baseUrl,
|
|
139
145
|
auth: options.auth,
|
|
140
146
|
fetch: options.fetch,
|
|
141
147
|
maxRetries: options.maxRetries,
|
|
142
148
|
maxRetryDelayMs: options.maxRetryDelayMs
|
|
143
149
|
});
|
|
144
150
|
}
|
|
151
|
+
/** Synchronously return the current cached JWT, or null. Returns null if the auth provider does not expose one. */
|
|
152
|
+
getToken() {
|
|
153
|
+
return _nullishCoalesce(_optionalChain([this, 'access', _10 => _10.auth, 'access', _11 => _11.getToken, 'optionalCall', _12 => _12()]), () => ( null));
|
|
154
|
+
}
|
|
145
155
|
async getMarkets(params) {
|
|
146
156
|
const query = params ? buildQueryFromEntries(Object.entries(params)) : "";
|
|
147
157
|
return this.http.request(`/v1/prediction-markets/markets${query}`);
|
|
@@ -149,6 +159,10 @@ var DimesClient = class {
|
|
|
149
159
|
async getMarket(ticker) {
|
|
150
160
|
return this.http.request(`/v1/prediction-markets/markets/${encodeURIComponent(ticker)}`);
|
|
151
161
|
}
|
|
162
|
+
async searchMarkets(params) {
|
|
163
|
+
const query = buildQueryFromEntries(Object.entries(params));
|
|
164
|
+
return this.http.request(`/v1/prediction-markets/markets/search${query}`);
|
|
165
|
+
}
|
|
152
166
|
async createDraftQuote(params) {
|
|
153
167
|
return this.http.request("/v1/prediction-markets/draft-quotes", {
|
|
154
168
|
method: "POST",
|
|
@@ -176,15 +190,26 @@ var DimesClient = class {
|
|
|
176
190
|
);
|
|
177
191
|
}
|
|
178
192
|
async cancelPosition(positionId) {
|
|
179
|
-
await this.http.request(`/v1/prediction-markets/positions/${encodeURIComponent(positionId)}
|
|
180
|
-
method: "
|
|
193
|
+
await this.http.request(`/v1/prediction-markets/positions/${encodeURIComponent(positionId)}`, {
|
|
194
|
+
method: "DELETE"
|
|
181
195
|
});
|
|
182
196
|
}
|
|
183
197
|
async getContractInfo() {
|
|
184
198
|
return this.http.request("/v1/prediction-markets/contract-info");
|
|
185
199
|
}
|
|
200
|
+
async getUserLimits() {
|
|
201
|
+
return this.http.request("/v1/prediction-markets/user-limits");
|
|
202
|
+
}
|
|
203
|
+
async getPartnerLimits() {
|
|
204
|
+
return this.http.request("/v1/prediction-markets/partner-limits");
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* @deprecated The `/limits` path was never served by the API. Use {@link getUserLimits}
|
|
208
|
+
* (JWT/user context) or {@link getPartnerLimits} (API-key/partner context). This alias
|
|
209
|
+
* forwards to `getUserLimits` and will be removed in a future major.
|
|
210
|
+
*/
|
|
186
211
|
async getLimits() {
|
|
187
|
-
return this.
|
|
212
|
+
return this.getUserLimits();
|
|
188
213
|
}
|
|
189
214
|
async getFeeRates(params) {
|
|
190
215
|
const query = params ? buildQueryFromEntries(Object.entries(params)) : "";
|
|
@@ -220,6 +245,9 @@ var ApiKeyAuth = (_class = class {
|
|
|
220
245
|
}
|
|
221
246
|
return { Authorization: `Bearer ${this.jwt}` };
|
|
222
247
|
}
|
|
248
|
+
getToken() {
|
|
249
|
+
return this.jwt;
|
|
250
|
+
}
|
|
223
251
|
async refresh() {
|
|
224
252
|
if (this.refreshPromise) {
|
|
225
253
|
await this.refreshPromise;
|
|
@@ -237,7 +265,7 @@ var ApiKeyAuth = (_class = class {
|
|
|
237
265
|
method: "POST",
|
|
238
266
|
headers: {
|
|
239
267
|
"Content-Type": "application/json",
|
|
240
|
-
Authorization: `
|
|
268
|
+
Authorization: `Api-Key ${this.apiKey}`
|
|
241
269
|
},
|
|
242
270
|
body: JSON.stringify({ wallet_address: this.walletAddress })
|
|
243
271
|
});
|
|
@@ -267,6 +295,9 @@ var JwtAuth = (_class2 = class {
|
|
|
267
295
|
}
|
|
268
296
|
return { Authorization: `Bearer ${this.jwt}` };
|
|
269
297
|
}
|
|
298
|
+
getToken() {
|
|
299
|
+
return this.jwt;
|
|
300
|
+
}
|
|
270
301
|
async refresh() {
|
|
271
302
|
if (this.refreshPromise) {
|
|
272
303
|
await this.refreshPromise;
|
|
@@ -518,6 +549,45 @@ function computeMaxGain(input) {
|
|
|
518
549
|
};
|
|
519
550
|
}
|
|
520
551
|
|
|
552
|
+
// src/utils/partial-close.ts
|
|
553
|
+
var BPS_DIVISOR3 = 1e4;
|
|
554
|
+
var BPS_DIVISOR_BIG = 10000n;
|
|
555
|
+
var tokenUnitsPerToken = 1000000n;
|
|
556
|
+
var partialCloseMinTokens = 5n;
|
|
557
|
+
var partialCloseMinFractionBps = 2000n;
|
|
558
|
+
function closeTokenUnitsForPct(currentTokenUnits, pctBps) {
|
|
559
|
+
const clampedBps = BigInt(Math.max(0, Math.min(BPS_DIVISOR3, Math.round(pctBps))));
|
|
560
|
+
return currentTokenUnits * clampedBps / BPS_DIVISOR_BIG;
|
|
561
|
+
}
|
|
562
|
+
function pctBpsForCloseTokenUnits(currentTokenUnits, closeTokenUnits) {
|
|
563
|
+
if (currentTokenUnits <= 0n) {
|
|
564
|
+
return 0;
|
|
565
|
+
}
|
|
566
|
+
return Number(closeTokenUnits * BPS_DIVISOR_BIG / currentTokenUnits);
|
|
567
|
+
}
|
|
568
|
+
function fallbackMinPartialCloseTokenUnits(originalTokenUnits) {
|
|
569
|
+
const floorTokens = partialCloseMinTokens * tokenUnitsPerToken;
|
|
570
|
+
const floorFraction = originalTokenUnits * partialCloseMinFractionBps / BPS_DIVISOR_BIG;
|
|
571
|
+
return floorFraction > floorTokens ? floorFraction : floorTokens;
|
|
572
|
+
}
|
|
573
|
+
function clampCloseTokenUnits(value, min, max) {
|
|
574
|
+
if (value < min) {
|
|
575
|
+
return min;
|
|
576
|
+
}
|
|
577
|
+
if (value > max) {
|
|
578
|
+
return max;
|
|
579
|
+
}
|
|
580
|
+
return value;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
|
|
590
|
+
|
|
521
591
|
|
|
522
592
|
|
|
523
593
|
|
|
@@ -551,5 +621,5 @@ function computeMaxGain(input) {
|
|
|
551
621
|
|
|
552
622
|
|
|
553
623
|
|
|
554
|
-
exports.ApiKeyAuth = ApiKeyAuth; exports.DimesApiError = _chunk72LTVPD2cjs.DimesApiError; exports.DimesClient = DimesClient; exports.DimesContractError = _chunk72LTVPD2cjs.DimesContractError; exports.DimesError = _chunk72LTVPD2cjs.DimesError; exports.JwtAuth = JwtAuth; exports.buildQuoteParams =
|
|
624
|
+
exports.ApiKeyAuth = ApiKeyAuth; exports.DimesApiError = _chunk72LTVPD2cjs.DimesApiError; exports.DimesClient = DimesClient; exports.DimesContractError = _chunk72LTVPD2cjs.DimesContractError; exports.DimesError = _chunk72LTVPD2cjs.DimesError; exports.JwtAuth = JwtAuth; exports.buildQuoteParams = _chunk7SAMK7CScjs.buildQuoteParams; exports.clampCloseTokenUnits = clampCloseTokenUnits; exports.closeTokenUnitsForPct = closeTokenUnitsForPct; exports.computeMaxGain = computeMaxGain; exports.computeOriginationFeeUsdcUnits = computeOriginationFeeUsdcUnits; exports.computePolymarketTradingFee = computePolymarketTradingFee; exports.defaultSide = defaultSide; exports.estimateLiquidationPrice = estimateLiquidationPrice; exports.executeQuote = _chunk7SAMK7CScjs.executeQuote; exports.expectedPositionTokenUnits = expectedPositionTokenUnits; exports.fallbackMinPartialCloseTokenUnits = fallbackMinPartialCloseTokenUnits; exports.formatErrorMessage = _chunk72LTVPD2cjs.formatErrorMessage; exports.getOriginationFeeBreakdown = getOriginationFeeBreakdown; exports.getSidedEligibility = getSidedEligibility; exports.hintAdjustment = _chunk7SAMK7CScjs.hintAdjustment; exports.isClosedPosition = isClosedPosition; exports.isFullyClosed = isFullyClosed; exports.isFullyOpen = isFullyOpen; exports.isMarketMovedError = _chunk7SAMK7CScjs.isMarketMovedError; exports.isOpenPosition = isOpenPosition; exports.leverageMaxBps = leverageMaxBps; exports.marketMovedCodes = _chunk7SAMK7CScjs.marketMovedCodes; exports.maxLeverageBpsAtNotional = maxLeverageBpsAtNotional; exports.maxViableLeverageBpsForCollateral = maxViableLeverageBpsForCollateral; exports.partialCloseMinFractionBps = partialCloseMinFractionBps; exports.partialCloseMinTokens = partialCloseMinTokens; exports.pctBpsForCloseTokenUnits = pctBpsForCloseTokenUnits; exports.positionEventTypes = _chunk53U53KZ2cjs.positionEventTypes; exports.quoteErrorHint = _chunk7SAMK7CScjs.quoteErrorHint; exports.rejectionReasonShort = rejectionReasonShort; exports.rejectionReasonText = rejectionReasonText; exports.resolveFriendlyMessage = _chunk72LTVPD2cjs.resolveFriendlyMessage; exports.resolveOriginationFeeBps = resolveOriginationFeeBps; exports.resolveProtocolOriginationFeeBps = resolveProtocolOriginationFeeBps; exports.tokenUnitsPerToken = tokenUnitsPerToken;
|
|
555
625
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/alexemery/bl/dimes-sdk/dist/index.cjs","../src/client/dimes-client.ts","../src/client/http.ts","../src/client/auth.ts","../src/types/aliases.ts","../src/utils/eligibility.ts","../src/utils/leverage.ts","../src/utils/fees.ts","../src/utils/max-gain.ts"],"names":["BPS_DIVISOR"],"mappings":"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","file":"/Users/alexemery/bl/dimes-sdk/dist/index.cjs","sourcesContent":[null,"import { decamelizeKeys } from \"humps\";\nimport type {\n ContractInfo,\n CreateOfferParams,\n CustomerLimit,\n FeeRates,\n FeeReport,\n FeeReportParams,\n Market,\n Offer,\n Paginated,\n Position,\n PositionTransactions,\n} from \"../types\";\nimport type { AuthProvider } from \"./auth\";\nimport { HttpClient } from \"./http\";\n\nexport interface DimesClientOptions {\n baseUrl?: string;\n auth: AuthProvider;\n fetch?: typeof globalThis.fetch;\n maxRetries?: number;\n maxRetryDelayMs?: number;\n}\n\nexport type MarketSort = \"ticker_asc\" | \"depth_desc\" | \"discovered_at_desc\";\n\nexport interface GetMarketsParams {\n cursor?: string;\n limit?: number;\n category?: string;\n status?: string;\n provider?: \"polymarket\" | \"kalshi\";\n sort?: MarketSort;\n}\n\nexport interface GetPositionsParams {\n cursor?: string;\n limit?: number;\n status?: \"open\" | \"closed\";\n}\n\nexport interface GetFeeRatesParams {\n ticker?: string;\n}\n\nfunction buildQueryFromEntries(entries: [string, unknown][]): string {\n const filtered = entries.filter(([, v]) => v !== undefined && v !== null);\n if (filtered.length === 0) {\n return \"\";\n }\n\n const search = new URLSearchParams();\n for (const [key, value] of filtered) {\n search.set(\n key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),\n String(value),\n );\n }\n\n return `?${search.toString()}`;\n}\n\nexport class DimesClient {\n private readonly http: HttpClient;\n\n constructor(options: DimesClientOptions) {\n this.http = new HttpClient({\n baseUrl: options.baseUrl ?? \"https://api.dimes.fi\",\n auth: options.auth,\n fetch: options.fetch,\n maxRetries: options.maxRetries,\n maxRetryDelayMs: options.maxRetryDelayMs,\n });\n }\n\n public async getMarkets(params?: GetMarketsParams): Promise<Paginated<Market>> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.request<Paginated<Market>>(`/v1/prediction-markets/markets${query}`);\n }\n\n public async getMarket(ticker: string): Promise<Market> {\n return this.http.request<Market>(`/v1/prediction-markets/markets/${encodeURIComponent(ticker)}`);\n }\n\n public async createDraftQuote(params: CreateOfferParams): Promise<Offer> {\n return this.http.request<Offer>(\"/v1/prediction-markets/draft-quotes\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n\n public async promoteDraftQuote(draftId: string): Promise<Offer> {\n return this.http.request<Offer>(`/v1/prediction-markets/promoted-quotes/${encodeURIComponent(draftId)}`, {\n method: \"POST\",\n });\n }\n\n public async createQuote(params: CreateOfferParams): Promise<Offer> {\n return this.http.request<Offer>(\"/v1/prediction-markets/quotes\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n\n public async getPositions(params?: GetPositionsParams): Promise<Position[]> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.requestList<Position>(`/v1/prediction-markets/positions${query}`);\n }\n\n public async getPositionTransactions(positionId: string): Promise<PositionTransactions> {\n return this.http.request<PositionTransactions>(\n `/v1/prediction-markets/positions/${encodeURIComponent(positionId)}/transactions`,\n );\n }\n\n public async cancelPosition(positionId: string): Promise<void> {\n await this.http.request<unknown>(`/v1/prediction-markets/positions/${encodeURIComponent(positionId)}/cancel`, {\n method: \"POST\",\n });\n }\n\n public async getContractInfo(): Promise<ContractInfo> {\n return this.http.request<ContractInfo>(\"/v1/prediction-markets/contract-info\");\n }\n\n public async getLimits(): Promise<CustomerLimit> {\n return this.http.request<CustomerLimit>(\"/v1/prediction-markets/limits\");\n }\n\n public async getFeeRates(params?: GetFeeRatesParams): Promise<FeeRates> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.request<FeeRates>(`/v1/prediction-markets/fee-rates${query}`);\n }\n\n public async getFeeReport(params: FeeReportParams): Promise<FeeReport> {\n return this.http.request<FeeReport>(\"/v1/prediction-markets/fee-reports\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n}\n","import { camelizeKeys, type Camelized } from \"humps\";\nimport { DimesApiError } from \"../errors/dimes-error\";\nimport type { AuthProvider } from \"./auth\";\n\nexport type { Camelized };\n\ninterface ApiErrorBody {\n error?: {\n type?: string;\n code?: string;\n message: string;\n params?: Record<string, unknown>;\n };\n}\n\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_TOO_MANY_REQUESTS = 429;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\nconst DEFAULT_RETRY_DELAY_MS = 1000;\nconst BACKOFF_FACTOR = 2;\nconst MS_PER_SECOND = 1000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseRetryAfterMs(header: string | null, attempt: number): number {\n const fallbackMs = DEFAULT_RETRY_DELAY_MS * BACKOFF_FACTOR ** attempt;\n if (!header) {\n return fallbackMs;\n }\n\n const seconds = Number(header);\n if (!Number.isNaN(seconds) && seconds >= 0) {\n return seconds * MS_PER_SECOND;\n }\n\n const dateMs = Date.parse(header);\n if (!Number.isNaN(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return fallbackMs;\n}\n\n// eslint-disable-next-line complexity\nasync function throwFromResponse(response: Response): Promise<never> {\n const rawBody = await response.text();\n let parsed: ApiErrorBody | null;\n try {\n parsed = rawBody ? (JSON.parse(rawBody) as ApiErrorBody) : null;\n } catch {\n parsed = null;\n }\n\n const code = parsed?.error?.code ?? \"unknown_error\";\n const type = parsed?.error?.type ?? null;\n const message = parsed?.error?.message ?? `API error ${response.status}`;\n const rawParams = parsed?.error?.params;\n const params = rawParams ? (camelizeKeys(rawParams) as Record<string, unknown>) : null;\n\n throw new DimesApiError({ status: response.status, code, type, message, params });\n}\n\nexport interface HttpClientOptions {\n baseUrl: string;\n auth: AuthProvider;\n fetch?: typeof globalThis.fetch;\n maxRetries?: number;\n maxRetryDelayMs?: number;\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n\n private readonly auth: AuthProvider;\n\n private readonly fetchFn: typeof globalThis.fetch;\n\n private readonly maxRetries: number;\n\n private readonly maxRetryDelayMs: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl;\n this.auth = options.auth;\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.maxRetryDelayMs = options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n }\n\n public async request<T>(path: string, options?: RequestInit): Promise<T> {\n const authHeaders = await this.auth.getHeaders();\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n ...(options?.headers as Record<string, string> | undefined),\n };\n\n let response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers,\n });\n\n if (response.status === HTTP_UNAUTHORIZED && this.auth.refresh) {\n await this.auth.refresh();\n const retryHeaders = await this.auth.getHeaders();\n response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers: { ...headers, ...retryHeaders },\n });\n }\n\n for (let attempt = 0; response.status === HTTP_TOO_MANY_REQUESTS && attempt < this.maxRetries; attempt++) {\n const delayMs = parseRetryAfterMs(response.headers.get(\"retry-after\"), attempt);\n if (delayMs > this.maxRetryDelayMs) {\n break;\n }\n await sleep(delayMs);\n const freshAuth = await this.auth.getHeaders();\n response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers: { ...headers, ...freshAuth },\n });\n }\n\n if (!response.ok) {\n await throwFromResponse(response);\n }\n\n return camelizeKeys(await response.json()) as T;\n }\n\n public async requestList<T>(path: string, options?: RequestInit): Promise<T[]> {\n const result = await this.request<{ data: T[] }>(path, options);\n return result.data;\n }\n}\n","import { DimesError } from \"../errors/dimes-error\";\n\nexport interface AuthProvider {\n getHeaders(): Promise<Record<string, string>>;\n refresh?(): Promise<void>;\n}\n\ninterface TokenResponse {\n token: string;\n expires_at: string;\n}\n\nexport interface ApiKeyAuthOptions {\n apiKey: string;\n walletAddress: string;\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport class ApiKeyAuth implements AuthProvider {\n private jwt: string | null = null;\n\n private expiresAt = 0;\n\n private refreshPromise: Promise<void> | null = null;\n\n private readonly apiKey: string;\n\n private readonly walletAddress: string;\n\n private readonly baseUrl: string;\n\n private readonly fetchFn: typeof globalThis.fetch;\n\n constructor(options: ApiKeyAuthOptions) {\n this.apiKey = options.apiKey;\n this.walletAddress = options.walletAddress;\n this.baseUrl = options.baseUrl ?? \"https://api.dimes.fi\";\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getHeaders(): Promise<Record<string, string>> {\n const TOKEN_EXPIRY_BUFFER_MS = 30_000;\n if (!this.jwt || Date.now() >= this.expiresAt - TOKEN_EXPIRY_BUFFER_MS) {\n await this.refresh();\n }\n return { Authorization: `Bearer ${this.jwt}` };\n }\n\n public async refresh(): Promise<void> {\n if (this.refreshPromise) {\n await this.refreshPromise;\n return;\n }\n\n this.refreshPromise = this.fetchToken();\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async fetchToken(): Promise<void> {\n const response = await this.fetchFn(`${this.baseUrl}/v1/prediction-markets/tokens`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Bearer ${this.apiKey}`,\n },\n body: JSON.stringify({ wallet_address: this.walletAddress }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new DimesError(\"auth_token_failed\", `Failed to obtain auth token (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n this.jwt = data.token;\n this.expiresAt = new Date(data.expires_at).getTime();\n }\n}\n\nexport interface JwtAuthOptions {\n tokenUrl: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport class JwtAuth implements AuthProvider {\n private readonly fetchFn: typeof globalThis.fetch;\n\n private readonly tokenUrl: string;\n\n private jwt: string | null = null;\n\n private expiresAt = 0;\n\n private refreshPromise: Promise<void> | null = null;\n\n constructor(options: JwtAuthOptions) {\n this.tokenUrl = options.tokenUrl;\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getHeaders(): Promise<Record<string, string>> {\n const TOKEN_EXPIRY_BUFFER_MS = 30_000;\n if (!this.jwt || Date.now() >= this.expiresAt - TOKEN_EXPIRY_BUFFER_MS) {\n await this.refresh();\n }\n return { Authorization: `Bearer ${this.jwt}` };\n }\n\n public async refresh(): Promise<void> {\n if (this.refreshPromise) {\n await this.refreshPromise;\n return;\n }\n\n this.refreshPromise = this.fetchToken();\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async fetchToken(): Promise<void> {\n const response = await this.fetchFn(this.tokenUrl);\n\n if (!response.ok) {\n const text = await response.text();\n throw new DimesError(\n \"auth_token_failed\",\n `Failed to fetch token from ${this.tokenUrl} (${response.status}): ${text}`,\n );\n }\n\n const data = (await response.json()) as TokenResponse;\n this.jwt = data.token;\n this.expiresAt = new Date(data.expires_at).getTime();\n }\n}\n","import type { CamelizeKeys } from \"./camelize\";\nimport type { components } from \"./generated\";\n\ntype Raw = components[\"schemas\"];\n\nexport type Market = CamelizeKeys<Raw[\"CustomerMarket\"]>;\n\nexport type MarketLeverage = CamelizeKeys<Raw[\"CustomerLeverage\"]>;\n\nexport type MarketMaxLeveragePerNotional = CamelizeKeys<Raw[\"CustomerMaxMarketLeveragePerNotional\"]>;\n\nexport type MarketSidedMaxLeveragePerNotional = CamelizeKeys<Raw[\"CustomerSidedMaxMarketLeveragePerNotional\"]>;\n\nexport type MarketSidedEligibility = CamelizeKeys<Raw[\"CustomerSidedEligibility\"]>;\n\nexport type MarketFees = CamelizeKeys<Raw[\"CustomerFees\"]>;\n\nexport type MarketPrices = CamelizeKeys<Raw[\"CustomerMarketPrices\"]>;\n\nexport interface MarketPolymarket {\n conditionId: string | null;\n noTokenId: string;\n slug: string;\n yesTokenId: string;\n}\n\nexport type OriginationTier = CamelizeKeys<Raw[\"CustomerOriginationTier\"]>;\n\nexport type Offer = CamelizeKeys<Raw[\"CustomerOffer\"]>;\n\nexport type OpenPosition = CamelizeKeys<Raw[\"CustomerOpenPosition\"]>;\n\nexport type ClosedPosition = CamelizeKeys<Raw[\"CustomerClosedPosition\"]>;\n\nexport type Position = OpenPosition | ClosedPosition;\n\nexport type PositionEntry = CamelizeKeys<Raw[\"CustomerPositionEntry\"]>;\n\nexport type PositionCurrent = CamelizeKeys<Raw[\"CustomerPositionCurrent\"]>;\n\nexport type PositionRisk = CamelizeKeys<Raw[\"CustomerPositionRisk\"]>;\n\nexport type PositionOpenFees = CamelizeKeys<Raw[\"CustomerPositionOpenFees\"]>;\n\nexport type PositionClosedFees = CamelizeKeys<Raw[\"CustomerPositionClosedFees\"]>;\n\nexport type PositionResult = CamelizeKeys<Raw[\"CustomerPositionResult\"]>;\n\nexport type PositionTiming = CamelizeKeys<Raw[\"CustomerPositionTiming\"]>;\n\nexport type PositionFailure = CamelizeKeys<Raw[\"CustomerPositionFailure\"]>;\n\nexport type CloseAttempt = CamelizeKeys<Raw[\"CustomerCloseAttempt\"]>;\n\nexport type PositionUnwind = CamelizeKeys<Raw[\"CustomerPositionUnwind\"]>;\n\nexport type PositionUnwindList = CamelizeKeys<Raw[\"CustomerPositionUnwindList\"]>;\n\nexport type PositionTransactionEntry = CamelizeKeys<Raw[\"PositionTransactionEntry\"]>;\n\nexport type PositionTransactionGroup = CamelizeKeys<Raw[\"PositionTransactionGroup\"]>;\n\nexport type PositionTransactions = CamelizeKeys<Raw[\"PositionTransactions\"]>;\n\nexport type ContractInfo = CamelizeKeys<Raw[\"CustomerContractInfo\"]>;\n\nexport type CustomerLimit = CamelizeKeys<Raw[\"CustomerLimit\"]>;\n\nexport type CreateTokenResult = CamelizeKeys<Raw[\"CreateTokenResult\"]>;\n\nexport type FeeRatesOriginationTier = CamelizeKeys<Raw[\"CustomerOriginationFeeTier\"]>;\n\nexport type FeeRatesMarket = CamelizeKeys<Raw[\"CustomerFeeRatesMarket\"]>;\n\nexport type FeeRates = CamelizeKeys<Raw[\"CustomerFeeRates\"]>;\n\nexport type FeeReport = CamelizeKeys<Raw[\"CustomerFeeReport\"]>;\n\nexport interface CreateOfferParams {\n marketTicker: string;\n effectiveSide: \"yes\" | \"no\";\n leverageBps: number;\n notionalAmountUsdPips: string;\n slippageBps: number;\n pmProvider?: \"polymarket\" | \"kalshi\";\n}\n\nexport interface FeeReportParams {\n marketTicker: string;\n effectiveSide: \"yes\" | \"no\";\n leverageBps: number;\n notionalAmountUsdPips: string;\n entryPriceUsdPips?: string;\n}\n\nexport function isOpenPosition(p: Position): p is OpenPosition {\n return \"current\" in p;\n}\n\nexport function isClosedPosition(p: Position): p is ClosedPosition {\n return \"result\" in p;\n}\n\nexport function leverageMaxBps(lev: MarketLeverage, side: \"yes\" | \"no\"): number {\n const perSide = side === \"yes\" ? lev.maxYesBps : lev.maxNoBps;\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return perSide ?? lev.maxBps ?? lev.minBps;\n}\n","import type { Market } from \"../types/aliases\";\n\nexport type Side = \"yes\" | \"no\";\n\nexport interface SideEligibility {\n open: boolean;\n reasonCode: string | null;\n}\n\nexport interface SidedEligibility {\n yes: SideEligibility;\n no: SideEligibility;\n}\n\nexport function getSidedEligibility(market: Market): SidedEligibility {\n const sided = market.sidedEligibility;\n return {\n yes: {\n open: sided.yes.acceptingNewPositions,\n reasonCode: sided.yes.rejectionReasonCode ?? null,\n },\n no: {\n open: sided.no.acceptingNewPositions,\n reasonCode: sided.no.rejectionReasonCode ?? null,\n },\n };\n}\n\nexport function defaultSide(eligibility: SidedEligibility): Side | null {\n if (eligibility.yes.open && eligibility.no.open) {\n return \"yes\";\n }\n if (eligibility.yes.open) {\n return \"yes\";\n }\n if (eligibility.no.open) {\n return \"no\";\n }\n return null;\n}\n\nexport function isFullyOpen(eligibility: SidedEligibility): boolean {\n return eligibility.yes.open && eligibility.no.open;\n}\n\nexport function isFullyClosed(eligibility: SidedEligibility): boolean {\n return !eligibility.yes.open && !eligibility.no.open;\n}\n\nexport function rejectionReasonText(code: string | null | undefined): string {\n if (!code) {\n return \"Not eligible for new positions\";\n }\n const stripped = code\n .replace(/^quote_/i, \"\")\n .replaceAll(\"_\", \" \")\n .toLowerCase();\n return stripped.charAt(0).toUpperCase() + stripped.slice(1);\n}\n\nexport function rejectionReasonShort(code: string | null | undefined): string {\n if (!code) {\n return \"unavailable\";\n }\n const stripped = code\n .replace(/^quote_/i, \"\")\n .replace(/_exceeded$/i, \"\")\n .replace(/_too_(low|high)$/i, \"\")\n .replaceAll(\"_\", \" \")\n .toLowerCase();\n return stripped;\n}\n","import { type Market, type MarketMaxLeveragePerNotional, leverageMaxBps } from \"../types/aliases\";\nimport type { Side } from \"./eligibility\";\n\nconst USD_100 = 100;\nconst USD_500 = 500;\nconst USD_1K = 1_000;\nconst USD_10K = 10_000;\nconst BPS_PER_UNIT = 10_000;\n\nconst ANCHORS_USD = [USD_100, USD_500, USD_1K, USD_10K] as const;\n\ntype Anchor = readonly [usd: number, bps: number];\n\nfunction anchorsForSide(market: Market, side: Side): readonly Anchor[] {\n const sided = market.leverage.maxMarketLeveragePerNotional;\n const per: MarketMaxLeveragePerNotional = side === \"yes\" ? sided.yes : sided.no;\n return [\n [ANCHORS_USD[0], per.at100UsdBps],\n [ANCHORS_USD[1], per.at500UsdBps],\n [ANCHORS_USD[2], per.at1000UsdBps],\n [ANCHORS_USD[3], per.at10000UsdBps],\n ];\n}\n\nfunction snapDownToStep(bps: number, stepBps: number, minBps: number): number {\n if (stepBps <= 0) {\n return Math.max(minBps, bps);\n }\n const k = Math.floor((bps - minBps) / stepBps);\n return Math.max(minBps, minBps + Math.max(0, k) * stepBps);\n}\n\nfunction interpolateRawBps(anchors: readonly Anchor[], notionalUsd: number): number {\n const first = anchors[0];\n const last = anchors[anchors.length - 1];\n if (!first || !last) {\n return 0;\n }\n if (notionalUsd <= first[0]) {\n return first[1];\n }\n if (notionalUsd >= last[0]) {\n return last[1];\n }\n for (let i = 1; i < anchors.length; i++) {\n const hi = anchors[i];\n const lo = anchors[i - 1];\n if (!hi || !lo) {\n continue;\n }\n if (notionalUsd <= hi[0]) {\n const t = (notionalUsd - lo[0]) / (hi[0] - lo[0]);\n return lo[1] + (hi[1] - lo[1]) * t;\n }\n }\n return last[1];\n}\n\n/**\n * Maximum leverage in basis points the API will allow at a given notional, per side.\n * Piecewise-linear interpolation across the four anchor points ($100/$500/$1k/$10k).\n * Below $100 uses the $100 cap; above $10k uses the $10k cap. Result is clamped to\n * the absolute side max and rounded down to `stepBps`.\n */\nexport function maxLeverageBpsAtNotional(market: Market, side: Side, notionalUsd: number): number {\n const sideAbsMax = leverageMaxBps(market.leverage, side);\n if (!Number.isFinite(notionalUsd) || notionalUsd <= 0) {\n return sideAbsMax;\n }\n const anchors = anchorsForSide(market, side);\n const raw = interpolateRawBps(anchors, notionalUsd);\n const clamped = Math.min(sideAbsMax, raw);\n return snapDownToStep(clamped, market.leverage.stepBps, market.leverage.minBps);\n}\n\n/**\n * Largest leverage (bps) the user can pick for a given collateral such that the\n * resulting notional still satisfies the per-notional cap.\n *\n * Walks down from the absolute side max in `stepBps` increments and returns the\n * first leverage `lev` where `lev <= maxLeverageBpsAtNotional(collateral * lev / 1)`.\n */\nexport function maxViableLeverageBpsForCollateral(market: Market, side: Side, collateralUsd: number): number {\n const sideAbsMax = leverageMaxBps(market.leverage, side);\n if (!Number.isFinite(collateralUsd) || collateralUsd <= 0) {\n return sideAbsMax;\n }\n\n const step = Math.max(market.leverage.stepBps, 1);\n const minBps = market.leverage.minBps;\n const maxSteps = Math.max(0, Math.floor((sideAbsMax - minBps) / step));\n\n for (let i = maxSteps; i >= 0; i--) {\n const lev = minBps + i * step;\n const notionalUsd = collateralUsd * (lev / BPS_PER_UNIT);\n if (lev <= maxLeverageBpsAtNotional(market, side, notionalUsd)) {\n return lev;\n }\n }\n return minBps;\n}\n","import type { Position, PositionEntry } from \"../types/aliases\";\n\nconst BPS_DIVISOR = 10_000;\n\n/**\n * One leverage-tiered origination-fee rate. `maxLeverageBps` is the inclusive\n * upper bound (in basis points) of leverage covered by this tier; `feeBps` is\n * the protocol origination fee charged for any leverage at or below that bound.\n * Tiers must be supplied in ascending `maxLeverageBps` order; the final tier\n * acts as the catch-all for the highest leverages.\n */\nexport interface OriginationFeeTier {\n maxLeverageBps: number;\n feeBps: number;\n}\n\n/**\n * Resolve the protocol origination fee rate (bps) for a given leverage from an\n * ordered list of leverage tiers. Returns the `feeBps` of the first tier whose\n * `maxLeverageBps >= leverageBps`. If no tier matches (leverage exceeds every\n * bound) the last tier is used as the catch-all. The caller fetches the tiers\n * from the `fee-rates` API; the math is pure.\n *\n * Note: this returns the PROTOCOL rate only — add `partnerOriginationFeeBps`\n * separately (see {@link resolveOriginationFeeBps}).\n */\nexport function resolveProtocolOriginationFeeBps(tiers: readonly OriginationFeeTier[], leverageBps: number): number {\n const lastTier = tiers[tiers.length - 1];\n if (!lastTier) {\n return 0;\n }\n for (const tier of tiers) {\n if (leverageBps <= tier.maxLeverageBps) {\n return tier.feeBps;\n }\n }\n return lastTier.feeBps;\n}\n\n/**\n * Combined origination fee rate (bps) = protocol tier rate + partner add-on.\n * Mirrors the server: the protocol rate is leverage-tiered, the partner rate is\n * a flat add-on supplied by the partner's `fee-rates` config.\n */\nexport function resolveOriginationFeeBps(\n tiers: readonly OriginationFeeTier[],\n leverageBps: number,\n partnerOriginationFeeBps: number,\n): number {\n return resolveProtocolOriginationFeeBps(tiers, leverageBps) + partnerOriginationFeeBps;\n}\n\n/**\n * Origination fee in USDC units (1_000_000 = $1), floored, matching the server:\n * `floor(notionalUsdcUnits * combinedOriginationFeeBps / 10_000)`.\n */\nexport function computeOriginationFeeUsdcUnits(notionalUsdcUnits: number, combinedOriginationFeeBps: number): number {\n return Math.floor((notionalUsdcUnits * combinedOriginationFeeBps) / BPS_DIVISOR);\n}\n\nexport interface OriginationFeeBreakdown {\n totalBps: number;\n totalUsd: number;\n protocolBps: number;\n protocolUsd: number;\n partnerBps: number;\n partnerUsd: number;\n}\n\nfunction getEntry(position: Position): PositionEntry {\n return position.entry;\n}\n\nexport function getOriginationFeeBreakdown(position: Position): OriginationFeeBreakdown {\n const entry = getEntry(position);\n const totalBps = entry.originationFeeBps;\n const totalUsd = Number(entry.originationFeeUsd) || 0;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const protocolBps = entry.protocolOriginationFeeBps ?? totalBps;\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const partnerBps = entry.partnerOriginationFeeBps ?? 0;\n\n const share = totalBps > 0 ? protocolBps / totalBps : 1;\n const protocolUsd = totalUsd * share;\n const partnerUsd = totalUsd - protocolUsd;\n\n return {\n totalBps,\n totalUsd,\n protocolBps,\n protocolUsd,\n partnerBps,\n partnerUsd,\n };\n}\n","const BPS_DIVISOR = 10_000;\nconst USDC_UNITS_PER_USD = 1_000_000;\nconst TOKEN_UNITS_PER_TOKEN = 1_000_000;\n\n/**\n * Inputs to the Polymarket trading-fee curve. All rates are basis points; the\n * caller fetches `feeRateBps`, `feeExponent`, and `builderTakerFeeRateBps` from\n * the `fee-rates` API (snapshotted server-side as `Offer.polymarketFeeRateBps`\n * / `polymarketFeeExponent` / `polymarketBuilderFeeRateBps`).\n */\nexport interface PolymarketTradingFeeInput {\n notionalUsdcUnits: number;\n priceUsd: number;\n feeRateBps: number;\n feeExponent?: number;\n builderTakerFeeRateBps?: number;\n}\n\n/**\n * Polymarket open/close trading fee in USDC units (1_000_000 = $1).\n *\n * Matches the canonical formula (docs: position-math/09-exchange-trading-fees):\n *\n * ```\n * protocol = feeRateBps / 10_000 * C * (p * (1 - p)) ^ feeExponent * 1e6\n * builder = builderTakerFeeRateBps / 10_000 * notionalUsdc\n * fee = ceil(protocol + builder)\n * ```\n *\n * where `C` = contracts = notionalUsd / p, `p` = price in dollars (0..1). The\n * curvature term peaks at p = 0.5 and is symmetric; the flat builder term does\n * not shrink at price extremes. `feeExponent` defaults to 1 (standard quadratic\n * curvature); some markets use 0 (flat `feeRate * C`). `builderTakerFeeRateBps`\n * defaults to 0 (Bloom's own operator builder code is always 0-fee).\n */\nexport function computePolymarketTradingFee(input: PolymarketTradingFeeInput): number {\n const { notionalUsdcUnits, priceUsd, feeRateBps } = input;\n const feeExponent = input.feeExponent ?? 1;\n const builderTakerFeeRateBps = input.builderTakerFeeRateBps ?? 0;\n\n if (priceUsd <= 0) {\n return 0;\n }\n\n const notionalUsd = notionalUsdcUnits / USDC_UNITS_PER_USD;\n const contracts = notionalUsd / priceUsd;\n const curvature = (priceUsd * (1 - priceUsd)) ** feeExponent;\n\n const protocolFeeUsdcUnits = (feeRateBps / BPS_DIVISOR) * contracts * curvature * USDC_UNITS_PER_USD;\n const builderFeeUsdcUnits = (builderTakerFeeRateBps / BPS_DIVISOR) * notionalUsdcUnits;\n\n return Math.ceil(protocolFeeUsdcUnits + builderFeeUsdcUnits);\n}\n\n/**\n * Expected position size in token units (1_000_000 = 1 whole token) for a given\n * notional and entry price, optionally discounted by slippage. A token redeems\n * at $1 on a win, so token units are numerically equal to USDC units.\n *\n * `tokens = notionalUsd / entryPrice`, then scaled down by `slippageBps`.\n * Floored to whole token units. This is an at-quote estimate of the offer's\n * `minExpectedPositionTokenUnits`; the server is authoritative.\n */\nexport function expectedPositionTokenUnits(notionalUsdcUnits: number, entryPriceUsd: number, slippageBps = 0): number {\n if (entryPriceUsd <= 0) {\n return 0;\n }\n const notionalUsd = notionalUsdcUnits / USDC_UNITS_PER_USD;\n const tokens = notionalUsd / entryPriceUsd;\n const slippageFactor = (BPS_DIVISOR - slippageBps) / BPS_DIVISOR;\n return Math.floor(tokens * slippageFactor * TOKEN_UNITS_PER_TOKEN);\n}\n\n/**\n * Estimated base liquidation price (deterministic, at-entry closed form).\n *\n * **This is an estimate, not the server's TWAP solver result.** It computes the\n * at-entry base liquidation price (F = 0, no safety margin) per the docs\n * (position-math/04-liquidation):\n *\n * ```\n * P_liq_base = entryPrice * (L - 1) / L * (1 + liquidationFeeBps / 10_000)\n * ```\n *\n * where `L = leverageBps / 10_000`. At 1x leverage the borrowed capital is zero\n * so the liquidation price is zero. `entryPriceUsd` is in dollars; the result is\n * in the same units. The live server liquidation price drifts above this as the\n * lifetime fee `F` accrues and includes a safety margin.\n */\nexport function estimateLiquidationPrice(\n entryPriceUsd: number,\n leverageBps: number,\n liquidationFeeBps: number,\n): number {\n const leverage = leverageBps / BPS_DIVISOR;\n if (leverage <= 1) {\n return 0;\n }\n const loanPerToken = entryPriceUsd * ((leverage - 1) / leverage);\n return loanPerToken * (1 + liquidationFeeBps / BPS_DIVISOR);\n}\n\n/**\n * Inputs for {@link computeMaxGain}. All amounts are in USDC units\n * (1_000_000 = $1) except `positionTokenUnits` (token units, 1_000_000 = 1\n * token) which is numerically equal to its settlement USDC value.\n */\nexport interface MaxGainInput {\n positionTokenUnits: number;\n notionalUsdcUnits: number;\n expectedOpenTradingFeeUsdcUnits: number;\n originationFeeUsdcUnits: number;\n}\n\n/**\n * Result of {@link computeMaxGain}. All amounts in USDC units (1_000_000 = $1).\n * Gains are profit OVER principal (notional = loan + collateral) and may be\n * negative — they are signed and never clamped.\n */\nexport interface MaxGainResult {\n fullValueOnWinUsdcUnits: number;\n grossMaxGainUsdcUnits: number;\n netMaxGainUsdcUnits: number;\n}\n\n/**\n * Maximum gain (gross and net) for a winning settlement exit — profit over\n * principal. On a win each position token redeems at $1 (CTF settlement), so\n * the full value equals `positionTokenUnits` and there is NO exit trading fee.\n *\n * ```\n * fullValueOnWin = positionTokenUnits\n * grossMaxGain = fullValueOnWin - notionalUsdcUnits\n * netMaxGain = grossMaxGain - expectedOpenTradingFee - originationFee\n * ```\n *\n * Lifetime fees are excluded, so net max gain is an upper bound (a position held\n * to settlement accrues lifetime fees that reduce realized profit). Gains are\n * signed — a position whose principal exceeds its winning redemption value\n * yields a negative max gain.\n */\nexport function computeMaxGain(input: MaxGainInput): MaxGainResult {\n const fullValueOnWinUsdcUnits = input.positionTokenUnits;\n const grossMaxGainUsdcUnits = fullValueOnWinUsdcUnits - input.notionalUsdcUnits;\n const netMaxGainUsdcUnits =\n grossMaxGainUsdcUnits - input.expectedOpenTradingFeeUsdcUnits - input.originationFeeUsdcUnits;\n\n return {\n fullValueOnWinUsdcUnits,\n grossMaxGainUsdcUnits,\n netMaxGainUsdcUnits,\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["/Users/alexemery/bl/dimes-sdk/dist/index.cjs","../src/client/dimes-client.ts","../src/client/http.ts","../src/client/auth.ts","../src/types/aliases.ts","../src/utils/eligibility.ts","../src/utils/leverage.ts","../src/utils/fees.ts","../src/utils/max-gain.ts","../src/utils/partial-close.ts"],"names":["BPS_DIVISOR"],"mappings":"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A;AAEqB,EAAA;AACD,EAAA;AAEV,EAAA;AAC7D;AAW6F;AACnE,EAAA;AACf,IAAA;AACT,EAAA;AACwC,EAAA;AACX,EAAA;AACwBA,EAAAA;AACY,EAAA;AACnE;AAqBE;AAE+BA,EAAAA;AACZ,EAAA;AACV,IAAA;AACT,EAAA;AACuD,EAAA;AACRA,EAAAA;AACjD;AAyCmE;AAC3B,EAAA;AACwB,EAAA;AAE9B,EAAA;AAEzB,EAAA;AACL,IAAA;AACA,IAAA;AACA,IAAA;AACF,EAAA;AACF;AR8YyE;AACA;ASviBrD;AACI;AAEU;AAEG;AAEK;AAE+C;AACtB,EAAA;AAEvB,EAAA;AAC5C;AAEqG;AACtE,EAAA;AACpB,IAAA;AACT,EAAA;AAEqE,EAAA;AACvE;AAEsF;AACxC,EAAA;AACA,EAAA;AAES,EAAA;AACvD;AAEsF;AACnE,EAAA;AACR,IAAA;AACT,EAAA;AAEiB,EAAA;AACR,IAAA;AACT,EAAA;AAEO,EAAA;AACT;AT6hByE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"/Users/alexemery/bl/dimes-sdk/dist/index.cjs","sourcesContent":[null,"import { decamelizeKeys } from \"humps\";\nimport type {\n ContractInfo,\n CreateQuoteParams,\n CustomerLimit,\n FeeRates,\n FeeReport,\n FeeReportParams,\n Market,\n Paginated,\n Position,\n PositionTransactions,\n Quote,\n} from \"../types\";\nimport type { AuthProvider } from \"./auth\";\nimport { HttpClient } from \"./http\";\n\nexport interface DimesClientOptions {\n baseUrl?: string;\n auth: AuthProvider;\n fetch?: typeof globalThis.fetch;\n maxRetries?: number;\n maxRetryDelayMs?: number;\n}\n\nexport type MarketSort = \"ticker_asc\" | \"depth_desc\" | \"discovered_at_desc\";\n\nexport type MarketExpand = \"total_count\" | \"prices\";\n\nexport type PositionStatus = \"open\" | \"unwinding\" | \"pending\" | \"closing\" | \"closed\" | \"settled\" | \"liquidated\";\n\nexport interface GetMarketsParams {\n limit?: number;\n startingAfter?: string;\n endingBefore?: string;\n expand?: MarketExpand[];\n sort?: MarketSort;\n acceptingNewPositions?: boolean;\n category?: string;\n status?: string;\n provider?: \"polymarket\" | \"kalshi\";\n}\n\nexport interface SearchMarketsParams extends GetMarketsParams {\n query: string;\n}\n\nexport interface GetPositionsParams {\n limit?: number;\n startingAfter?: string;\n endingBefore?: string;\n expand?: string[];\n sortBy?: \"created_at\" | \"closed_at\";\n sortDirection?: \"asc\" | \"desc\";\n status?: PositionStatus;\n state?: \"active\" | \"inactive\";\n}\n\nexport interface GetFeeRatesParams {\n ticker?: string;\n}\n\nfunction buildQueryFromEntries(entries: [string, unknown][]): string {\n const filtered = entries.filter(([, v]) => v !== undefined && v !== null);\n if (filtered.length === 0) {\n return \"\";\n }\n\n const search = new URLSearchParams();\n for (const [key, value] of filtered) {\n search.set(\n key.replace(/[A-Z]/g, (c) => `_${c.toLowerCase()}`),\n String(value),\n );\n }\n\n return `?${search.toString()}`;\n}\n\nexport class DimesClient {\n /** The resolved API base URL. Useful for constructing WebSocket clients against the same host. */\n public readonly baseUrl: string;\n\n private readonly http: HttpClient;\n\n private readonly auth: AuthProvider;\n\n constructor(options: DimesClientOptions) {\n this.baseUrl = options.baseUrl ?? \"https://api.dimes.fi\";\n this.auth = options.auth;\n this.http = new HttpClient({\n baseUrl: this.baseUrl,\n auth: options.auth,\n fetch: options.fetch,\n maxRetries: options.maxRetries,\n maxRetryDelayMs: options.maxRetryDelayMs,\n });\n }\n\n /** Synchronously return the current cached JWT, or null. Returns null if the auth provider does not expose one. */\n public getToken(): string | null {\n return this.auth.getToken?.() ?? null;\n }\n\n public async getMarkets(params?: GetMarketsParams): Promise<Paginated<Market>> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.request<Paginated<Market>>(`/v1/prediction-markets/markets${query}`);\n }\n\n public async getMarket(ticker: string): Promise<Market> {\n return this.http.request<Market>(`/v1/prediction-markets/markets/${encodeURIComponent(ticker)}`);\n }\n\n public async searchMarkets(params: SearchMarketsParams): Promise<Paginated<Market>> {\n const query = buildQueryFromEntries(Object.entries(params));\n return this.http.request<Paginated<Market>>(`/v1/prediction-markets/markets/search${query}`);\n }\n\n public async createDraftQuote(params: CreateQuoteParams): Promise<Quote> {\n return this.http.request<Quote>(\"/v1/prediction-markets/draft-quotes\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n\n public async promoteDraftQuote(draftId: string): Promise<Quote> {\n return this.http.request<Quote>(`/v1/prediction-markets/promoted-quotes/${encodeURIComponent(draftId)}`, {\n method: \"POST\",\n });\n }\n\n public async createQuote(params: CreateQuoteParams): Promise<Quote> {\n return this.http.request<Quote>(\"/v1/prediction-markets/quotes\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n\n public async getPositions(params?: GetPositionsParams): Promise<Position[]> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.requestList<Position>(`/v1/prediction-markets/positions${query}`);\n }\n\n public async getPositionTransactions(positionId: string): Promise<PositionTransactions> {\n return this.http.request<PositionTransactions>(\n `/v1/prediction-markets/positions/${encodeURIComponent(positionId)}/transactions`,\n );\n }\n\n public async cancelPosition(positionId: string): Promise<void> {\n await this.http.request<unknown>(`/v1/prediction-markets/positions/${encodeURIComponent(positionId)}`, {\n method: \"DELETE\",\n });\n }\n\n public async getContractInfo(): Promise<ContractInfo> {\n return this.http.request<ContractInfo>(\"/v1/prediction-markets/contract-info\");\n }\n\n public async getUserLimits(): Promise<CustomerLimit> {\n return this.http.request<CustomerLimit>(\"/v1/prediction-markets/user-limits\");\n }\n\n public async getPartnerLimits(): Promise<CustomerLimit> {\n return this.http.request<CustomerLimit>(\"/v1/prediction-markets/partner-limits\");\n }\n\n /**\n * @deprecated The `/limits` path was never served by the API. Use {@link getUserLimits}\n * (JWT/user context) or {@link getPartnerLimits} (API-key/partner context). This alias\n * forwards to `getUserLimits` and will be removed in a future major.\n */\n public async getLimits(): Promise<CustomerLimit> {\n return this.getUserLimits();\n }\n\n public async getFeeRates(params?: GetFeeRatesParams): Promise<FeeRates> {\n const query = params ? buildQueryFromEntries(Object.entries(params)) : \"\";\n return this.http.request<FeeRates>(`/v1/prediction-markets/fee-rates${query}`);\n }\n\n public async getFeeReport(params: FeeReportParams): Promise<FeeReport> {\n return this.http.request<FeeReport>(\"/v1/prediction-markets/fee-reports\", {\n method: \"POST\",\n body: JSON.stringify(decamelizeKeys(params)),\n });\n }\n}\n","import { camelizeKeys, type Camelized } from \"humps\";\nimport { DimesApiError } from \"../errors/dimes-error\";\nimport type { AuthProvider } from \"./auth\";\n\nexport type { Camelized };\n\ninterface ApiErrorBody {\n error?: {\n type?: string;\n code?: string;\n message: string;\n params?: Record<string, unknown>;\n };\n}\n\nconst HTTP_UNAUTHORIZED = 401;\nconst HTTP_TOO_MANY_REQUESTS = 429;\nconst DEFAULT_MAX_RETRIES = 3;\nconst DEFAULT_MAX_RETRY_DELAY_MS = 60_000;\nconst DEFAULT_RETRY_DELAY_MS = 1000;\nconst BACKOFF_FACTOR = 2;\nconst MS_PER_SECOND = 1000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction parseRetryAfterMs(header: string | null, attempt: number): number {\n const fallbackMs = DEFAULT_RETRY_DELAY_MS * BACKOFF_FACTOR ** attempt;\n if (!header) {\n return fallbackMs;\n }\n\n const seconds = Number(header);\n if (!Number.isNaN(seconds) && seconds >= 0) {\n return seconds * MS_PER_SECOND;\n }\n\n const dateMs = Date.parse(header);\n if (!Number.isNaN(dateMs)) {\n return Math.max(0, dateMs - Date.now());\n }\n\n return fallbackMs;\n}\n\n// eslint-disable-next-line complexity\nasync function throwFromResponse(response: Response): Promise<never> {\n const rawBody = await response.text();\n let parsed: ApiErrorBody | null;\n try {\n parsed = rawBody ? (JSON.parse(rawBody) as ApiErrorBody) : null;\n } catch {\n parsed = null;\n }\n\n const code = parsed?.error?.code ?? \"unknown_error\";\n const type = parsed?.error?.type ?? null;\n const message = parsed?.error?.message ?? `API error ${response.status}`;\n const rawParams = parsed?.error?.params;\n const params = rawParams ? (camelizeKeys(rawParams) as Record<string, unknown>) : null;\n\n throw new DimesApiError({ status: response.status, code, type, message, params });\n}\n\nexport interface HttpClientOptions {\n baseUrl: string;\n auth: AuthProvider;\n fetch?: typeof globalThis.fetch;\n maxRetries?: number;\n maxRetryDelayMs?: number;\n}\n\nexport class HttpClient {\n private readonly baseUrl: string;\n\n private readonly auth: AuthProvider;\n\n private readonly fetchFn: typeof globalThis.fetch;\n\n private readonly maxRetries: number;\n\n private readonly maxRetryDelayMs: number;\n\n constructor(options: HttpClientOptions) {\n this.baseUrl = options.baseUrl;\n this.auth = options.auth;\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;\n this.maxRetryDelayMs = options.maxRetryDelayMs ?? DEFAULT_MAX_RETRY_DELAY_MS;\n }\n\n public async request<T>(path: string, options?: RequestInit): Promise<T> {\n const authHeaders = await this.auth.getHeaders();\n const headers: Record<string, string> = {\n \"Content-Type\": \"application/json\",\n ...authHeaders,\n ...(options?.headers as Record<string, string> | undefined),\n };\n\n let response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers,\n });\n\n if (response.status === HTTP_UNAUTHORIZED && this.auth.refresh) {\n await this.auth.refresh();\n const retryHeaders = await this.auth.getHeaders();\n response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers: { ...headers, ...retryHeaders },\n });\n }\n\n for (let attempt = 0; response.status === HTTP_TOO_MANY_REQUESTS && attempt < this.maxRetries; attempt++) {\n const delayMs = parseRetryAfterMs(response.headers.get(\"retry-after\"), attempt);\n if (delayMs > this.maxRetryDelayMs) {\n break;\n }\n await sleep(delayMs);\n const freshAuth = await this.auth.getHeaders();\n response = await this.fetchFn(`${this.baseUrl}${path}`, {\n ...options,\n headers: { ...headers, ...freshAuth },\n });\n }\n\n if (!response.ok) {\n await throwFromResponse(response);\n }\n\n return camelizeKeys(await response.json()) as T;\n }\n\n public async requestList<T>(path: string, options?: RequestInit): Promise<T[]> {\n const result = await this.request<{ data: T[] }>(path, options);\n return result.data;\n }\n}\n","import { DimesError } from \"../errors/dimes-error\";\n\nexport interface AuthProvider {\n getHeaders(): Promise<Record<string, string>>;\n refresh?(): Promise<void>;\n /** Synchronously return the current cached JWT, or null if not yet obtained. Used by the WebSocket clients. */\n getToken?(): string | null;\n}\n\ninterface TokenResponse {\n token: string;\n expires_at: string;\n}\n\nexport interface ApiKeyAuthOptions {\n apiKey: string;\n walletAddress: string;\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport class ApiKeyAuth implements AuthProvider {\n private jwt: string | null = null;\n\n private expiresAt = 0;\n\n private refreshPromise: Promise<void> | null = null;\n\n private readonly apiKey: string;\n\n private readonly walletAddress: string;\n\n private readonly baseUrl: string;\n\n private readonly fetchFn: typeof globalThis.fetch;\n\n constructor(options: ApiKeyAuthOptions) {\n this.apiKey = options.apiKey;\n this.walletAddress = options.walletAddress;\n this.baseUrl = options.baseUrl ?? \"https://api.dimes.fi\";\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getHeaders(): Promise<Record<string, string>> {\n const TOKEN_EXPIRY_BUFFER_MS = 30_000;\n if (!this.jwt || Date.now() >= this.expiresAt - TOKEN_EXPIRY_BUFFER_MS) {\n await this.refresh();\n }\n return { Authorization: `Bearer ${this.jwt}` };\n }\n\n public getToken(): string | null {\n return this.jwt;\n }\n\n public async refresh(): Promise<void> {\n if (this.refreshPromise) {\n await this.refreshPromise;\n return;\n }\n\n this.refreshPromise = this.fetchToken();\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async fetchToken(): Promise<void> {\n const response = await this.fetchFn(`${this.baseUrl}/v1/prediction-markets/tokens`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n Authorization: `Api-Key ${this.apiKey}`,\n },\n body: JSON.stringify({ wallet_address: this.walletAddress }),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new DimesError(\"auth_token_failed\", `Failed to obtain auth token (${response.status}): ${text}`);\n }\n\n const data = (await response.json()) as TokenResponse;\n this.jwt = data.token;\n this.expiresAt = new Date(data.expires_at).getTime();\n }\n}\n\nexport interface JwtAuthOptions {\n tokenUrl: string;\n fetch?: typeof globalThis.fetch;\n}\n\nexport class JwtAuth implements AuthProvider {\n private readonly fetchFn: typeof globalThis.fetch;\n\n private readonly tokenUrl: string;\n\n private jwt: string | null = null;\n\n private expiresAt = 0;\n\n private refreshPromise: Promise<void> | null = null;\n\n constructor(options: JwtAuthOptions) {\n this.tokenUrl = options.tokenUrl;\n this.fetchFn = options.fetch ?? globalThis.fetch.bind(globalThis);\n }\n\n public async getHeaders(): Promise<Record<string, string>> {\n const TOKEN_EXPIRY_BUFFER_MS = 30_000;\n if (!this.jwt || Date.now() >= this.expiresAt - TOKEN_EXPIRY_BUFFER_MS) {\n await this.refresh();\n }\n return { Authorization: `Bearer ${this.jwt}` };\n }\n\n public getToken(): string | null {\n return this.jwt;\n }\n\n public async refresh(): Promise<void> {\n if (this.refreshPromise) {\n await this.refreshPromise;\n return;\n }\n\n this.refreshPromise = this.fetchToken();\n try {\n await this.refreshPromise;\n } finally {\n this.refreshPromise = null;\n }\n }\n\n private async fetchToken(): Promise<void> {\n const response = await this.fetchFn(this.tokenUrl);\n\n if (!response.ok) {\n const text = await response.text();\n throw new DimesError(\n \"auth_token_failed\",\n `Failed to fetch token from ${this.tokenUrl} (${response.status}): ${text}`,\n );\n }\n\n const data = (await response.json()) as TokenResponse;\n this.jwt = data.token;\n this.expiresAt = new Date(data.expires_at).getTime();\n }\n}\n","import type { CamelizeKeys } from \"./camelize\";\nimport type { components } from \"./generated\";\n\ntype Raw = components[\"schemas\"];\n\nexport type Market = CamelizeKeys<Raw[\"CustomerMarket\"]>;\n\nexport type MarketLeverage = CamelizeKeys<Raw[\"CustomerLeverage\"]>;\n\nexport type MarketMaxLeveragePerNotional = CamelizeKeys<Raw[\"CustomerMaxMarketLeveragePerNotional\"]>;\n\nexport type MarketSidedMaxLeveragePerNotional = CamelizeKeys<Raw[\"CustomerSidedMaxMarketLeveragePerNotional\"]>;\n\nexport type MarketSidedEligibility = CamelizeKeys<Raw[\"CustomerSidedEligibility\"]>;\n\nexport type MarketFees = CamelizeKeys<Raw[\"CustomerFees\"]>;\n\nexport type MarketPrices = CamelizeKeys<Raw[\"CustomerMarketPrices\"]>;\n\nexport interface MarketPolymarket {\n conditionId: string | null;\n noTokenId: string;\n slug: string;\n yesTokenId: string;\n}\n\nexport type OriginationTier = CamelizeKeys<Raw[\"CustomerOriginationTier\"]>;\n\nexport type Quote = CamelizeKeys<Raw[\"CustomerOffer\"]>;\n\n/** @deprecated Use {@link Quote}. Back-compat alias; removed in a future major. */\n// eslint-disable-next-line no-restricted-syntax -- intentional deprecated alias\nexport type Offer = Quote;\n\nexport type OpenPosition = CamelizeKeys<Raw[\"CustomerOpenPosition\"]>;\n\nexport type ClosedPosition = CamelizeKeys<Raw[\"CustomerClosedPosition\"]>;\n\nexport type Position = OpenPosition | ClosedPosition;\n\nexport type PositionEntry = CamelizeKeys<Raw[\"CustomerPositionEntry\"]>;\n\nexport type PositionCurrent = CamelizeKeys<Raw[\"CustomerPositionCurrent\"]>;\n\nexport type PositionRisk = CamelizeKeys<Raw[\"CustomerPositionRisk\"]>;\n\nexport type PositionOpenFees = CamelizeKeys<Raw[\"CustomerPositionOpenFees\"]>;\n\nexport type PositionClosedFees = CamelizeKeys<Raw[\"CustomerPositionClosedFees\"]>;\n\nexport type PositionResult = CamelizeKeys<Raw[\"CustomerPositionResult\"]>;\n\nexport type PositionTiming = CamelizeKeys<Raw[\"CustomerPositionTiming\"]>;\n\nexport type PositionFailure = CamelizeKeys<Raw[\"CustomerPositionFailure\"]>;\n\nexport type CloseAttempt = CamelizeKeys<Raw[\"CustomerCloseAttempt\"]>;\n\nexport type PendingOperation = CamelizeKeys<Raw[\"CustomerPendingOperation\"]>;\n\nexport type PositionUnwind = CamelizeKeys<Raw[\"CustomerPositionUnwind\"]>;\n\nexport type PositionUnwindList = CamelizeKeys<Raw[\"CustomerPositionUnwindList\"]>;\n\nexport type PositionTransactionEntry = CamelizeKeys<Raw[\"PositionTransactionEntry\"]>;\n\nexport type PositionTransactionGroup = CamelizeKeys<Raw[\"PositionTransactionGroup\"]>;\n\nexport type PositionTransactions = CamelizeKeys<Raw[\"PositionTransactions\"]>;\n\nexport type ContractInfo = CamelizeKeys<Raw[\"CustomerContractInfo\"]>;\n\nexport type CustomerLimit = CamelizeKeys<Raw[\"CustomerLimit\"]>;\n\nexport type CreateTokenResult = CamelizeKeys<Raw[\"CreateTokenResult\"]>;\n\nexport type FeeRatesOriginationTier = CamelizeKeys<Raw[\"CustomerOriginationFeeTier\"]>;\n\nexport type FeeRatesMarket = CamelizeKeys<Raw[\"CustomerFeeRatesMarket\"]>;\n\nexport type FeeRates = CamelizeKeys<Raw[\"CustomerFeeRates\"]>;\n\nexport type FeeReport = CamelizeKeys<Raw[\"CustomerFeeReport\"]>;\n\nexport interface CreateQuoteParams {\n marketTicker: string;\n effectiveSide: \"yes\" | \"no\";\n leverageBps: number;\n notionalAmountUsdPips: string;\n slippageBps: number;\n pmProvider?: \"polymarket\" | \"kalshi\";\n allowPartialFill?: boolean;\n minFillBps?: number;\n}\n\n/** @deprecated Renamed to {@link CreateQuoteParams}. Kept as an alias for backward compatibility. */\n// eslint-disable-next-line no-restricted-syntax -- intentional deprecated alias\nexport type CreateOfferParams = CreateQuoteParams;\n\nexport interface FeeReportParams {\n marketTicker: string;\n effectiveSide: \"yes\" | \"no\";\n leverageBps: number;\n notionalAmountUsdPips: string;\n entryPriceUsdPips?: string;\n}\n\nexport function isOpenPosition(p: Position): p is OpenPosition {\n return \"current\" in p;\n}\n\nexport function isClosedPosition(p: Position): p is ClosedPosition {\n return \"result\" in p;\n}\n\nexport function leverageMaxBps(lev: MarketLeverage, side: \"yes\" | \"no\"): number {\n const perSide = side === \"yes\" ? lev.maxYesBps : lev.maxNoBps;\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n return perSide ?? lev.maxBps ?? lev.minBps;\n}\n","import type { Market } from \"../types/aliases\";\n\nexport type Side = \"yes\" | \"no\";\n\nexport interface SideEligibility {\n open: boolean;\n reasonCode: string | null;\n}\n\nexport interface SidedEligibility {\n yes: SideEligibility;\n no: SideEligibility;\n}\n\nexport function getSidedEligibility(market: Market): SidedEligibility {\n const sided = market.sidedEligibility;\n return {\n yes: {\n open: sided.yes.acceptingNewPositions,\n reasonCode: sided.yes.rejectionReasonCode ?? null,\n },\n no: {\n open: sided.no.acceptingNewPositions,\n reasonCode: sided.no.rejectionReasonCode ?? null,\n },\n };\n}\n\nexport function defaultSide(eligibility: SidedEligibility): Side | null {\n if (eligibility.yes.open && eligibility.no.open) {\n return \"yes\";\n }\n if (eligibility.yes.open) {\n return \"yes\";\n }\n if (eligibility.no.open) {\n return \"no\";\n }\n return null;\n}\n\nexport function isFullyOpen(eligibility: SidedEligibility): boolean {\n return eligibility.yes.open && eligibility.no.open;\n}\n\nexport function isFullyClosed(eligibility: SidedEligibility): boolean {\n return !eligibility.yes.open && !eligibility.no.open;\n}\n\nexport function rejectionReasonText(code: string | null | undefined): string {\n if (!code) {\n return \"Not eligible for new positions\";\n }\n const stripped = code\n .replace(/^quote_/i, \"\")\n .replaceAll(\"_\", \" \")\n .toLowerCase();\n return stripped.charAt(0).toUpperCase() + stripped.slice(1);\n}\n\nexport function rejectionReasonShort(code: string | null | undefined): string {\n if (!code) {\n return \"unavailable\";\n }\n const stripped = code\n .replace(/^quote_/i, \"\")\n .replace(/_exceeded$/i, \"\")\n .replace(/_too_(low|high)$/i, \"\")\n .replaceAll(\"_\", \" \")\n .toLowerCase();\n return stripped;\n}\n","import { type Market, type MarketMaxLeveragePerNotional, leverageMaxBps } from \"../types/aliases\";\nimport type { Side } from \"./eligibility\";\n\nconst USD_100 = 100;\nconst USD_500 = 500;\nconst USD_1K = 1_000;\nconst USD_10K = 10_000;\nconst BPS_PER_UNIT = 10_000;\n\nconst ANCHORS_USD = [USD_100, USD_500, USD_1K, USD_10K] as const;\n\ntype Anchor = readonly [usd: number, bps: number];\n\nfunction anchorsForSide(market: Market, side: Side): readonly Anchor[] {\n const sided = market.leverage.maxMarketLeveragePerNotional;\n const per: MarketMaxLeveragePerNotional = side === \"yes\" ? sided.yes : sided.no;\n return [\n [ANCHORS_USD[0], per.at100UsdBps],\n [ANCHORS_USD[1], per.at500UsdBps],\n [ANCHORS_USD[2], per.at1000UsdBps],\n [ANCHORS_USD[3], per.at10000UsdBps],\n ];\n}\n\nfunction snapDownToStep(bps: number, stepBps: number, minBps: number): number {\n if (stepBps <= 0) {\n return Math.max(minBps, bps);\n }\n const k = Math.floor((bps - minBps) / stepBps);\n return Math.max(minBps, minBps + Math.max(0, k) * stepBps);\n}\n\nfunction interpolateRawBps(anchors: readonly Anchor[], notionalUsd: number): number {\n const first = anchors[0];\n const last = anchors[anchors.length - 1];\n if (!first || !last) {\n return 0;\n }\n if (notionalUsd <= first[0]) {\n return first[1];\n }\n if (notionalUsd >= last[0]) {\n return last[1];\n }\n for (let i = 1; i < anchors.length; i++) {\n const hi = anchors[i];\n const lo = anchors[i - 1];\n if (!hi || !lo) {\n continue;\n }\n if (notionalUsd <= hi[0]) {\n const t = (notionalUsd - lo[0]) / (hi[0] - lo[0]);\n return lo[1] + (hi[1] - lo[1]) * t;\n }\n }\n return last[1];\n}\n\n/**\n * Maximum leverage in basis points the API will allow at a given notional, per side.\n * Piecewise-linear interpolation across the four anchor points ($100/$500/$1k/$10k).\n * Below $100 uses the $100 cap; above $10k uses the $10k cap. Result is clamped to\n * the absolute side max and rounded down to `stepBps`.\n */\nexport function maxLeverageBpsAtNotional(market: Market, side: Side, notionalUsd: number): number {\n const sideAbsMax = leverageMaxBps(market.leverage, side);\n if (!Number.isFinite(notionalUsd) || notionalUsd <= 0) {\n return sideAbsMax;\n }\n const anchors = anchorsForSide(market, side);\n const raw = interpolateRawBps(anchors, notionalUsd);\n const clamped = Math.min(sideAbsMax, raw);\n return snapDownToStep(clamped, market.leverage.stepBps, market.leverage.minBps);\n}\n\n/**\n * Largest leverage (bps) the user can pick for a given collateral such that the\n * resulting notional still satisfies the per-notional cap.\n *\n * Walks down from the absolute side max in `stepBps` increments and returns the\n * first leverage `lev` where `lev <= maxLeverageBpsAtNotional(collateral * lev / 1)`.\n */\nexport function maxViableLeverageBpsForCollateral(market: Market, side: Side, collateralUsd: number): number {\n const sideAbsMax = leverageMaxBps(market.leverage, side);\n if (!Number.isFinite(collateralUsd) || collateralUsd <= 0) {\n return sideAbsMax;\n }\n\n const step = Math.max(market.leverage.stepBps, 1);\n const minBps = market.leverage.minBps;\n const maxSteps = Math.max(0, Math.floor((sideAbsMax - minBps) / step));\n\n for (let i = maxSteps; i >= 0; i--) {\n const lev = minBps + i * step;\n const notionalUsd = collateralUsd * (lev / BPS_PER_UNIT);\n if (lev <= maxLeverageBpsAtNotional(market, side, notionalUsd)) {\n return lev;\n }\n }\n return minBps;\n}\n","import type { Position, PositionEntry } from \"../types/aliases\";\n\nconst BPS_DIVISOR = 10_000;\n\n/**\n * One leverage-tiered origination-fee rate. `maxLeverageBps` is the inclusive\n * upper bound (in basis points) of leverage covered by this tier; `feeBps` is\n * the protocol origination fee charged for any leverage at or below that bound.\n * Tiers must be supplied in ascending `maxLeverageBps` order; the final tier\n * acts as the catch-all for the highest leverages.\n */\nexport interface OriginationFeeTier {\n maxLeverageBps: number;\n feeBps: number;\n}\n\n/**\n * Resolve the protocol origination fee rate (bps) for a given leverage from an\n * ordered list of leverage tiers. Returns the `feeBps` of the first tier whose\n * `maxLeverageBps >= leverageBps`. If no tier matches (leverage exceeds every\n * bound) the last tier is used as the catch-all. The caller fetches the tiers\n * from the `fee-rates` API; the math is pure.\n *\n * Note: this returns the PROTOCOL rate only — add `partnerOriginationFeeBps`\n * separately (see {@link resolveOriginationFeeBps}).\n */\nexport function resolveProtocolOriginationFeeBps(tiers: readonly OriginationFeeTier[], leverageBps: number): number {\n const lastTier = tiers[tiers.length - 1];\n if (!lastTier) {\n return 0;\n }\n for (const tier of tiers) {\n if (leverageBps <= tier.maxLeverageBps) {\n return tier.feeBps;\n }\n }\n return lastTier.feeBps;\n}\n\n/**\n * Combined origination fee rate (bps) = protocol tier rate + partner add-on.\n * Mirrors the server: the protocol rate is leverage-tiered, the partner rate is\n * a flat add-on supplied by the partner's `fee-rates` config.\n */\nexport function resolveOriginationFeeBps(\n tiers: readonly OriginationFeeTier[],\n leverageBps: number,\n partnerOriginationFeeBps: number,\n): number {\n return resolveProtocolOriginationFeeBps(tiers, leverageBps) + partnerOriginationFeeBps;\n}\n\n/**\n * Origination fee in USDC units (1_000_000 = $1), floored, matching the server:\n * `floor(notionalUsdcUnits * combinedOriginationFeeBps / 10_000)`.\n */\nexport function computeOriginationFeeUsdcUnits(notionalUsdcUnits: number, combinedOriginationFeeBps: number): number {\n return Math.floor((notionalUsdcUnits * combinedOriginationFeeBps) / BPS_DIVISOR);\n}\n\nexport interface OriginationFeeBreakdown {\n totalBps: number;\n totalUsd: number;\n protocolBps: number;\n protocolUsd: number;\n partnerBps: number;\n partnerUsd: number;\n}\n\nfunction getEntry(position: Position): PositionEntry {\n return position.entry;\n}\n\nexport function getOriginationFeeBreakdown(position: Position): OriginationFeeBreakdown {\n const entry = getEntry(position);\n const totalBps = entry.originationFeeBps;\n const totalUsd = Number(entry.originationFeeUsd) || 0;\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const protocolBps = entry.protocolOriginationFeeBps ?? totalBps;\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const partnerBps = entry.partnerOriginationFeeBps ?? 0;\n\n const share = totalBps > 0 ? protocolBps / totalBps : 1;\n const protocolUsd = totalUsd * share;\n const partnerUsd = totalUsd - protocolUsd;\n\n return {\n totalBps,\n totalUsd,\n protocolBps,\n protocolUsd,\n partnerBps,\n partnerUsd,\n };\n}\n","const BPS_DIVISOR = 10_000;\nconst USDC_UNITS_PER_USD = 1_000_000;\nconst TOKEN_UNITS_PER_TOKEN = 1_000_000;\n\n/**\n * Inputs to the Polymarket trading-fee curve. All rates are basis points; the\n * caller fetches `feeRateBps`, `feeExponent`, and `builderTakerFeeRateBps` from\n * the `fee-rates` API (snapshotted server-side as `Quote.polymarketFeeRateBps`\n * / `polymarketFeeExponent` / `polymarketBuilderFeeRateBps`).\n */\nexport interface PolymarketTradingFeeInput {\n notionalUsdcUnits: number;\n priceUsd: number;\n feeRateBps: number;\n feeExponent?: number;\n builderTakerFeeRateBps?: number;\n}\n\n/**\n * Polymarket open/close trading fee in USDC units (1_000_000 = $1).\n *\n * Matches the canonical formula (docs: position-math/09-exchange-trading-fees):\n *\n * ```\n * protocol = feeRateBps / 10_000 * C * (p * (1 - p)) ^ feeExponent * 1e6\n * builder = builderTakerFeeRateBps / 10_000 * notionalUsdc\n * fee = ceil(protocol + builder)\n * ```\n *\n * where `C` = contracts = notionalUsd / p, `p` = price in dollars (0..1). The\n * curvature term peaks at p = 0.5 and is symmetric; the flat builder term does\n * not shrink at price extremes. `feeExponent` defaults to 1 (standard quadratic\n * curvature); some markets use 0 (flat `feeRate * C`). `builderTakerFeeRateBps`\n * defaults to 0 (Bloom's own operator builder code is always 0-fee).\n */\nexport function computePolymarketTradingFee(input: PolymarketTradingFeeInput): number {\n const { notionalUsdcUnits, priceUsd, feeRateBps } = input;\n const feeExponent = input.feeExponent ?? 1;\n const builderTakerFeeRateBps = input.builderTakerFeeRateBps ?? 0;\n\n if (priceUsd <= 0) {\n return 0;\n }\n\n const notionalUsd = notionalUsdcUnits / USDC_UNITS_PER_USD;\n const contracts = notionalUsd / priceUsd;\n const curvature = (priceUsd * (1 - priceUsd)) ** feeExponent;\n\n const protocolFeeUsdcUnits = (feeRateBps / BPS_DIVISOR) * contracts * curvature * USDC_UNITS_PER_USD;\n const builderFeeUsdcUnits = (builderTakerFeeRateBps / BPS_DIVISOR) * notionalUsdcUnits;\n\n return Math.ceil(protocolFeeUsdcUnits + builderFeeUsdcUnits);\n}\n\n/**\n * Expected position size in token units (1_000_000 = 1 whole token) for a given\n * notional and entry price, optionally discounted by slippage. A token redeems\n * at $1 on a win, so token units are numerically equal to USDC units.\n *\n * `tokens = notionalUsd / entryPrice`, then scaled down by `slippageBps`.\n * Floored to whole token units. This is an at-quote estimate of the quote's\n * `minExpectedPositionTokenUnits`; the server is authoritative.\n */\nexport function expectedPositionTokenUnits(notionalUsdcUnits: number, entryPriceUsd: number, slippageBps = 0): number {\n if (entryPriceUsd <= 0) {\n return 0;\n }\n const notionalUsd = notionalUsdcUnits / USDC_UNITS_PER_USD;\n const tokens = notionalUsd / entryPriceUsd;\n const slippageFactor = (BPS_DIVISOR - slippageBps) / BPS_DIVISOR;\n return Math.floor(tokens * slippageFactor * TOKEN_UNITS_PER_TOKEN);\n}\n\n/**\n * Estimated base liquidation price (deterministic, at-entry closed form).\n *\n * **This is an estimate, not the server's TWAP solver result.** It computes the\n * at-entry base liquidation price (F = 0, no safety margin) per the docs\n * (position-math/04-liquidation):\n *\n * ```\n * P_liq_base = entryPrice * (L - 1) / L * (1 + liquidationFeeBps / 10_000)\n * ```\n *\n * where `L = leverageBps / 10_000`. At 1x leverage the borrowed capital is zero\n * so the liquidation price is zero. `entryPriceUsd` is in dollars; the result is\n * in the same units. The live server liquidation price drifts above this as the\n * lifetime fee `F` accrues and includes a safety margin.\n */\nexport function estimateLiquidationPrice(\n entryPriceUsd: number,\n leverageBps: number,\n liquidationFeeBps: number,\n): number {\n const leverage = leverageBps / BPS_DIVISOR;\n if (leverage <= 1) {\n return 0;\n }\n const loanPerToken = entryPriceUsd * ((leverage - 1) / leverage);\n return loanPerToken * (1 + liquidationFeeBps / BPS_DIVISOR);\n}\n\n/**\n * Inputs for {@link computeMaxGain}. All amounts are in USDC units\n * (1_000_000 = $1) except `positionTokenUnits` (token units, 1_000_000 = 1\n * token) which is numerically equal to its settlement USDC value.\n */\nexport interface MaxGainInput {\n positionTokenUnits: number;\n notionalUsdcUnits: number;\n expectedOpenTradingFeeUsdcUnits: number;\n originationFeeUsdcUnits: number;\n}\n\n/**\n * Result of {@link computeMaxGain}. All amounts in USDC units (1_000_000 = $1).\n * Gains are profit OVER principal (notional = loan + collateral) and may be\n * negative — they are signed and never clamped.\n */\nexport interface MaxGainResult {\n fullValueOnWinUsdcUnits: number;\n grossMaxGainUsdcUnits: number;\n netMaxGainUsdcUnits: number;\n}\n\n/**\n * Maximum gain (gross and net) for a winning settlement exit — profit over\n * principal. On a win each position token redeems at $1 (CTF settlement), so\n * the full value equals `positionTokenUnits` and there is NO exit trading fee.\n *\n * ```\n * fullValueOnWin = positionTokenUnits\n * grossMaxGain = fullValueOnWin - notionalUsdcUnits\n * netMaxGain = grossMaxGain - expectedOpenTradingFee - originationFee\n * ```\n *\n * Lifetime fees are excluded, so net max gain is an upper bound (a position held\n * to settlement accrues lifetime fees that reduce realized profit). Gains are\n * signed — a position whose principal exceeds its winning redemption value\n * yields a negative max gain.\n */\nexport function computeMaxGain(input: MaxGainInput): MaxGainResult {\n const fullValueOnWinUsdcUnits = input.positionTokenUnits;\n const grossMaxGainUsdcUnits = fullValueOnWinUsdcUnits - input.notionalUsdcUnits;\n const netMaxGainUsdcUnits =\n grossMaxGainUsdcUnits - input.expectedOpenTradingFeeUsdcUnits - input.originationFeeUsdcUnits;\n\n return {\n fullValueOnWinUsdcUnits,\n grossMaxGainUsdcUnits,\n netMaxGainUsdcUnits,\n };\n}\n","const BPS_DIVISOR = 10_000;\nconst BPS_DIVISOR_BIG = 10_000n;\n\nexport const tokenUnitsPerToken = 1_000_000n;\n\nexport const partialCloseMinTokens = 5n;\n\nexport const partialCloseMinFractionBps = 2_000n;\n\nexport function closeTokenUnitsForPct(currentTokenUnits: bigint, pctBps: number): bigint {\n const clampedBps = BigInt(Math.max(0, Math.min(BPS_DIVISOR, Math.round(pctBps))));\n\n return (currentTokenUnits * clampedBps) / BPS_DIVISOR_BIG;\n}\n\nexport function pctBpsForCloseTokenUnits(currentTokenUnits: bigint, closeTokenUnits: bigint): number {\n if (currentTokenUnits <= 0n) {\n return 0;\n }\n\n return Number((closeTokenUnits * BPS_DIVISOR_BIG) / currentTokenUnits);\n}\n\nexport function fallbackMinPartialCloseTokenUnits(originalTokenUnits: bigint): bigint {\n const floorTokens = partialCloseMinTokens * tokenUnitsPerToken;\n const floorFraction = (originalTokenUnits * partialCloseMinFractionBps) / BPS_DIVISOR_BIG;\n\n return floorFraction > floorTokens ? floorFraction : floorTokens;\n}\n\nexport function clampCloseTokenUnits(value: bigint, min: bigint, max: bigint): bigint {\n if (value < min) {\n return min;\n }\n\n if (value > max) {\n return max;\n }\n\n return value;\n}\n"]}
|