@pear-protocol/exchanges-sdk 0.1.9 → 0.1.11
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/dist/index.d.ts
CHANGED
|
@@ -470,7 +470,7 @@ declare class LighterAccountSyncer extends BaseAccountSyncer<LighterRawTrade> {
|
|
|
470
470
|
private getPositionSizeBefore;
|
|
471
471
|
private getTradeFee;
|
|
472
472
|
private getIntegratorFee;
|
|
473
|
-
private
|
|
473
|
+
private feeTickToAmount;
|
|
474
474
|
}
|
|
475
475
|
|
|
476
476
|
interface OkxRawFill {
|
|
@@ -44,8 +44,8 @@ class LighterAccountSyncer extends BaseAccountSyncer {
|
|
|
44
44
|
side,
|
|
45
45
|
reduceOnly: this.isReduceOnly(trade),
|
|
46
46
|
timestamp: trade.timestamp,
|
|
47
|
-
tradeFee: this.
|
|
48
|
-
builderFee: this.
|
|
47
|
+
tradeFee: this.getTradeFee(trade, accountSide),
|
|
48
|
+
builderFee: this.getIntegratorFee(trade, accountSide),
|
|
49
49
|
liquidation: trade.type === "liquidation" ? { mark: trade.price } : null
|
|
50
50
|
});
|
|
51
51
|
return acc;
|
|
@@ -81,13 +81,17 @@ class LighterAccountSyncer extends BaseAccountSyncer {
|
|
|
81
81
|
return new BigNumber(value ?? 0);
|
|
82
82
|
}
|
|
83
83
|
getTradeFee(trade, side) {
|
|
84
|
-
|
|
84
|
+
const rateTick = this.isMaker(trade, side) ? trade.maker_fee : trade.taker_fee;
|
|
85
|
+
return this.feeTickToAmount(rateTick, trade.usd_amount);
|
|
85
86
|
}
|
|
86
87
|
getIntegratorFee(trade, side) {
|
|
87
|
-
|
|
88
|
+
const rateTick = this.isMaker(trade, side) ? trade.integrator_maker_fee : trade.integrator_taker_fee;
|
|
89
|
+
return this.feeTickToAmount(rateTick, trade.usd_amount);
|
|
88
90
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
+
// Lighter fees come as ppm rates (÷1e6), not amounts — the charged fee is that rate times the trade's USD notional.
|
|
92
|
+
feeTickToAmount(rateTick, usdAmount) {
|
|
93
|
+
const rate = new BigNumber(rateTick ?? 0).abs().div(LIGHTER_FEE_TICK_DENOMINATOR);
|
|
94
|
+
return rate.times(usdAmount).toFixed();
|
|
91
95
|
}
|
|
92
96
|
}
|
|
93
97
|
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { vi, describe, beforeEach, it, expect } from 'vitest';
|
|
2
|
+
import { fetchTradesSince } from '../../client/lighter/rest';
|
|
3
|
+
import { LighterAccountSyncer } from './syncer';
|
|
4
|
+
|
|
5
|
+
vi.mock("../../client/lighter/rest", () => ({
|
|
6
|
+
fetchTradesSince: vi.fn()
|
|
7
|
+
}));
|
|
8
|
+
const ACCOUNT_INDEX = 87440;
|
|
9
|
+
const TRADES = [
|
|
10
|
+
{
|
|
11
|
+
// Real /trades row — account is taker via bid, integrator fee only:
|
|
12
|
+
// builderFee = 200/1e6 × 49.279769, tradeFee = 0.
|
|
13
|
+
trade_id: 23432371018,
|
|
14
|
+
trade_id_str: "23432371018",
|
|
15
|
+
type: "trade",
|
|
16
|
+
market_id: 1,
|
|
17
|
+
size: "0.00077",
|
|
18
|
+
price: "63999.7",
|
|
19
|
+
usd_amount: "49.279769",
|
|
20
|
+
ask_account_id: 281474976656674,
|
|
21
|
+
bid_account_id: ACCOUNT_INDEX,
|
|
22
|
+
is_maker_ask: true,
|
|
23
|
+
timestamp: 1782044759045,
|
|
24
|
+
taker_position_size_before: "0.00000",
|
|
25
|
+
maker_position_size_before: "0.00000",
|
|
26
|
+
integrator_taker_fee: 200
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
// Real /trades row — account is taker via ask, base taker fee only:
|
|
30
|
+
// tradeFee = 280/1e6 × 11.896090, builderFee = 0.
|
|
31
|
+
trade_id: 22207283246,
|
|
32
|
+
trade_id_str: "22207283246",
|
|
33
|
+
type: "trade",
|
|
34
|
+
market_id: 1,
|
|
35
|
+
size: "0.00019",
|
|
36
|
+
price: "62611.0",
|
|
37
|
+
usd_amount: "11.896090",
|
|
38
|
+
ask_account_id: ACCOUNT_INDEX,
|
|
39
|
+
bid_account_id: 587622,
|
|
40
|
+
is_maker_ask: false,
|
|
41
|
+
timestamp: 1781010160339,
|
|
42
|
+
taker_fee: 280,
|
|
43
|
+
taker_position_size_before: "0.00019",
|
|
44
|
+
maker_position_size_before: "0.00000"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
// Real /trades row — no fee fields at all, so both fees resolve to 0.
|
|
48
|
+
trade_id: 23432472386,
|
|
49
|
+
trade_id_str: "23432472386",
|
|
50
|
+
type: "trade",
|
|
51
|
+
market_id: 1,
|
|
52
|
+
size: "0.00079",
|
|
53
|
+
price: "63973.6",
|
|
54
|
+
usd_amount: "50.539144",
|
|
55
|
+
ask_account_id: 726722,
|
|
56
|
+
bid_account_id: ACCOUNT_INDEX,
|
|
57
|
+
is_maker_ask: true,
|
|
58
|
+
timestamp: 1782044888766,
|
|
59
|
+
taker_position_size_before: "-0.00079",
|
|
60
|
+
maker_position_size_before: "0.00000"
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
// Synthetic row — the real payload only carries taker-side fees, so we construct a
|
|
64
|
+
// maker fill (account is the resting ask) to cover the maker_fee / integrator_maker_fee branch:
|
|
65
|
+
// tradeFee = 100/1e6 × 100, builderFee = 50/1e6 × 100.
|
|
66
|
+
trade_id: 90000000001,
|
|
67
|
+
trade_id_str: "90000000001",
|
|
68
|
+
type: "trade",
|
|
69
|
+
market_id: 1,
|
|
70
|
+
size: "0.00100",
|
|
71
|
+
price: "65000.0",
|
|
72
|
+
usd_amount: "100.000000",
|
|
73
|
+
ask_account_id: ACCOUNT_INDEX,
|
|
74
|
+
bid_account_id: 726722,
|
|
75
|
+
is_maker_ask: true,
|
|
76
|
+
timestamp: 178205e7,
|
|
77
|
+
maker_fee: 100,
|
|
78
|
+
taker_position_size_before: "0.00000",
|
|
79
|
+
maker_position_size_before: "0.00000",
|
|
80
|
+
integrator_maker_fee: 50
|
|
81
|
+
}
|
|
82
|
+
];
|
|
83
|
+
function createSyncer() {
|
|
84
|
+
const sdk = {
|
|
85
|
+
core: { misc: { lighter: { createAuthToken: vi.fn().mockResolvedValue({ token: "test-token" }) } } }
|
|
86
|
+
};
|
|
87
|
+
const config = {
|
|
88
|
+
sdk,
|
|
89
|
+
connector: "lighter",
|
|
90
|
+
account: { exchangeIdentifier: String(ACCOUNT_INDEX) },
|
|
91
|
+
credentials: { api_key_index: 0, api_private_key: "test-key" },
|
|
92
|
+
ws: {},
|
|
93
|
+
demo: false
|
|
94
|
+
};
|
|
95
|
+
return new LighterAccountSyncer(config);
|
|
96
|
+
}
|
|
97
|
+
async function listFills() {
|
|
98
|
+
const syncer = createSyncer();
|
|
99
|
+
const params = {
|
|
100
|
+
since: /* @__PURE__ */ new Date(0),
|
|
101
|
+
instruments: [{ id: "1" }],
|
|
102
|
+
openPositions: [],
|
|
103
|
+
provisionalFills: []
|
|
104
|
+
};
|
|
105
|
+
return syncer.listExternalFills(params);
|
|
106
|
+
}
|
|
107
|
+
const byId = (fills, id) => fills.find((fill) => fill.exchangeFillId === id);
|
|
108
|
+
describe("LighterAccountSyncer fee mapping", () => {
|
|
109
|
+
beforeEach(() => {
|
|
110
|
+
vi.mocked(fetchTradesSince).mockResolvedValue(TRADES);
|
|
111
|
+
});
|
|
112
|
+
it("computes the integrator (builder) fee as the taker ppm rate times the USD notional", async () => {
|
|
113
|
+
const fills = await listFills();
|
|
114
|
+
expect(byId(fills, "23432371018")).toMatchObject({
|
|
115
|
+
symbol: "1",
|
|
116
|
+
side: "BUY",
|
|
117
|
+
tradeFee: "0",
|
|
118
|
+
builderFee: "0.0098559538"
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
it("computes the trade fee as the base taker ppm rate times the USD notional", async () => {
|
|
122
|
+
const fills = await listFills();
|
|
123
|
+
expect(byId(fills, "22207283246")).toMatchObject({
|
|
124
|
+
symbol: "1",
|
|
125
|
+
side: "SELL",
|
|
126
|
+
tradeFee: "0.0033309052",
|
|
127
|
+
builderFee: "0"
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
it("emits zero fees when the trade carries no fee fields", async () => {
|
|
131
|
+
const fills = await listFills();
|
|
132
|
+
expect(byId(fills, "23432472386")).toMatchObject({
|
|
133
|
+
symbol: "1",
|
|
134
|
+
side: "BUY",
|
|
135
|
+
tradeFee: "0",
|
|
136
|
+
builderFee: "0"
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
it("reads the maker-side fee fields when the account is the maker", async () => {
|
|
140
|
+
const fills = await listFills();
|
|
141
|
+
expect(byId(fills, "90000000001")).toMatchObject({
|
|
142
|
+
symbol: "1",
|
|
143
|
+
side: "SELL",
|
|
144
|
+
tradeFee: "0.01",
|
|
145
|
+
builderFee: "0.005"
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/exchanges-sdk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "Pear Protocol Exchanges SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -22,12 +22,13 @@
|
|
|
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/core-sdk": "^0.0
|
|
29
|
-
"@pear-protocol/types": "^0.0
|
|
30
|
-
"@pear-protocol/utils": "^0.0.
|
|
29
|
+
"@pear-protocol/core-sdk": "^0.1.0",
|
|
30
|
+
"@pear-protocol/types": "^0.1.0",
|
|
31
|
+
"@pear-protocol/utils": "^0.0.22",
|
|
31
32
|
"bignumber.js": "^11.1.1",
|
|
32
33
|
"partysocket": "^1.0.3",
|
|
33
34
|
"viem": "^2.48.8"
|
|
@@ -35,6 +36,7 @@
|
|
|
35
36
|
"devDependencies": {
|
|
36
37
|
"@backend/typescript-config": "workspace:*",
|
|
37
38
|
"tsup": "8.5.1",
|
|
38
|
-
"typescript": "5.9.3"
|
|
39
|
+
"typescript": "5.9.3",
|
|
40
|
+
"vitest": "4.1.2"
|
|
39
41
|
}
|
|
40
42
|
}
|