@pear-protocol/exchanges-sdk 0.19.1 → 0.21.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 +1 -0
- package/dist/client/lighter/ws.js +15 -0
- package/dist/index.d.ts +4 -2
- package/dist/syncer/base.js +44 -15
- package/dist/tracker/lighter/tracker.js +6 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -34,6 +34,7 @@ require an authenticated connection. Existing `createTracker(connection)` calls
|
|
|
34
34
|
| Bybit | `bybit` | Linear Perpetuals |
|
|
35
35
|
| Hyperliquid | `hyperliquid` | Perpetuals |
|
|
36
36
|
| Lighter | `lighter` | Perpetuals |
|
|
37
|
+
| Lighter (Robinhood Chain) | `lighter_rhc` | Perpetuals |
|
|
37
38
|
| OKX | `okx` | Linear SWAP |
|
|
38
39
|
|
|
39
40
|
## Installation
|
|
@@ -12,6 +12,7 @@ class LighterWs extends BaseWs {
|
|
|
12
12
|
demo;
|
|
13
13
|
constructor(connector, demo = false) {
|
|
14
14
|
super(), this.connector = connector, this.demo = demo;
|
|
15
|
+
this.addMessageListener((data) => this.answerPing(data));
|
|
15
16
|
}
|
|
16
17
|
getWsUrl() {
|
|
17
18
|
return getLighterWsUrl(this.connector, this.demo);
|
|
@@ -32,6 +33,20 @@ class LighterWs extends BaseWs {
|
|
|
32
33
|
auth: authToken
|
|
33
34
|
});
|
|
34
35
|
}
|
|
36
|
+
/** The venue pings the client too; an unanswered ping can leave the socket open while data stops. */
|
|
37
|
+
answerPing(data) {
|
|
38
|
+
let frame;
|
|
39
|
+
try {
|
|
40
|
+
frame = JSON.parse(data);
|
|
41
|
+
} catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (frame.type === "ping") {
|
|
45
|
+
this.send({
|
|
46
|
+
type: "pong"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
35
50
|
}
|
|
36
51
|
|
|
37
52
|
export { LighterWs };
|
package/dist/index.d.ts
CHANGED
|
@@ -206,6 +206,8 @@ declare class LighterWs extends BaseWs {
|
|
|
206
206
|
protected buildPingMessage(): WsMessage;
|
|
207
207
|
/** Subscribes to one account channel with the authentication Lighter requires. */
|
|
208
208
|
subscribe(channel: string, authToken: string): void;
|
|
209
|
+
/** The venue pings the client too; an unanswered ping can leave the socket open while data stops. */
|
|
210
|
+
private answerPing;
|
|
209
211
|
}
|
|
210
212
|
|
|
211
213
|
declare class LighterExchangeWs extends ExchangeWs {
|
|
@@ -1263,8 +1265,8 @@ declare abstract class BaseAccountSyncer<ExternalFill> implements AccountSyncer
|
|
|
1263
1265
|
protected abstract isReduceOnly(fill: ExternalFill): boolean;
|
|
1264
1266
|
private loadPearState;
|
|
1265
1267
|
/**
|
|
1266
|
-
* Global per-symbol cost-basis entry of existing open positions, derived
|
|
1267
|
-
* position's
|
|
1268
|
+
* Global per-symbol cost-basis entry of existing open positions, derived from each
|
|
1269
|
+
* position's assigned ledger and remaining exposure. One call per open position today —
|
|
1268
1270
|
* can collapse to a single endpoint later.
|
|
1269
1271
|
*/
|
|
1270
1272
|
private loadEntryBySymbol;
|
package/dist/syncer/base.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computePositionEntryPrices, abs, isZero, add, multiply, divide, computeSyncPayload } from '@pear-protocol/utils';
|
|
2
2
|
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
@@ -43,23 +43,40 @@ class BaseAccountSyncer {
|
|
|
43
43
|
};
|
|
44
44
|
}
|
|
45
45
|
/**
|
|
46
|
-
* Global per-symbol cost-basis entry of existing open positions, derived
|
|
47
|
-
* position's
|
|
46
|
+
* Global per-symbol cost-basis entry of existing open positions, derived from each
|
|
47
|
+
* position's assigned ledger and remaining exposure. One call per open position today —
|
|
48
48
|
* can collapse to a single endpoint later.
|
|
49
49
|
*/
|
|
50
50
|
async loadEntryBySymbol(openPositions) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
51
|
+
const states = await Promise.all(openPositions.map(async (position) => {
|
|
52
|
+
const { fills } = await this.sdk.core.fills.details(position.id);
|
|
53
|
+
fills.sort((a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime());
|
|
54
|
+
return {
|
|
55
|
+
exposure: position.exposure,
|
|
56
|
+
entryPrices: computePositionEntryPrices(position.exposure, fills)
|
|
57
|
+
};
|
|
58
|
+
}));
|
|
59
|
+
const totals = /* @__PURE__ */ new Map();
|
|
60
|
+
for (const state of states) {
|
|
61
|
+
for (const [symbol, signedQuantity] of Object.entries(state.exposure)) {
|
|
62
|
+
const quantity = abs(signedQuantity);
|
|
63
|
+
if (isZero(quantity)) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const entry = state.entryPrices[symbol];
|
|
67
|
+
const existing = totals.get(symbol);
|
|
68
|
+
totals.set(symbol, {
|
|
69
|
+
quantity: add(existing?.quantity ?? "0", quantity).toFixed(),
|
|
70
|
+
notional: add(existing?.notional ?? "0", multiply(quantity, entry ?? "0")).toFixed(),
|
|
71
|
+
unpriced: existing?.unpriced === true || entry === void 0
|
|
72
|
+
});
|
|
73
|
+
}
|
|
58
74
|
}
|
|
59
75
|
const entryBySymbol = {};
|
|
60
|
-
for (const [symbol,
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
for (const [symbol, total] of totals) {
|
|
77
|
+
if (!total.unpriced) {
|
|
78
|
+
entryBySymbol[symbol] = divide(total.notional, total.quantity).toFixed();
|
|
79
|
+
}
|
|
63
80
|
}
|
|
64
81
|
return entryBySymbol;
|
|
65
82
|
}
|
|
@@ -86,7 +103,7 @@ class BaseAccountSyncer {
|
|
|
86
103
|
async computeAndPostSync(state, snapshot) {
|
|
87
104
|
const { openPositions, entryBySymbol, provisionalFills, watermark } = state;
|
|
88
105
|
const { exchangePositions, externalFills, firstSyncStartedAt } = snapshot;
|
|
89
|
-
|
|
106
|
+
let payload = await computeSyncPayload({
|
|
90
107
|
tradeAccountId: this.account.id,
|
|
91
108
|
connector: this.connector,
|
|
92
109
|
openPositions,
|
|
@@ -98,7 +115,18 @@ class BaseAccountSyncer {
|
|
|
98
115
|
neverSynced: watermark.neverSynced
|
|
99
116
|
});
|
|
100
117
|
if (!payload) {
|
|
101
|
-
|
|
118
|
+
if (watermark.hasPendingSynthetics !== true) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
payload = {
|
|
122
|
+
fills: [],
|
|
123
|
+
applicables: [],
|
|
124
|
+
exchangePositions,
|
|
125
|
+
meta: {
|
|
126
|
+
externalMaxTs: null
|
|
127
|
+
},
|
|
128
|
+
firstSyncTrim: null
|
|
129
|
+
};
|
|
102
130
|
}
|
|
103
131
|
const { firstSyncTrim } = payload;
|
|
104
132
|
let meta = {
|
|
@@ -122,6 +150,7 @@ class BaseAccountSyncer {
|
|
|
122
150
|
}
|
|
123
151
|
}
|
|
124
152
|
const createBody = {
|
|
153
|
+
fills: payload.fills,
|
|
125
154
|
applicables: payload.applicables,
|
|
126
155
|
exchangePositions: payload.exchangePositions,
|
|
127
156
|
meta
|
|
@@ -297,7 +297,12 @@ class LighterAccountTracker extends BaseAccountTracker {
|
|
|
297
297
|
routeRaw(raw) {
|
|
298
298
|
try {
|
|
299
299
|
const msg = JSON.parse(raw);
|
|
300
|
-
if (!msg.type)
|
|
300
|
+
if (!msg.type) {
|
|
301
|
+
if (msg.error !== void 0) {
|
|
302
|
+
this.onError(new Error(`Lighter stream error: ${JSON.stringify(msg.error)}`));
|
|
303
|
+
}
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
301
306
|
if (msg.type === "subscribed/account_all" || msg.type === "update/account_all") {
|
|
302
307
|
this.handle({
|
|
303
308
|
kind: "account_all",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/exchanges-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Pear Protocol Exchanges SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
"scripts": {
|
|
23
23
|
"build": "tsup",
|
|
24
24
|
"clean": "rm -rf dist .turbo",
|
|
25
|
-
"typecheck": "
|
|
25
|
+
"typecheck": "bunx --package bun@1.4.3 bun --check --threads=2",
|
|
26
26
|
"test": "vitest run",
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@nktkas/hyperliquid": "0.33.2",
|
|
31
|
-
"@pear-protocol/core-sdk": "^2.
|
|
32
|
-
"@pear-protocol/types": "^2.
|
|
33
|
-
"@pear-protocol/utils": "^1.
|
|
31
|
+
"@pear-protocol/core-sdk": "^2.16.0",
|
|
32
|
+
"@pear-protocol/types": "^2.24.0",
|
|
33
|
+
"@pear-protocol/utils": "^1.6.0",
|
|
34
34
|
"bignumber.js": "^11.1.1",
|
|
35
35
|
"partysocket": "^1.0.3",
|
|
36
36
|
"viem": "^2.48.8",
|