@pulsepairs/sdk 0.2.0
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/README.md +264 -0
- package/dist/accountKit.d.ts +265 -0
- package/dist/accountKit.js +638 -0
- package/dist/approve.d.ts +46 -0
- package/dist/approve.js +69 -0
- package/dist/auth.d.ts +77 -0
- package/dist/auth.js +76 -0
- package/dist/eip712.d.ts +216 -0
- package/dist/eip712.js +229 -0
- package/dist/http.d.ts +46 -0
- package/dist/http.js +120 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +15 -0
- package/dist/types.d.ts +235 -0
- package/dist/types.js +20 -0
- package/dist/ws.d.ts +118 -0
- package/dist/ws.js +224 -0
- package/package.json +62 -0
package/dist/http.d.ts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ApiConfig, Balance, CancelOrderBody, MarketDetail, MarketListItem, OrderBookFull, OrdersResponse, PairSymbol, PostOrderBody, Position, Stats, Trade, Version } from "./types.js";
|
|
2
|
+
export declare class UpDownHttpClient {
|
|
3
|
+
private readonly baseUrl;
|
|
4
|
+
constructor(baseUrl: string);
|
|
5
|
+
getVersion(): Promise<Version>;
|
|
6
|
+
getHealth(): Promise<{
|
|
7
|
+
status: string;
|
|
8
|
+
relayer?: string;
|
|
9
|
+
uptime?: number;
|
|
10
|
+
}>;
|
|
11
|
+
getConfig(): Promise<ApiConfig>;
|
|
12
|
+
getMarkets(opts?: {
|
|
13
|
+
timeframe?: 300 | 900 | 3600;
|
|
14
|
+
pair?: PairSymbol;
|
|
15
|
+
}): Promise<MarketListItem[]>;
|
|
16
|
+
getMarket(address: string): Promise<MarketDetail>;
|
|
17
|
+
getOrderbook(marketAddress: string): Promise<OrderBookFull>;
|
|
18
|
+
getBalance(wallet: string): Promise<Balance>;
|
|
19
|
+
getPositions(wallet: string): Promise<Position[]>;
|
|
20
|
+
getTrades(wallet: string, opts?: {
|
|
21
|
+
limit?: number;
|
|
22
|
+
offset?: number;
|
|
23
|
+
}): Promise<Trade[]>;
|
|
24
|
+
getOrders(wallet: string, opts?: {
|
|
25
|
+
status?: string[];
|
|
26
|
+
limit?: number;
|
|
27
|
+
offset?: number;
|
|
28
|
+
}): Promise<OrdersResponse>;
|
|
29
|
+
getStats(): Promise<Stats>;
|
|
30
|
+
postOrder(body: PostOrderBody): Promise<{
|
|
31
|
+
id: string;
|
|
32
|
+
status: string;
|
|
33
|
+
market: string;
|
|
34
|
+
option: number;
|
|
35
|
+
side: string;
|
|
36
|
+
type: string;
|
|
37
|
+
price: number;
|
|
38
|
+
amount: string;
|
|
39
|
+
createdAt: number | string;
|
|
40
|
+
}>;
|
|
41
|
+
cancelOrder(orderId: string, body: CancelOrderBody): Promise<{
|
|
42
|
+
id: string;
|
|
43
|
+
status: "CANCEL_PENDING" | string;
|
|
44
|
+
}>;
|
|
45
|
+
}
|
|
46
|
+
export declare function wsUrlFromHttpBase(httpBase: string): string;
|
package/dist/http.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
function buildUrl(base, path, query) {
|
|
2
|
+
const b = base.replace(/\/$/, "");
|
|
3
|
+
const p = path.startsWith("/") ? path : `/${path}`;
|
|
4
|
+
const u = new URL(`${b}${p}`);
|
|
5
|
+
if (query) {
|
|
6
|
+
for (const [k, v] of Object.entries(query)) {
|
|
7
|
+
if (v !== undefined && v !== "")
|
|
8
|
+
u.searchParams.set(k, String(v));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return u.toString();
|
|
12
|
+
}
|
|
13
|
+
async function parseJson(res) {
|
|
14
|
+
const text = await res.text();
|
|
15
|
+
if (!res.ok) {
|
|
16
|
+
let msg = text;
|
|
17
|
+
try {
|
|
18
|
+
const j = JSON.parse(text);
|
|
19
|
+
if (j.error)
|
|
20
|
+
msg = j.error;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
/* ignore */
|
|
24
|
+
}
|
|
25
|
+
throw new Error(msg || res.statusText);
|
|
26
|
+
}
|
|
27
|
+
if (!text)
|
|
28
|
+
return undefined;
|
|
29
|
+
return JSON.parse(text);
|
|
30
|
+
}
|
|
31
|
+
export class UpDownHttpClient {
|
|
32
|
+
baseUrl;
|
|
33
|
+
constructor(baseUrl) {
|
|
34
|
+
this.baseUrl = baseUrl;
|
|
35
|
+
}
|
|
36
|
+
async getVersion() {
|
|
37
|
+
const res = await fetch(buildUrl(this.baseUrl, "/version"));
|
|
38
|
+
return parseJson(res);
|
|
39
|
+
}
|
|
40
|
+
async getHealth() {
|
|
41
|
+
const res = await fetch(buildUrl(this.baseUrl, "/health"));
|
|
42
|
+
return parseJson(res);
|
|
43
|
+
}
|
|
44
|
+
async getConfig() {
|
|
45
|
+
const res = await fetch(buildUrl(this.baseUrl, "/config"));
|
|
46
|
+
return parseJson(res);
|
|
47
|
+
}
|
|
48
|
+
async getMarkets(opts) {
|
|
49
|
+
const res = await fetch(buildUrl(this.baseUrl, "/markets", {
|
|
50
|
+
timeframe: opts?.timeframe,
|
|
51
|
+
pair: opts?.pair,
|
|
52
|
+
}));
|
|
53
|
+
return parseJson(res);
|
|
54
|
+
}
|
|
55
|
+
async getMarket(address) {
|
|
56
|
+
const res = await fetch(buildUrl(this.baseUrl, `/markets/${encodeURIComponent(address)}`));
|
|
57
|
+
return parseJson(res);
|
|
58
|
+
}
|
|
59
|
+
async getOrderbook(marketAddress) {
|
|
60
|
+
const res = await fetch(buildUrl(this.baseUrl, `/orderbook/${encodeURIComponent(marketAddress)}`));
|
|
61
|
+
return parseJson(res);
|
|
62
|
+
}
|
|
63
|
+
async getBalance(wallet) {
|
|
64
|
+
const res = await fetch(buildUrl(this.baseUrl, `/balance/${wallet}`));
|
|
65
|
+
return parseJson(res);
|
|
66
|
+
}
|
|
67
|
+
async getPositions(wallet) {
|
|
68
|
+
const res = await fetch(buildUrl(this.baseUrl, `/positions/${wallet}`));
|
|
69
|
+
return parseJson(res);
|
|
70
|
+
}
|
|
71
|
+
async getTrades(wallet, opts) {
|
|
72
|
+
const res = await fetch(buildUrl(this.baseUrl, `/trades/${wallet}`, {
|
|
73
|
+
limit: opts?.limit,
|
|
74
|
+
offset: opts?.offset,
|
|
75
|
+
}));
|
|
76
|
+
return parseJson(res);
|
|
77
|
+
}
|
|
78
|
+
async getOrders(wallet, opts) {
|
|
79
|
+
const params = new URLSearchParams();
|
|
80
|
+
if (opts?.limit != null)
|
|
81
|
+
params.set("limit", String(opts.limit));
|
|
82
|
+
if (opts?.offset != null)
|
|
83
|
+
params.set("offset", String(opts.offset));
|
|
84
|
+
if (opts?.status)
|
|
85
|
+
for (const s of opts.status)
|
|
86
|
+
params.append("status", s);
|
|
87
|
+
const url = buildUrl(this.baseUrl, `/orders/${wallet}`) +
|
|
88
|
+
(params.toString() ? `?${params.toString()}` : "");
|
|
89
|
+
const res = await fetch(url);
|
|
90
|
+
return parseJson(res);
|
|
91
|
+
}
|
|
92
|
+
async getStats() {
|
|
93
|
+
const res = await fetch(buildUrl(this.baseUrl, "/stats"));
|
|
94
|
+
return parseJson(res);
|
|
95
|
+
}
|
|
96
|
+
async postOrder(body) {
|
|
97
|
+
const res = await fetch(buildUrl(this.baseUrl, "/orders"), {
|
|
98
|
+
method: "POST",
|
|
99
|
+
headers: { "Content-Type": "application/json" },
|
|
100
|
+
body: JSON.stringify(body),
|
|
101
|
+
});
|
|
102
|
+
return parseJson(res);
|
|
103
|
+
}
|
|
104
|
+
async cancelOrder(orderId, body) {
|
|
105
|
+
const res = await fetch(buildUrl(this.baseUrl, `/orders/${orderId}`), {
|
|
106
|
+
method: "DELETE",
|
|
107
|
+
headers: { "Content-Type": "application/json" },
|
|
108
|
+
body: JSON.stringify(body),
|
|
109
|
+
});
|
|
110
|
+
return parseJson(res);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export function wsUrlFromHttpBase(httpBase) {
|
|
114
|
+
const u = new URL(httpBase);
|
|
115
|
+
u.protocol = u.protocol === "https:" ? "wss:" : "ws:";
|
|
116
|
+
u.pathname = "/stream";
|
|
117
|
+
u.search = "";
|
|
118
|
+
u.hash = "";
|
|
119
|
+
return u.toString();
|
|
120
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
|
|
2
|
+
export { UpDownWsClient, type UpDownWsMessage, type SubscribePayload, type WsAuthCredentials, type ConnectAuthedOptions, } from "./ws.js";
|
|
3
|
+
export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic, MIN_STAKE_ATOMIC, MAX_STAKE_ATOMIC, type OrderSignMessage, type CancelSignMessage, type WsAuthMessage, type ParsedComposite, } from "./eip712.js";
|
|
4
|
+
export { CLOB_AUTH_TYPES, CLOB_AUTH_MESSAGE, buildClobAuthTypedData, buildHmacSignature, HMAC_HEADERS, type ClobAuthDomain, type ClobAuthMessage, } from "./auth.js";
|
|
5
|
+
export { ensureSettlementAllowance, MAX_UINT256, type EnsureAllowanceResult, } from "./approve.js";
|
|
6
|
+
export { UpDownAccountKitSigner, bareErc1271Signer, stripErc6492Wrapper, isErc6492Signature, type UpDownAccountKitConfig, type Eip1193Provider, type GrantSessionResult, type RawTypedDataSigner, } from "./accountKit.js";
|
|
7
|
+
export { OrderType, OrderSide, Option, type ApiConfig, type Balance, type CancelOrderBody, type Eip712Domain, type MarketDetail, type MarketListItem, type OptionValue, type OrderBookFull, type OrderBookLevel, type OrderBookSide, type OrderRow, type OrderSideKey, type OrderSideValue, type OrderStatus, type OrderTypeKey, type OrderTypeValue, type OrdersResponse, type PairConfig, type PairSymbol, type PostOrderBody, type Position, type Stats, type Trade, type Version, } from "./types.js";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// HTTP + WS clients
|
|
2
|
+
export { UpDownHttpClient, wsUrlFromHttpBase } from "./http.js";
|
|
3
|
+
export { UpDownWsClient, } from "./ws.js";
|
|
4
|
+
// EIP-712 helpers
|
|
5
|
+
export { ORDER_TYPES, CANCEL_TYPES, WS_AUTH_TYPES, buildOrderTypedData, buildCancelTypedData, buildWsAuthTypedData, freshSessionId, domainForSettlement, findPairBySettlement, parseCompositeMarketKey, centsToBps, bpsToCents, parseStake, assertStakeBounds, feeAtomic, MIN_STAKE_ATOMIC, MAX_STAKE_ATOMIC, } from "./eip712.js";
|
|
6
|
+
// Phase 3 / Gate 1 — L2 HMAC auth helpers
|
|
7
|
+
export { CLOB_AUTH_TYPES, CLOB_AUTH_MESSAGE, buildClobAuthTypedData, buildHmacSignature, HMAC_HEADERS, } from "./auth.js";
|
|
8
|
+
// Approve helper
|
|
9
|
+
export { ensureSettlementAllowance, MAX_UINT256, } from "./approve.js";
|
|
10
|
+
// Account Kit (Alchemy SCA) — owner-key ERC-1271 order signing + gasless custody.
|
|
11
|
+
// Peer deps (@account-kit/*, @aa-sdk/core) are lazy-imported; importing this
|
|
12
|
+
// module does NOT require them unless you construct/connect the signer.
|
|
13
|
+
export { UpDownAccountKitSigner, bareErc1271Signer, stripErc6492Wrapper, isErc6492Signature, } from "./accountKit.js";
|
|
14
|
+
// Types
|
|
15
|
+
export { OrderType, OrderSide, Option, } from "./types.js";
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpDown SDK type surface — mirrors the backend's REST + WS response shapes
|
|
3
|
+
* one-to-one. Keep field names and casing in sync with `docs/api.md`; if a
|
|
4
|
+
* shape drifts, update both in the same commit.
|
|
5
|
+
*/
|
|
6
|
+
export type PairSymbol = "BTC-USD" | "ETH-USD";
|
|
7
|
+
export type OrderTypeKey = "LIMIT" | "MARKET" | "POST_ONLY" | "IOC";
|
|
8
|
+
export type OrderSideKey = "BUY" | "SELL";
|
|
9
|
+
/** Numeric enum values match the backend's on-chain encoding. */
|
|
10
|
+
export declare const OrderType: {
|
|
11
|
+
readonly LIMIT: 0;
|
|
12
|
+
readonly MARKET: 1;
|
|
13
|
+
readonly POST_ONLY: 2;
|
|
14
|
+
readonly IOC: 3;
|
|
15
|
+
};
|
|
16
|
+
export type OrderTypeValue = (typeof OrderType)[OrderTypeKey];
|
|
17
|
+
export declare const OrderSide: {
|
|
18
|
+
readonly BUY: 0;
|
|
19
|
+
readonly SELL: 1;
|
|
20
|
+
};
|
|
21
|
+
export type OrderSideValue = (typeof OrderSide)[OrderSideKey];
|
|
22
|
+
export declare const Option: {
|
|
23
|
+
readonly UP: 1;
|
|
24
|
+
readonly DOWN: 2;
|
|
25
|
+
};
|
|
26
|
+
export type OptionValue = (typeof Option)[keyof typeof Option];
|
|
27
|
+
/** Per-pair config emitted by `/config.pairs[]`. */
|
|
28
|
+
export type PairConfig = {
|
|
29
|
+
pairId: PairSymbol;
|
|
30
|
+
settlementAddress: `0x${string}`;
|
|
31
|
+
/** Empty string when the pair has no autocycler wired (read-only mode). */
|
|
32
|
+
autocyclerAddress: `0x${string}` | "";
|
|
33
|
+
eip712: {
|
|
34
|
+
domain: Eip712Domain;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
export type Eip712Domain = {
|
|
38
|
+
name: string;
|
|
39
|
+
version: string;
|
|
40
|
+
chainId: number;
|
|
41
|
+
verifyingContract: `0x${string}`;
|
|
42
|
+
};
|
|
43
|
+
export type ApiConfig = {
|
|
44
|
+
chainId: number;
|
|
45
|
+
usdtAddress: `0x${string}`;
|
|
46
|
+
relayerAddress: `0x${string}`;
|
|
47
|
+
platformFeeBps: number;
|
|
48
|
+
makerFeeBps: number;
|
|
49
|
+
/** When `"probability-weighted"`, taker fees scale as `4 × p × (1 − p) × totalBps`. */
|
|
50
|
+
feeModel?: "probability-weighted" | string;
|
|
51
|
+
/** `platformFeeBps + makerFeeBps` (peak fee at 50¢). */
|
|
52
|
+
peakFeeBps?: number;
|
|
53
|
+
dmmRebateBps: number;
|
|
54
|
+
usdtDecimals: number;
|
|
55
|
+
/** Multi-pair list — preferred for new clients. */
|
|
56
|
+
pairs: PairConfig[];
|
|
57
|
+
/**
|
|
58
|
+
* @deprecated Use `pairs[i].settlementAddress` and look up by the market's
|
|
59
|
+
* `settlementAddress` via `findPairBySettlement(...)`. Kept as the FIRST
|
|
60
|
+
* pair's value for one release cycle of overlap.
|
|
61
|
+
*/
|
|
62
|
+
settlementAddress: `0x${string}`;
|
|
63
|
+
/**
|
|
64
|
+
* @deprecated Use `pairs[i].eip712.domain` per market. Kept as the FIRST
|
|
65
|
+
* pair's domain for one release cycle of overlap.
|
|
66
|
+
*/
|
|
67
|
+
eip712: {
|
|
68
|
+
domain: Eip712Domain;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
71
|
+
export type MarketListItem = {
|
|
72
|
+
/** Composite key `<settlementAddress>-<marketId>`. */
|
|
73
|
+
address: string;
|
|
74
|
+
marketId?: string;
|
|
75
|
+
settlementAddress?: `0x${string}`;
|
|
76
|
+
pairId: string;
|
|
77
|
+
pairSymbol?: PairSymbol;
|
|
78
|
+
chartSymbol?: "BTC" | "ETH";
|
|
79
|
+
startTime: number;
|
|
80
|
+
endTime: number;
|
|
81
|
+
duration: number;
|
|
82
|
+
status: "ACTIVE" | "TRADING_ENDED" | "RESOLVED" | "CLAIMED" | string;
|
|
83
|
+
winner: number | null;
|
|
84
|
+
upPrice: string;
|
|
85
|
+
downPrice: string;
|
|
86
|
+
strikePrice?: string;
|
|
87
|
+
settlementPrice?: string;
|
|
88
|
+
volume: string;
|
|
89
|
+
};
|
|
90
|
+
export type MarketDetail = MarketListItem & {
|
|
91
|
+
timeRemainingSeconds: number;
|
|
92
|
+
orderBook: {
|
|
93
|
+
up: OrderBookSide;
|
|
94
|
+
down: OrderBookSide;
|
|
95
|
+
};
|
|
96
|
+
};
|
|
97
|
+
export type OrderBookSide = {
|
|
98
|
+
bestBid: {
|
|
99
|
+
price: number;
|
|
100
|
+
depth: string;
|
|
101
|
+
} | null;
|
|
102
|
+
bestAsk: {
|
|
103
|
+
price: number;
|
|
104
|
+
depth: string;
|
|
105
|
+
} | null;
|
|
106
|
+
};
|
|
107
|
+
export type OrderBookFull = {
|
|
108
|
+
up: {
|
|
109
|
+
bids: OrderBookLevel[];
|
|
110
|
+
asks: OrderBookLevel[];
|
|
111
|
+
};
|
|
112
|
+
down: {
|
|
113
|
+
bids: OrderBookLevel[];
|
|
114
|
+
asks: OrderBookLevel[];
|
|
115
|
+
};
|
|
116
|
+
};
|
|
117
|
+
export type OrderBookLevel = {
|
|
118
|
+
price: number;
|
|
119
|
+
depth: string;
|
|
120
|
+
count: number;
|
|
121
|
+
};
|
|
122
|
+
export type Balance = {
|
|
123
|
+
wallet: string;
|
|
124
|
+
smartAccountAddress: string;
|
|
125
|
+
available: string;
|
|
126
|
+
inOrders: string;
|
|
127
|
+
cachedBalance: string;
|
|
128
|
+
balanceLastSyncedAt: string;
|
|
129
|
+
withdrawNonce: number;
|
|
130
|
+
};
|
|
131
|
+
export type Position = {
|
|
132
|
+
market: string;
|
|
133
|
+
marketStatus: string;
|
|
134
|
+
option: number;
|
|
135
|
+
optionLabel: "UP" | "DOWN";
|
|
136
|
+
shares: string;
|
|
137
|
+
avgPrice: number;
|
|
138
|
+
costBasis: string;
|
|
139
|
+
};
|
|
140
|
+
export type Trade = {
|
|
141
|
+
tradeId: string;
|
|
142
|
+
market: string;
|
|
143
|
+
option: number;
|
|
144
|
+
buyOrderId: string;
|
|
145
|
+
sellOrderId: string;
|
|
146
|
+
buyer: string;
|
|
147
|
+
seller: string;
|
|
148
|
+
price: number;
|
|
149
|
+
amount: string;
|
|
150
|
+
platformFee: string;
|
|
151
|
+
makerFee: string;
|
|
152
|
+
settlementStatus: string;
|
|
153
|
+
createdAt: string;
|
|
154
|
+
};
|
|
155
|
+
export type OrderStatus = "OPEN" | "PARTIALLY_FILLED" | "FILLED" | "CANCEL_PENDING" | "CANCELLED" | string;
|
|
156
|
+
export type OrderRow = {
|
|
157
|
+
orderId: string;
|
|
158
|
+
maker: string;
|
|
159
|
+
market: string;
|
|
160
|
+
option: number;
|
|
161
|
+
side: number;
|
|
162
|
+
type: number;
|
|
163
|
+
price: number;
|
|
164
|
+
amount: string;
|
|
165
|
+
filledAmount: string;
|
|
166
|
+
/**
|
|
167
|
+
* uint256 nonce, JSON-string-encoded. Backend's `OrderModel` schema
|
|
168
|
+
* stores this as `String` (PR-13.1 hotfix) to preserve full precision
|
|
169
|
+
* — a `number` here would silently truncate above 2^53. Parse via
|
|
170
|
+
* `BigInt(row.nonce)` if you need the value, or pass the string
|
|
171
|
+
* straight to `signTypedData` (viem accepts string-form bigints).
|
|
172
|
+
*/
|
|
173
|
+
nonce: string;
|
|
174
|
+
/** uint256 unix-sec expiry, JSON-string-encoded. See `nonce`. */
|
|
175
|
+
expiry: string;
|
|
176
|
+
/** EIP-712 signature; backend marks this required on the row. */
|
|
177
|
+
signature: string;
|
|
178
|
+
status: OrderStatus;
|
|
179
|
+
reason?: string;
|
|
180
|
+
createdAt: string;
|
|
181
|
+
updatedAt?: string;
|
|
182
|
+
};
|
|
183
|
+
export type OrdersResponse = {
|
|
184
|
+
orders: OrderRow[];
|
|
185
|
+
total: number;
|
|
186
|
+
limit: number;
|
|
187
|
+
offset: number;
|
|
188
|
+
};
|
|
189
|
+
export type Stats = {
|
|
190
|
+
totalVolume: string;
|
|
191
|
+
activeMarketsCount: number;
|
|
192
|
+
totalTraders: number;
|
|
193
|
+
};
|
|
194
|
+
export type Version = {
|
|
195
|
+
commit: string;
|
|
196
|
+
bootedAt: string;
|
|
197
|
+
env: string;
|
|
198
|
+
nodeVersion: string;
|
|
199
|
+
};
|
|
200
|
+
/** Atomic-USDT body for `POST /orders`. The backend re-verifies the
|
|
201
|
+
* signature against the per-pair domain on receipt. */
|
|
202
|
+
export type PostOrderBody = {
|
|
203
|
+
maker: string;
|
|
204
|
+
/** Composite key `<settlement>-<marketId>` — used for routing only. */
|
|
205
|
+
market: string;
|
|
206
|
+
option: number;
|
|
207
|
+
side: number | OrderSideKey;
|
|
208
|
+
type: number | OrderTypeKey;
|
|
209
|
+
/** bps; 0 for MARKET. */
|
|
210
|
+
price?: number;
|
|
211
|
+
/** Atomic USDT (1 USDT = 1_000_000). */
|
|
212
|
+
amount: string;
|
|
213
|
+
/**
|
|
214
|
+
* F-2026-17731 (Hacken remediation V2): signed fee cap, atomic-USDT decimal
|
|
215
|
+
* string. Position between `amount` and `nonce` in the signed `Order` — MUST
|
|
216
|
+
* be present and MUST equal the value inside the signature, else the on-chain
|
|
217
|
+
* `SignatureChecker` rejects the fill (the ORDER_TYPEHASH includes `maxFee`).
|
|
218
|
+
* Compute as `amount * (platformFeeBps + makerFeeBps) / 10000` (peak fee).
|
|
219
|
+
*/
|
|
220
|
+
maxFee: string;
|
|
221
|
+
nonce: number;
|
|
222
|
+
expiry: number;
|
|
223
|
+
signature: string;
|
|
224
|
+
};
|
|
225
|
+
export type CancelOrderBody = {
|
|
226
|
+
maker: string;
|
|
227
|
+
signature: string;
|
|
228
|
+
/**
|
|
229
|
+
* uint256 nonce — must match what was signed. **Send as a JSON string** to
|
|
230
|
+
* preserve full precision; backend's PR-13.1 hotfix coerces via `BigInt(...)`.
|
|
231
|
+
*/
|
|
232
|
+
nonce: string | number;
|
|
233
|
+
/** uint256 unix-sec expiry — must match what was signed. */
|
|
234
|
+
expiry: string | number;
|
|
235
|
+
};
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UpDown SDK type surface — mirrors the backend's REST + WS response shapes
|
|
3
|
+
* one-to-one. Keep field names and casing in sync with `docs/api.md`; if a
|
|
4
|
+
* shape drifts, update both in the same commit.
|
|
5
|
+
*/
|
|
6
|
+
/** Numeric enum values match the backend's on-chain encoding. */
|
|
7
|
+
export const OrderType = {
|
|
8
|
+
LIMIT: 0,
|
|
9
|
+
MARKET: 1,
|
|
10
|
+
POST_ONLY: 2,
|
|
11
|
+
IOC: 3,
|
|
12
|
+
};
|
|
13
|
+
export const OrderSide = {
|
|
14
|
+
BUY: 0,
|
|
15
|
+
SELL: 1,
|
|
16
|
+
};
|
|
17
|
+
export const Option = {
|
|
18
|
+
UP: 1,
|
|
19
|
+
DOWN: 2,
|
|
20
|
+
};
|
package/dist/ws.d.ts
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WebSocket client with auto-reconnect + PR-19 auth handshake.
|
|
3
|
+
*
|
|
4
|
+
* Two paths:
|
|
5
|
+
*
|
|
6
|
+
* - `connectPublic(channels)` — public channels only (`markets`,
|
|
7
|
+
* `orderbook:*`, `trades:*`). Sends `{type:'subscribe', channels}`
|
|
8
|
+
* immediately on open, no auth.
|
|
9
|
+
*
|
|
10
|
+
* - `connectAuthed({ signAuth, channels })` — sends
|
|
11
|
+
* `{type:'auth', ...}` first, awaits `auth_ok`, then sends
|
|
12
|
+
* `{type:'subscribe', channels}`. Required for `orders:<wallet>` +
|
|
13
|
+
* `balance:<wallet>` (PR-19 gates these behind a verified WsAuth
|
|
14
|
+
* signature). On reconnect within the 24h token TTL, replays the
|
|
15
|
+
* cached token via `{type:'auth', token}` — only re-prompts the
|
|
16
|
+
* `signAuth` callback if the server rejects the cached token.
|
|
17
|
+
*
|
|
18
|
+
* Server message envelope (mirrors `src/ws/WebSocketServer.ts:235-293`):
|
|
19
|
+
*
|
|
20
|
+
* client → server { type: 'auth', wallet, timestamp, sessionId, signature }
|
|
21
|
+
* server → client { type: 'auth_ok', wallet, token, expiresAt }
|
|
22
|
+
* or { type: 'auth_error' } (no reason echoed — anti-probe)
|
|
23
|
+
*
|
|
24
|
+
* client → server { type: 'auth', token } (token replay)
|
|
25
|
+
* server → client { type: 'auth_ok', wallet, token } (token re-validated)
|
|
26
|
+
*
|
|
27
|
+
* State machine:
|
|
28
|
+
* idle → authenticating → subscribing → live
|
|
29
|
+
* → unauthed-failure (signAuth rejected)
|
|
30
|
+
*
|
|
31
|
+
* The legacy `connect(SubscribePayload)` API is retained as a deprecated
|
|
32
|
+
* shim that routes to `connectPublic` so existing public-only callers
|
|
33
|
+
* keep working without code changes. The `wallet` field on the legacy
|
|
34
|
+
* payload is now silently ignored (it was never authenticated and
|
|
35
|
+
* post-PR-19 the server drops private subs from anonymous clients).
|
|
36
|
+
*/
|
|
37
|
+
export type UpDownWsMessage = {
|
|
38
|
+
type: string;
|
|
39
|
+
channel?: string;
|
|
40
|
+
data?: unknown;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* @deprecated Use `connectPublic(channels)` for public channels or
|
|
44
|
+
* `connectAuthed({ signAuth, channels })` for `orders:*` / `balance:*`.
|
|
45
|
+
* Pre-PR-19 the `wallet` field auto-subscribed `orders:<wallet>` without
|
|
46
|
+
* any signature check; that gate is now closed and a `wallet`-only
|
|
47
|
+
* payload no longer receives per-wallet events.
|
|
48
|
+
*/
|
|
49
|
+
export type SubscribePayload = {
|
|
50
|
+
type: "subscribe";
|
|
51
|
+
channels: string[];
|
|
52
|
+
wallet?: string;
|
|
53
|
+
};
|
|
54
|
+
/** Returned from a `signAuth` callback. */
|
|
55
|
+
export type WsAuthCredentials = {
|
|
56
|
+
wallet: `0x${string}`;
|
|
57
|
+
/** Unix seconds, as a bigint. Server window is ±60s. */
|
|
58
|
+
timestamp: bigint;
|
|
59
|
+
/** 32-byte hex (bytes32). See `freshSessionId()`. */
|
|
60
|
+
sessionId: `0x${string}`;
|
|
61
|
+
/** EIP-712 signature over `WS_AUTH_TYPES`. */
|
|
62
|
+
signature: `0x${string}`;
|
|
63
|
+
/**
|
|
64
|
+
* Optional cached token from a previous `auth_ok`. When supplied, the
|
|
65
|
+
* client tries token replay first and only sends the signed payload if
|
|
66
|
+
* the server returns `auth_error`.
|
|
67
|
+
*/
|
|
68
|
+
token?: string;
|
|
69
|
+
};
|
|
70
|
+
export type ConnectAuthedOptions = {
|
|
71
|
+
/** Async callback that returns the credentials to authenticate with. */
|
|
72
|
+
signAuth: () => Promise<WsAuthCredentials>;
|
|
73
|
+
/** Channels to subscribe to AFTER auth_ok. May include public channels. */
|
|
74
|
+
channels: string[];
|
|
75
|
+
/** Optional callback fired when auth fails terminally (signAuth rejected
|
|
76
|
+
* by the server). The client stops reconnecting in that case. */
|
|
77
|
+
onAuthError?: (reason?: string) => void;
|
|
78
|
+
};
|
|
79
|
+
export declare class UpDownWsClient {
|
|
80
|
+
private readonly url;
|
|
81
|
+
private readonly onMessage;
|
|
82
|
+
private ws;
|
|
83
|
+
private reconnect;
|
|
84
|
+
private timer;
|
|
85
|
+
private closed;
|
|
86
|
+
private mode;
|
|
87
|
+
private state;
|
|
88
|
+
/** Cached token + expiry from the last successful auth_ok. Replayed on
|
|
89
|
+
* reconnect; cleared if the server returns auth_error against it. */
|
|
90
|
+
private cachedToken;
|
|
91
|
+
private cachedTokenExpiresAt;
|
|
92
|
+
constructor(url: string, onMessage: (msg: UpDownWsMessage) => void);
|
|
93
|
+
/**
|
|
94
|
+
* Subscribe to public channels only (`markets`, `orderbook:*`,
|
|
95
|
+
* `trades:*`). No signature required.
|
|
96
|
+
*/
|
|
97
|
+
connectPublic(channels: string[]): void;
|
|
98
|
+
/**
|
|
99
|
+
* Authenticate via the PR-19 EIP-712 handshake, then subscribe.
|
|
100
|
+
* Required for `orders:*` and `balance:*` channels.
|
|
101
|
+
*/
|
|
102
|
+
connectAuthed(opts: ConnectAuthedOptions): void;
|
|
103
|
+
/**
|
|
104
|
+
* @deprecated Use `connectPublic(channels)` or `connectAuthed({ ... })`.
|
|
105
|
+
* Pre-PR-19 the WS server accepted `{type:'subscribe', wallet}` and
|
|
106
|
+
* auto-attached `orders:<wallet>` to the anonymous client; that path
|
|
107
|
+
* is now closed. The `wallet` field on the payload is ignored — if
|
|
108
|
+
* the channel list contains private channels they will be silently
|
|
109
|
+
* dropped by the server because the connection is unauthenticated.
|
|
110
|
+
*/
|
|
111
|
+
connect(subscribe: SubscribePayload): void;
|
|
112
|
+
disconnect(): void;
|
|
113
|
+
private openSocket;
|
|
114
|
+
private startHandshake;
|
|
115
|
+
private runSignAndSendAuth;
|
|
116
|
+
private handleIncoming;
|
|
117
|
+
private send;
|
|
118
|
+
}
|