@pear-protocol/market-sdk 0.0.13 → 0.1.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 +27 -0
- package/dist/chart/cache/index.d.ts +1 -1
- package/dist/chart/cache/index.js +15 -3
- package/dist/chart/chart.d.ts +1 -1
- package/dist/chart/chart.js +14 -23
- package/dist/chart/collector/binance.d.ts +1 -1
- package/dist/chart/collector/binance.js +4 -0
- package/dist/chart/collector/bybit.d.ts +1 -1
- package/dist/chart/collector/bybit.js +12 -1
- package/dist/chart/collector/helpers.d.ts +2 -2
- package/dist/chart/collector/helpers.js +5 -2
- package/dist/chart/collector/hyperliquid.d.ts +1 -1
- package/dist/chart/collector/hyperliquid.js +12 -2
- package/dist/chart/collector/index.d.ts +1 -1
- package/dist/chart/collector/index.js +28 -12
- package/dist/chart/collector/lighter.d.ts +2 -2
- package/dist/chart/collector/lighter.js +10 -4
- package/dist/chart/collector/okx.d.ts +1 -1
- package/dist/chart/collector/okx.js +12 -1
- package/dist/chart/compute/asset.d.ts +1 -1
- package/dist/chart/compute/asset.js +4 -0
- package/dist/chart/compute/index.d.ts +1 -1
- package/dist/chart/compute/performance.d.ts +1 -1
- package/dist/chart/compute/performance.js +20 -4
- package/dist/chart/compute/price-ratio.d.ts +1 -1
- package/dist/chart/compute/price-ratio.js +8 -1
- package/dist/chart/compute/weighted-ratio.d.ts +1 -1
- package/dist/chart/compute/weighted-ratio.js +9 -1
- package/dist/chart/types.d.ts +1 -1
- package/dist/chart/utils.d.ts +1 -1
- package/dist/chart/utils.js +19 -11
- package/dist/chart/ws/base-candle.d.ts +11 -5
- package/dist/chart/ws/base-candle.js +6 -2
- package/dist/chart/ws/binance.d.ts +4 -6
- package/dist/chart/ws/binance.js +33 -19
- package/dist/chart/ws/bybit.d.ts +4 -6
- package/dist/chart/ws/bybit.js +40 -24
- package/dist/chart/ws/hyperliquid.d.ts +4 -6
- package/dist/chart/ws/hyperliquid.js +37 -19
- package/dist/chart/ws/index.js +3 -0
- package/dist/chart/ws/lighter.d.ts +20 -20
- package/dist/chart/ws/lighter.js +83 -196
- package/dist/chart/ws/okx.d.ts +4 -6
- package/dist/chart/ws/okx.js +47 -23
- package/dist/funding-rate/fetcher/binance.d.ts +1 -1
- package/dist/funding-rate/fetcher/binance.js +7 -1
- package/dist/funding-rate/fetcher/bybit.d.ts +1 -1
- package/dist/funding-rate/fetcher/bybit.js +15 -13
- package/dist/funding-rate/fetcher/http-client.d.ts +1 -1
- package/dist/funding-rate/fetcher/http-client.js +15 -10
- package/dist/funding-rate/fetcher/hyperliquid.d.ts +1 -1
- package/dist/funding-rate/fetcher/hyperliquid.js +7 -1
- package/dist/funding-rate/fetcher/index.d.ts +2 -2
- package/dist/funding-rate/fetcher/index.js +5 -2
- package/dist/funding-rate/fetcher/lighter.d.ts +2 -2
- package/dist/funding-rate/fetcher/lighter.js +11 -3
- package/dist/funding-rate/fetcher/okx.d.ts +1 -1
- package/dist/funding-rate/fetcher/okx.js +14 -12
- package/dist/funding-rate/funding-rate.d.ts +1 -1
- package/dist/funding-rate/funding-rate.js +30 -21
- package/dist/funding-rate/types.d.ts +1 -1
- package/dist/funding-rate/utils.d.ts +1 -1
- package/dist/funding-rate/utils.js +26 -5
- package/dist/index.d.ts +2 -2
- package/dist/orderbook/book/aggregate.d.ts +1 -1
- package/dist/orderbook/book/aggregate.js +16 -3
- package/dist/orderbook/book/local-book.d.ts +1 -1
- package/dist/orderbook/book/local-book.js +16 -5
- package/dist/orderbook/orderbook.d.ts +1 -1
- package/dist/orderbook/orderbook.js +29 -13
- package/dist/orderbook/types.d.ts +1 -1
- package/dist/orderbook/types.js +7 -1
- package/dist/orderbook/utils.d.ts +1 -1
- package/dist/orderbook/utils.js +5 -0
- package/dist/orderbook/ws/base-depth.js +10 -1
- package/dist/orderbook/ws/binance.js +28 -7
- package/dist/orderbook/ws/bybit.js +20 -5
- package/dist/orderbook/ws/hyperliquid.js +31 -11
- package/dist/orderbook/ws/index.js +3 -0
- package/dist/orderbook/ws/lighter.js +30 -7
- package/dist/orderbook/ws/okx.js +23 -2
- package/dist/shared/types.d.ts +6 -1
- package/dist/transport/base-transport.d.ts +4 -0
- package/dist/transport/base-transport.js +13 -0
- package/dist/transport/binance.js +5 -0
- package/dist/transport/bybit.js +8 -1
- package/dist/transport/hyperliquid.js +8 -1
- package/dist/transport/index.d.ts +11 -3
- package/dist/transport/index.js +5 -2
- package/dist/transport/lighter.d.ts +31 -1
- package/dist/transport/lighter.js +86 -1
- package/dist/transport/okx.js +5 -0
- package/package.json +5 -3
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
import { BaseDepthWs, parseStringLevels } from './base-depth';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
const BINANCE_DEPTH_REST = "https://fapi.binance.com/fapi/v1/depth";
|
|
4
|
-
const toStreamName = (symbol) => `${symbol.toLowerCase()}@depth@100ms
|
|
6
|
+
const toStreamName = /* @__PURE__ */ __name((symbol) => `${symbol.toLowerCase()}@depth@100ms`, "toStreamName");
|
|
5
7
|
class BinanceDepthWs extends BaseDepthWs {
|
|
8
|
+
static {
|
|
9
|
+
__name(this, "BinanceDepthWs");
|
|
10
|
+
}
|
|
6
11
|
idCounter = 1;
|
|
7
12
|
symbolStates = /* @__PURE__ */ new Map();
|
|
8
13
|
buildSubscribeMessage(symbol) {
|
|
9
14
|
return {
|
|
10
15
|
method: "SUBSCRIBE",
|
|
11
|
-
params: [
|
|
16
|
+
params: [
|
|
17
|
+
toStreamName(symbol)
|
|
18
|
+
],
|
|
12
19
|
id: this.idCounter++
|
|
13
20
|
};
|
|
14
21
|
}
|
|
@@ -16,13 +23,18 @@ class BinanceDepthWs extends BaseDepthWs {
|
|
|
16
23
|
this.symbolStates.delete(symbol);
|
|
17
24
|
return {
|
|
18
25
|
method: "UNSUBSCRIBE",
|
|
19
|
-
params: [
|
|
26
|
+
params: [
|
|
27
|
+
toStreamName(symbol)
|
|
28
|
+
],
|
|
20
29
|
id: this.idCounter++
|
|
21
30
|
};
|
|
22
31
|
}
|
|
23
32
|
subscribe(symbol) {
|
|
24
33
|
if (this.symbolStates.has(symbol)) return;
|
|
25
|
-
this.symbolStates.set(symbol, {
|
|
34
|
+
this.symbolStates.set(symbol, {
|
|
35
|
+
phase: "buffering",
|
|
36
|
+
buffer: []
|
|
37
|
+
});
|
|
26
38
|
super.subscribe(symbol);
|
|
27
39
|
this.bootstrap(symbol);
|
|
28
40
|
}
|
|
@@ -54,7 +66,10 @@ class BinanceDepthWs extends BaseDepthWs {
|
|
|
54
66
|
asks: parseStringLevels(snapshot.asks),
|
|
55
67
|
ts: snapshot.E ?? Date.now()
|
|
56
68
|
});
|
|
57
|
-
this.symbolStates.set(symbol, {
|
|
69
|
+
this.symbolStates.set(symbol, {
|
|
70
|
+
phase: "syncing",
|
|
71
|
+
lastUpdateId
|
|
72
|
+
});
|
|
58
73
|
for (const event of buffered) {
|
|
59
74
|
if (!this.processEvent(symbol, event)) {
|
|
60
75
|
this.rebootstrap(symbol);
|
|
@@ -70,7 +85,10 @@ class BinanceDepthWs extends BaseDepthWs {
|
|
|
70
85
|
if (!(payload.U <= state.lastUpdateId && payload.u >= state.lastUpdateId)) {
|
|
71
86
|
return false;
|
|
72
87
|
}
|
|
73
|
-
this.symbolStates.set(symbol, {
|
|
88
|
+
this.symbolStates.set(symbol, {
|
|
89
|
+
phase: "ready",
|
|
90
|
+
lastFinalUpdateId: payload.u
|
|
91
|
+
});
|
|
74
92
|
this.emitDelta(symbol, payload);
|
|
75
93
|
return true;
|
|
76
94
|
}
|
|
@@ -98,7 +116,10 @@ class BinanceDepthWs extends BaseDepthWs {
|
|
|
98
116
|
}
|
|
99
117
|
rebootstrap(symbol) {
|
|
100
118
|
if (!this.symbolStates.has(symbol)) return;
|
|
101
|
-
this.symbolStates.set(symbol, {
|
|
119
|
+
this.symbolStates.set(symbol, {
|
|
120
|
+
phase: "buffering",
|
|
121
|
+
buffer: []
|
|
122
|
+
});
|
|
102
123
|
setTimeout(() => this.bootstrap(symbol), 1e3);
|
|
103
124
|
}
|
|
104
125
|
parseMessage(data) {
|
|
@@ -1,16 +1,31 @@
|
|
|
1
1
|
import { BaseDepthWs, parseStringLevels } from './base-depth';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
const toTopic = /* @__PURE__ */ __name((symbol) => `orderbook.1000.${symbol}`, "toTopic");
|
|
6
|
+
const fromTopic = /* @__PURE__ */ __name((topic) => {
|
|
5
7
|
const parts = topic.split(".");
|
|
6
8
|
return parts[2] ?? "";
|
|
7
|
-
};
|
|
9
|
+
}, "fromTopic");
|
|
8
10
|
class BybitDepthWs extends BaseDepthWs {
|
|
11
|
+
static {
|
|
12
|
+
__name(this, "BybitDepthWs");
|
|
13
|
+
}
|
|
9
14
|
buildSubscribeMessage(symbol) {
|
|
10
|
-
return {
|
|
15
|
+
return {
|
|
16
|
+
op: "subscribe",
|
|
17
|
+
args: [
|
|
18
|
+
toTopic(symbol)
|
|
19
|
+
]
|
|
20
|
+
};
|
|
11
21
|
}
|
|
12
22
|
buildUnsubscribeMessage(symbol) {
|
|
13
|
-
return {
|
|
23
|
+
return {
|
|
24
|
+
op: "unsubscribe",
|
|
25
|
+
args: [
|
|
26
|
+
toTopic(symbol)
|
|
27
|
+
]
|
|
28
|
+
};
|
|
14
29
|
}
|
|
15
30
|
parseMessage(data) {
|
|
16
31
|
let msg;
|
|
@@ -1,27 +1,47 @@
|
|
|
1
1
|
import { BaseDepthWs } from './base-depth';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
const parseLevels = /* @__PURE__ */ __name((levels) => {
|
|
4
6
|
if (!levels) return [];
|
|
5
|
-
return levels.map((l) => [
|
|
6
|
-
|
|
7
|
+
return levels.map((l) => [
|
|
8
|
+
l.px,
|
|
9
|
+
Number(l.sz)
|
|
10
|
+
]);
|
|
11
|
+
}, "parseLevels");
|
|
7
12
|
function computeSigFigs(bucketSize, currentPrice) {
|
|
8
13
|
if (bucketSize <= 0 || currentPrice <= 0) return {};
|
|
9
14
|
const priceMag = Math.floor(Math.log10(currentPrice));
|
|
10
15
|
const bucketMag = Math.floor(Math.log10(bucketSize));
|
|
11
16
|
const rawSigFigs = priceMag - bucketMag + 1;
|
|
12
17
|
if (rawSigFigs < 2) return {};
|
|
13
|
-
if (rawSigFigs > 5) return {
|
|
18
|
+
if (rawSigFigs > 5) return {
|
|
19
|
+
nSigFigs: null
|
|
20
|
+
};
|
|
14
21
|
const nSigFigs = rawSigFigs;
|
|
15
|
-
if (nSigFigs < 5) return {
|
|
22
|
+
if (nSigFigs < 5) return {
|
|
23
|
+
nSigFigs
|
|
24
|
+
};
|
|
16
25
|
const ratio = bucketSize / 10 ** bucketMag;
|
|
17
|
-
const mantissaCandidates = [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
26
|
+
const mantissaCandidates = [
|
|
27
|
+
1,
|
|
28
|
+
2,
|
|
29
|
+
5
|
|
30
|
+
];
|
|
31
|
+
const mantissa = mantissaCandidates.reduce((closest, candidate) => Math.abs(candidate - ratio) < Math.abs(closest - ratio) ? candidate : closest);
|
|
32
|
+
if (mantissa === 1) return {
|
|
33
|
+
nSigFigs: 5
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
nSigFigs: 5,
|
|
37
|
+
mantissa
|
|
38
|
+
};
|
|
23
39
|
}
|
|
40
|
+
__name(computeSigFigs, "computeSigFigs");
|
|
24
41
|
class HyperliquidDepthWs extends BaseDepthWs {
|
|
42
|
+
static {
|
|
43
|
+
__name(this, "HyperliquidDepthWs");
|
|
44
|
+
}
|
|
25
45
|
aggregation = 0;
|
|
26
46
|
lastNSigFigs;
|
|
27
47
|
lastMantissa;
|
|
@@ -5,6 +5,8 @@ import { HyperliquidDepthWs } from './hyperliquid';
|
|
|
5
5
|
import { LighterDepthWs } from './lighter';
|
|
6
6
|
import { OkxDepthWs } from './okx';
|
|
7
7
|
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
8
10
|
function createDepthWs(transport, connector, onUpdate) {
|
|
9
11
|
switch (connector) {
|
|
10
12
|
case "hyperliquid":
|
|
@@ -23,5 +25,6 @@ function createDepthWs(transport, connector, onUpdate) {
|
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
__name(createDepthWs, "createDepthWs");
|
|
26
29
|
|
|
27
30
|
export { createDepthWs };
|
|
@@ -1,14 +1,33 @@
|
|
|
1
1
|
import { BaseDepthWs, parseStringLevels } from './base-depth';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class LighterDepthWs extends BaseDepthWs {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "LighterDepthWs");
|
|
8
|
+
}
|
|
4
9
|
snapshotReceived = /* @__PURE__ */ new Map();
|
|
5
10
|
lastNonce = /* @__PURE__ */ new Map();
|
|
6
11
|
resetListenerId = null;
|
|
7
12
|
buildSubscribeMessage(symbol) {
|
|
8
|
-
|
|
13
|
+
const authToken = this.transport.authToken;
|
|
14
|
+
if (!authToken) {
|
|
15
|
+
return {
|
|
16
|
+
type: "subscribe",
|
|
17
|
+
channel: `order_book@tier2/${symbol}`
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
type: "subscribe",
|
|
22
|
+
channel: `order_book@tier2/${symbol}`,
|
|
23
|
+
auth: authToken
|
|
24
|
+
};
|
|
9
25
|
}
|
|
10
26
|
buildUnsubscribeMessage(symbol) {
|
|
11
|
-
return {
|
|
27
|
+
return {
|
|
28
|
+
type: "unsubscribe",
|
|
29
|
+
channel: `order_book@tier2/${symbol}`
|
|
30
|
+
};
|
|
12
31
|
}
|
|
13
32
|
start() {
|
|
14
33
|
super.start();
|
|
@@ -44,9 +63,7 @@ class LighterDepthWs extends BaseDepthWs {
|
|
|
44
63
|
if (!isSnapshot) {
|
|
45
64
|
const prevNonce = this.lastNonce.get(symbol);
|
|
46
65
|
if (prevNonce !== void 0 && book.begin_nonce !== prevNonce) {
|
|
47
|
-
console.warn(
|
|
48
|
-
`[LighterDepthWs] Sequence break on symbol ${symbol}: expected begin_nonce ${prevNonce}, got ${book.begin_nonce}. Resubscribing.`
|
|
49
|
-
);
|
|
66
|
+
console.warn(`[LighterDepthWs] Sequence break on symbol ${symbol}: expected begin_nonce ${prevNonce}, got ${book.begin_nonce}. Resubscribing.`);
|
|
50
67
|
this.resubscribe(symbol);
|
|
51
68
|
return null;
|
|
52
69
|
}
|
|
@@ -56,8 +73,14 @@ class LighterDepthWs extends BaseDepthWs {
|
|
|
56
73
|
return {
|
|
57
74
|
symbol,
|
|
58
75
|
type: isSnapshot ? "snapshot" : "delta",
|
|
59
|
-
bids: parseStringLevels(book.bids.map((l) => [
|
|
60
|
-
|
|
76
|
+
bids: parseStringLevels(book.bids.map((l) => [
|
|
77
|
+
l.price,
|
|
78
|
+
l.size
|
|
79
|
+
])),
|
|
80
|
+
asks: parseStringLevels(book.asks.map((l) => [
|
|
81
|
+
l.price,
|
|
82
|
+
l.size
|
|
83
|
+
])),
|
|
61
84
|
ts: msg.timestamp
|
|
62
85
|
};
|
|
63
86
|
}
|
package/dist/orderbook/ws/okx.js
CHANGED
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
import { BaseDepthWs, parseStringLevels } from './base-depth';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class OkxDepthWs extends BaseDepthWs {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "OkxDepthWs");
|
|
8
|
+
}
|
|
4
9
|
buildSubscribeMessage(symbol) {
|
|
5
|
-
return {
|
|
10
|
+
return {
|
|
11
|
+
op: "subscribe",
|
|
12
|
+
args: [
|
|
13
|
+
{
|
|
14
|
+
channel: "books",
|
|
15
|
+
instId: symbol
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
};
|
|
6
19
|
}
|
|
7
20
|
buildUnsubscribeMessage(symbol) {
|
|
8
|
-
return {
|
|
21
|
+
return {
|
|
22
|
+
op: "unsubscribe",
|
|
23
|
+
args: [
|
|
24
|
+
{
|
|
25
|
+
channel: "books",
|
|
26
|
+
instId: symbol
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
};
|
|
9
30
|
}
|
|
10
31
|
parseMessage(data) {
|
|
11
32
|
if (data === "pong") return null;
|
package/dist/shared/types.d.ts
CHANGED
|
@@ -2,5 +2,10 @@ interface WsMessage {
|
|
|
2
2
|
[key: string]: unknown;
|
|
3
3
|
}
|
|
4
4
|
type MessageListener = (data: string) => void;
|
|
5
|
+
/**
|
|
6
|
+
* Mints the identity a venue rate-limits by. Supplied by the caller, which owns the credentials and
|
|
7
|
+
* the token cache; resolving `undefined` keeps the traffic anonymous.
|
|
8
|
+
*/
|
|
9
|
+
type AuthTokenProvider = () => Promise<string | undefined>;
|
|
5
10
|
|
|
6
|
-
export type { MessageListener, WsMessage };
|
|
11
|
+
export type { AuthTokenProvider, MessageListener, WsMessage };
|
|
@@ -15,6 +15,10 @@ declare abstract class BaseTransport {
|
|
|
15
15
|
connect(): void;
|
|
16
16
|
disconnect(): void;
|
|
17
17
|
get connected(): boolean;
|
|
18
|
+
/** The identity already in hand, for venues that widen rate limits for a known account. */
|
|
19
|
+
get authToken(): string | undefined;
|
|
20
|
+
/** Resolves a live identity for the next request; a venue without one stays anonymous. */
|
|
21
|
+
retrieveAuthToken(): Promise<string | undefined>;
|
|
18
22
|
send(msg: WsMessage | string): void;
|
|
19
23
|
addMessageListener(listener: MessageListener): string;
|
|
20
24
|
removeMessageListener(id: string): void;
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { WebSocket } from 'partysocket';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class BaseTransport {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "BaseTransport");
|
|
8
|
+
}
|
|
4
9
|
ws = null;
|
|
5
10
|
pingTimer = null;
|
|
6
11
|
messageListeners = /* @__PURE__ */ new Map();
|
|
@@ -50,6 +55,14 @@ class BaseTransport {
|
|
|
50
55
|
get connected() {
|
|
51
56
|
return this.ws?.readyState === WebSocket.OPEN;
|
|
52
57
|
}
|
|
58
|
+
/** The identity already in hand, for venues that widen rate limits for a known account. */
|
|
59
|
+
get authToken() {
|
|
60
|
+
return void 0;
|
|
61
|
+
}
|
|
62
|
+
/** Resolves a live identity for the next request; a venue without one stays anonymous. */
|
|
63
|
+
retrieveAuthToken() {
|
|
64
|
+
return Promise.resolve(void 0);
|
|
65
|
+
}
|
|
53
66
|
send(msg) {
|
|
54
67
|
if (this.ws?.readyState === WebSocket.OPEN) {
|
|
55
68
|
this.ws.send(typeof msg === "string" ? msg : JSON.stringify(msg));
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { BaseTransport } from './base-transport';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class BinanceTransport extends BaseTransport {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "BinanceTransport");
|
|
8
|
+
}
|
|
4
9
|
connector = "binance";
|
|
5
10
|
getWsUrl() {
|
|
6
11
|
return "wss://fstream.binance.com/ws";
|
package/dist/transport/bybit.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { BaseTransport } from './base-transport';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class BybitTransport extends BaseTransport {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "BybitTransport");
|
|
8
|
+
}
|
|
4
9
|
connector = "bybit";
|
|
5
10
|
getWsUrl() {
|
|
6
11
|
return "wss://stream.bybit.com/v5/public/linear";
|
|
@@ -9,7 +14,9 @@ class BybitTransport extends BaseTransport {
|
|
|
9
14
|
return 2e4;
|
|
10
15
|
}
|
|
11
16
|
buildPingMessage() {
|
|
12
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
op: "ping"
|
|
19
|
+
};
|
|
13
20
|
}
|
|
14
21
|
}
|
|
15
22
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { BaseTransport } from './base-transport';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class HyperliquidTransport extends BaseTransport {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "HyperliquidTransport");
|
|
8
|
+
}
|
|
4
9
|
connector = "hyperliquid";
|
|
5
10
|
getWsUrl() {
|
|
6
11
|
return "wss://api.hyperliquid.xyz/ws";
|
|
@@ -9,7 +14,9 @@ class HyperliquidTransport extends BaseTransport {
|
|
|
9
14
|
return 3e4;
|
|
10
15
|
}
|
|
11
16
|
buildPingMessage() {
|
|
12
|
-
return {
|
|
17
|
+
return {
|
|
18
|
+
method: "ping"
|
|
19
|
+
};
|
|
13
20
|
}
|
|
14
21
|
}
|
|
15
22
|
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Connector } from '@pear-protocol/types';
|
|
2
|
+
import { AuthTokenProvider } from '../shared/types.js';
|
|
2
3
|
import { BaseTransport } from './base-transport.js';
|
|
3
4
|
import 'partysocket';
|
|
4
|
-
import '../shared/types.js';
|
|
5
5
|
|
|
6
6
|
type Transport = BaseTransport;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
interface TransportOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Mints the token that attributes this transport's traffic to a connected account. Only Lighter
|
|
11
|
+
* reads it today, where an authenticated request earns a wider rate limit on public market data.
|
|
12
|
+
* Omit it — or resolve `undefined` — to stay anonymous.
|
|
13
|
+
*/
|
|
14
|
+
authToken?: AuthTokenProvider;
|
|
15
|
+
}
|
|
16
|
+
declare function CreateTransport(connector: Connector, options?: TransportOptions): Transport;
|
|
9
17
|
|
|
10
|
-
export { BaseTransport, CreateTransport, type Transport };
|
|
18
|
+
export { BaseTransport, CreateTransport, type Transport, type TransportOptions };
|
package/dist/transport/index.js
CHANGED
|
@@ -5,7 +5,9 @@ import { HyperliquidTransport } from './hyperliquid';
|
|
|
5
5
|
import { LighterTransport } from './lighter';
|
|
6
6
|
import { OkxTransport } from './okx';
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
10
|
+
function CreateTransport(connector, options) {
|
|
9
11
|
switch (connector) {
|
|
10
12
|
case "binance":
|
|
11
13
|
return new BinanceTransport();
|
|
@@ -16,12 +18,13 @@ function CreateTransport(connector) {
|
|
|
16
18
|
case "hyperliquid":
|
|
17
19
|
return new HyperliquidTransport();
|
|
18
20
|
case "lighter":
|
|
19
|
-
return new LighterTransport();
|
|
21
|
+
return new LighterTransport(options?.authToken);
|
|
20
22
|
default: {
|
|
21
23
|
const _exhaustive = connector;
|
|
22
24
|
throw new Error(`Unsupported connector: ${String(_exhaustive)}`);
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
__name(CreateTransport, "CreateTransport");
|
|
26
29
|
|
|
27
30
|
export { CreateTransport };
|
|
@@ -1,13 +1,43 @@
|
|
|
1
1
|
import { Connector } from '@pear-protocol/types';
|
|
2
|
-
import { WsMessage } from '../shared/types.js';
|
|
2
|
+
import { AuthTokenProvider, WsMessage } from '../shared/types.js';
|
|
3
3
|
import { BaseTransport } from './base-transport.js';
|
|
4
4
|
import 'partysocket';
|
|
5
5
|
|
|
6
|
+
/**
|
|
7
|
+
* Lighter's transport carries the connected account's identity alongside the socket, because the
|
|
8
|
+
* venue widens rate limits for authenticated traffic on otherwise public market data. The token is
|
|
9
|
+
* supplied by the caller — this package never holds Lighter credentials.
|
|
10
|
+
*/
|
|
11
|
+
|
|
6
12
|
declare class LighterTransport extends BaseTransport {
|
|
7
13
|
readonly connector: Connector;
|
|
14
|
+
private readonly authTokenProvider;
|
|
15
|
+
private cachedAuthToken;
|
|
16
|
+
private authRefreshTimer;
|
|
17
|
+
private mintInFlight;
|
|
18
|
+
private wantsConnection;
|
|
19
|
+
/** Takes the caller's minter; without one this transport stays anonymous for its whole life. */
|
|
20
|
+
constructor(authTokenProvider?: AuthTokenProvider);
|
|
21
|
+
/** Holds the socket until a first token lands, so the opening subscriptions carry it. */
|
|
22
|
+
connect(): void;
|
|
23
|
+
/** Tears the socket down and drops the identity that belonged to it. */
|
|
24
|
+
disconnect(): void;
|
|
25
|
+
/** Exposes the cached token to the subscription frames, which are built synchronously. */
|
|
26
|
+
get authToken(): string | undefined;
|
|
27
|
+
/** Refreshes the cached token; the caller's provider owns expiry and request coalescing. */
|
|
28
|
+
retrieveAuthToken(): Promise<string | undefined>;
|
|
29
|
+
/** The venue's public market-data stream, which serves both authenticated and anonymous traffic. */
|
|
8
30
|
protected getWsUrl(): string;
|
|
31
|
+
/** How often the socket must speak to stay open. */
|
|
9
32
|
protected getPingIntervalMs(): number;
|
|
33
|
+
/** The keep-alive frame the venue expects. */
|
|
10
34
|
protected buildPingMessage(): WsMessage;
|
|
35
|
+
/** Opens the socket and keeps the token fresh for as long as it stays open. */
|
|
36
|
+
private openSocket;
|
|
37
|
+
/** Re-mints the cached token on a timer, because reconnects resubscribe without warning. */
|
|
38
|
+
private startAuthRefresh;
|
|
39
|
+
/** Stops the re-minting timer, so a closed socket costs nothing. */
|
|
40
|
+
private stopAuthRefresh;
|
|
11
41
|
}
|
|
12
42
|
|
|
13
43
|
export { LighterTransport };
|
|
@@ -1,15 +1,100 @@
|
|
|
1
1
|
import { BaseTransport } from './base-transport';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
5
|
+
const AUTH_REFRESH_INTERVAL_MS = 6e4;
|
|
3
6
|
class LighterTransport extends BaseTransport {
|
|
7
|
+
static {
|
|
8
|
+
__name(this, "LighterTransport");
|
|
9
|
+
}
|
|
4
10
|
connector = "lighter";
|
|
11
|
+
authTokenProvider;
|
|
12
|
+
cachedAuthToken;
|
|
13
|
+
authRefreshTimer = null;
|
|
14
|
+
mintInFlight = false;
|
|
15
|
+
wantsConnection = false;
|
|
16
|
+
/** Takes the caller's minter; without one this transport stays anonymous for its whole life. */
|
|
17
|
+
constructor(authTokenProvider) {
|
|
18
|
+
super();
|
|
19
|
+
this.authTokenProvider = authTokenProvider;
|
|
20
|
+
}
|
|
21
|
+
/** Holds the socket until a first token lands, so the opening subscriptions carry it. */
|
|
22
|
+
connect() {
|
|
23
|
+
this.wantsConnection = true;
|
|
24
|
+
if (!this.authTokenProvider || this.cachedAuthToken) {
|
|
25
|
+
this.openSocket();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (this.mintInFlight) {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.mintInFlight = true;
|
|
32
|
+
void this.retrieveAuthToken().then(() => {
|
|
33
|
+
this.mintInFlight = false;
|
|
34
|
+
if (!this.wantsConnection) {
|
|
35
|
+
this.cachedAuthToken = void 0;
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
this.openSocket();
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
/** Tears the socket down and drops the identity that belonged to it. */
|
|
42
|
+
disconnect() {
|
|
43
|
+
this.wantsConnection = false;
|
|
44
|
+
this.stopAuthRefresh();
|
|
45
|
+
this.cachedAuthToken = void 0;
|
|
46
|
+
super.disconnect();
|
|
47
|
+
}
|
|
48
|
+
/** Exposes the cached token to the subscription frames, which are built synchronously. */
|
|
49
|
+
get authToken() {
|
|
50
|
+
return this.cachedAuthToken;
|
|
51
|
+
}
|
|
52
|
+
/** Refreshes the cached token; the caller's provider owns expiry and request coalescing. */
|
|
53
|
+
async retrieveAuthToken() {
|
|
54
|
+
if (!this.authTokenProvider) {
|
|
55
|
+
return void 0;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
this.cachedAuthToken = await this.authTokenProvider();
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.warn("Failed to mint Lighter auth token, keeping the identity already in hand", error);
|
|
61
|
+
}
|
|
62
|
+
return this.cachedAuthToken;
|
|
63
|
+
}
|
|
64
|
+
/** The venue's public market-data stream, which serves both authenticated and anonymous traffic. */
|
|
5
65
|
getWsUrl() {
|
|
6
66
|
return "wss://mainnet.zklighter.elliot.ai/stream";
|
|
7
67
|
}
|
|
68
|
+
/** How often the socket must speak to stay open. */
|
|
8
69
|
getPingIntervalMs() {
|
|
9
70
|
return 6e4;
|
|
10
71
|
}
|
|
72
|
+
/** The keep-alive frame the venue expects. */
|
|
11
73
|
buildPingMessage() {
|
|
12
|
-
return {
|
|
74
|
+
return {
|
|
75
|
+
type: "ping"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
/** Opens the socket and keeps the token fresh for as long as it stays open. */
|
|
79
|
+
openSocket() {
|
|
80
|
+
super.connect();
|
|
81
|
+
this.startAuthRefresh();
|
|
82
|
+
}
|
|
83
|
+
/** Re-mints the cached token on a timer, because reconnects resubscribe without warning. */
|
|
84
|
+
startAuthRefresh() {
|
|
85
|
+
if (!this.authTokenProvider || this.authRefreshTimer) {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.authRefreshTimer = setInterval(() => {
|
|
89
|
+
void this.retrieveAuthToken();
|
|
90
|
+
}, AUTH_REFRESH_INTERVAL_MS);
|
|
91
|
+
}
|
|
92
|
+
/** Stops the re-minting timer, so a closed socket costs nothing. */
|
|
93
|
+
stopAuthRefresh() {
|
|
94
|
+
if (this.authRefreshTimer) {
|
|
95
|
+
clearInterval(this.authRefreshTimer);
|
|
96
|
+
this.authRefreshTimer = null;
|
|
97
|
+
}
|
|
13
98
|
}
|
|
14
99
|
}
|
|
15
100
|
|
package/dist/transport/okx.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { BaseTransport } from './base-transport';
|
|
2
2
|
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
5
|
class OkxTransport extends BaseTransport {
|
|
6
|
+
static {
|
|
7
|
+
__name(this, "OkxTransport");
|
|
8
|
+
}
|
|
4
9
|
connector = "okx";
|
|
5
10
|
getWsUrl() {
|
|
6
11
|
return "wss://ws.okx.com:8443/ws/v5/public";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/market-sdk",
|
|
3
|
-
"version": "0.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Pear Protocol Market SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -22,16 +22,18 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsup",
|
|
24
24
|
"clean": "rm -rf dist .turbo",
|
|
25
|
+
"test": "vitest run",
|
|
25
26
|
"typecheck": "tsc --noEmit"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
|
-
"@pear-protocol/types": "^1.
|
|
29
|
+
"@pear-protocol/types": "^1.28.0",
|
|
29
30
|
"bignumber.js": "^9.1.2",
|
|
30
31
|
"partysocket": "^1.0.3"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"@backend/typescript-config": "workspace:*",
|
|
34
35
|
"tsup": "8.5.1",
|
|
35
|
-
"typescript": "5.9.3"
|
|
36
|
+
"typescript": "5.9.3",
|
|
37
|
+
"vitest": "4.1.2"
|
|
36
38
|
}
|
|
37
39
|
}
|