@pear-protocol/exchanges-sdk 0.5.2 → 0.6.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/dist/index.d.ts +9 -6
- package/dist/sdk.js +7 -4
- package/dist/sdk.test.js +16 -0
- package/dist/status/hyperliquid/status.js +13 -10
- package/dist/status/hyperliquid/status.test.js +8 -0
- package/dist/status/lighter/status.js +15 -10
- package/dist/status/lighter/status.test.js +11 -0
- package/dist/syncer/base.js +2 -2
- package/dist/syncer/base.test.js +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -8,7 +8,6 @@ type SupportedCredentials = HttpTypes.ExchangeCredentialsResponse['credentials']
|
|
|
8
8
|
type ExchangesSDKConfig = {
|
|
9
9
|
sdk: PearSDK;
|
|
10
10
|
demo?: boolean;
|
|
11
|
-
options?: ConnectOptions;
|
|
12
11
|
/**
|
|
13
12
|
* Optional forward-proxy for the exchange REST calls. When enabled, requests are re-pointed at
|
|
14
13
|
* `proxy.url` with the original venue host in `X-Proxy-Target`. Injected here because the SDK
|
|
@@ -368,16 +367,16 @@ interface AccountSyncer {
|
|
|
368
367
|
}
|
|
369
368
|
interface ListExternalFillsParams {
|
|
370
369
|
since: Date;
|
|
371
|
-
instruments: HttpTypes.
|
|
370
|
+
instruments: HttpTypes.InstrumentLite[];
|
|
372
371
|
openPositions: HttpTypes.OpenPositionLite[];
|
|
373
372
|
provisionalFills: HttpTypes.ProvisionalFill[];
|
|
374
373
|
}
|
|
375
374
|
interface ListExternalPositionsParams {
|
|
376
|
-
instruments: HttpTypes.
|
|
375
|
+
instruments: HttpTypes.InstrumentLite[];
|
|
377
376
|
}
|
|
378
377
|
interface ListExternalFundingParams {
|
|
379
378
|
since: Date;
|
|
380
|
-
instruments: HttpTypes.
|
|
379
|
+
instruments: HttpTypes.InstrumentLite[];
|
|
381
380
|
}
|
|
382
381
|
declare abstract class BaseAccountSyncer<ExternalFill> implements AccountSyncer {
|
|
383
382
|
protected sdk: PearSDK;
|
|
@@ -1007,10 +1006,14 @@ declare function createAccountTracker(exchange: Connector, config: CreateAccount
|
|
|
1007
1006
|
declare class ExchangesSDK {
|
|
1008
1007
|
private sdk;
|
|
1009
1008
|
private demo;
|
|
1010
|
-
private options?;
|
|
1011
1009
|
constructor(config: ExchangesSDKConfig);
|
|
1012
1010
|
connect(tradeAccountId: string): Promise<ExchangeConnection>;
|
|
1013
|
-
|
|
1011
|
+
/**
|
|
1012
|
+
* Options come in per call rather than at construction: the approval targets live in the
|
|
1013
|
+
* backend's fee-recipient source and can change, so the caller fetches (and caches) them and
|
|
1014
|
+
* passes the current values with every check.
|
|
1015
|
+
*/
|
|
1016
|
+
status(connection: ExchangeConnection, options?: ConnectOptions): Promise<AccountStatus>;
|
|
1014
1017
|
createTracker(connection: ExchangeConnection): AccountTracker;
|
|
1015
1018
|
snapshot(tradeAccountId: string): Promise<AccountSnapshot>;
|
|
1016
1019
|
snapshotAssets(tradeAccountId: string, coins: string[]): Promise<AssetSnapshotMap>;
|
package/dist/sdk.js
CHANGED
|
@@ -8,23 +8,26 @@ import { createAccountTracker } from './tracker';
|
|
|
8
8
|
class ExchangesSDK {
|
|
9
9
|
sdk;
|
|
10
10
|
demo;
|
|
11
|
-
options;
|
|
12
11
|
constructor(config) {
|
|
13
12
|
this.sdk = config.sdk;
|
|
14
13
|
this.demo = config.demo ?? false;
|
|
15
|
-
this.options = config.options;
|
|
16
14
|
if (config.proxy) configureProxy(config.proxy);
|
|
17
15
|
}
|
|
18
16
|
async connect(tradeAccountId) {
|
|
19
17
|
return this.fetchCredentials(tradeAccountId);
|
|
20
18
|
}
|
|
21
|
-
|
|
19
|
+
/**
|
|
20
|
+
* Options come in per call rather than at construction: the approval targets live in the
|
|
21
|
+
* backend's fee-recipient source and can change, so the caller fetches (and caches) them and
|
|
22
|
+
* passes the current values with every check.
|
|
23
|
+
*/
|
|
24
|
+
async status(connection, options) {
|
|
22
25
|
return checkAccountStatus(connection.connector, {
|
|
23
26
|
sdk: this.sdk,
|
|
24
27
|
connector: connection.connector,
|
|
25
28
|
account: connection.account,
|
|
26
29
|
credentials: connection.credentials,
|
|
27
|
-
statusOptions:
|
|
30
|
+
statusOptions: options,
|
|
28
31
|
demo: this.demo
|
|
29
32
|
});
|
|
30
33
|
}
|
package/dist/sdk.test.js
CHANGED
|
@@ -99,3 +99,19 @@ describe("ExchangesSDK configured network routing", () => {
|
|
|
99
99
|
expect(createAccountSyncerMock).toHaveBeenCalledWith("okx", expect.objectContaining({ demo: true }));
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
|
+
describe("ExchangesSDK status options", () => {
|
|
103
|
+
beforeEach(() => {
|
|
104
|
+
checkAccountStatusMock.mockReset().mockResolvedValue({ status: "active", reasons: [] });
|
|
105
|
+
});
|
|
106
|
+
it("passes per-call options through to the status check unchanged", async () => {
|
|
107
|
+
const options = { builderAddress: "0xabc", builderFeeTenthsBps: 60 };
|
|
108
|
+
const { exchangesSdk } = buildSdk();
|
|
109
|
+
await exchangesSdk.status(connection, options);
|
|
110
|
+
expect(checkAccountStatusMock).toHaveBeenCalledWith("okx", expect.objectContaining({ statusOptions: options }));
|
|
111
|
+
});
|
|
112
|
+
it("leaves the options absent when the caller passes none", async () => {
|
|
113
|
+
const { exchangesSdk } = buildSdk();
|
|
114
|
+
await exchangesSdk.status(connection);
|
|
115
|
+
expect(checkAccountStatusMock).toHaveBeenCalledWith("okx", expect.objectContaining({ statusOptions: void 0 }));
|
|
116
|
+
});
|
|
117
|
+
});
|
|
@@ -6,7 +6,8 @@ import { warningStatus } from '../base';
|
|
|
6
6
|
async function checkHyperliquidAccountStatus(config) {
|
|
7
7
|
const reasons = [];
|
|
8
8
|
const options = config.statusOptions;
|
|
9
|
-
|
|
9
|
+
const builderAddress = options?.builderAddress;
|
|
10
|
+
if (builderAddress && !options?.builderFeeTenthsBps) {
|
|
10
11
|
throw new Error("Missing required hyperliquid connect options");
|
|
11
12
|
}
|
|
12
13
|
const helper = createExchangeHelper({ exchange: "hyperliquid", demo: config.demo });
|
|
@@ -34,15 +35,17 @@ async function checkHyperliquidAccountStatus(config) {
|
|
|
34
35
|
reasons.push("hyperliquid_api_wallet_invalid");
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
if (builderAddress) {
|
|
39
|
+
try {
|
|
40
|
+
const approved = await helper.checkBuilderFeeApproval(
|
|
41
|
+
approvalAddress,
|
|
42
|
+
builderAddress,
|
|
43
|
+
options?.builderFeeTenthsBps
|
|
44
|
+
);
|
|
45
|
+
if (!approved) reasons.push("hyperliquid_builder_fee_not_approved");
|
|
46
|
+
} catch {
|
|
47
|
+
reasons.push("hyperliquid_builder_fee_not_approved");
|
|
48
|
+
}
|
|
46
49
|
}
|
|
47
50
|
return warningStatus(reasons);
|
|
48
51
|
}
|
|
@@ -83,4 +83,12 @@ describe("checkHyperliquidAccountStatus", () => {
|
|
|
83
83
|
config.statusOptions = { builderAddress };
|
|
84
84
|
await expect(checkHyperliquidAccountStatus(config)).rejects.toThrow("Missing required hyperliquid connect options");
|
|
85
85
|
});
|
|
86
|
+
it("skips the builder-fee check when no builder address is configured, keeping the other checks", async () => {
|
|
87
|
+
const config = statusConfig();
|
|
88
|
+
config.statusOptions = { builderFeeTenthsBps: 10 };
|
|
89
|
+
const status = await checkHyperliquidAccountStatus(config);
|
|
90
|
+
expect(status).toEqual({ status: "active", reasons: [] });
|
|
91
|
+
expect(checkBuilderFeeApprovalMock).not.toHaveBeenCalled();
|
|
92
|
+
expect(checkApiWalletMock).toHaveBeenCalled();
|
|
93
|
+
});
|
|
86
94
|
});
|
|
@@ -5,7 +5,10 @@ import { warningStatus } from '../base';
|
|
|
5
5
|
async function checkLighterAccountStatus(config) {
|
|
6
6
|
const reasons = [];
|
|
7
7
|
const options = config.statusOptions;
|
|
8
|
-
|
|
8
|
+
const integratorAccountIndex = options?.integratorAccountIndex;
|
|
9
|
+
const integratorMinPerpsMakerFee = options?.integratorMinPerpsMakerFee;
|
|
10
|
+
const integratorMinPerpsTakerFee = options?.integratorMinPerpsTakerFee;
|
|
11
|
+
if (integratorAccountIndex !== void 0 && (integratorMinPerpsMakerFee === void 0 || integratorMinPerpsTakerFee === void 0)) {
|
|
9
12
|
throw new Error("Missing required lighter connect options");
|
|
10
13
|
}
|
|
11
14
|
const credentials = config.credentials;
|
|
@@ -24,15 +27,17 @@ async function checkLighterAccountStatus(config) {
|
|
|
24
27
|
reasons.push("lighter_api_key_invalid");
|
|
25
28
|
}
|
|
26
29
|
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
if (integratorAccountIndex !== void 0 && integratorMinPerpsMakerFee !== void 0 && integratorMinPerpsTakerFee !== void 0) {
|
|
31
|
+
try {
|
|
32
|
+
const helper = createExchangeHelper({ exchange: "lighter", sdk: config.sdk, demo: config.demo });
|
|
33
|
+
const approved = await helper.checkIntegratorApproval(accountIndex, integratorAccountIndex, {
|
|
34
|
+
minPerpsMakerFee: integratorMinPerpsMakerFee,
|
|
35
|
+
minPerpsTakerFee: integratorMinPerpsTakerFee
|
|
36
|
+
});
|
|
37
|
+
if (!approved) reasons.push("lighter_integrator_not_approved");
|
|
38
|
+
} catch {
|
|
39
|
+
reasons.push("lighter_integrator_not_approved");
|
|
40
|
+
}
|
|
36
41
|
}
|
|
37
42
|
return warningStatus(reasons);
|
|
38
43
|
}
|
|
@@ -72,4 +72,15 @@ describe("checkLighterAccountStatus", () => {
|
|
|
72
72
|
);
|
|
73
73
|
expect(fetchAccountMetadataMock).not.toHaveBeenCalled();
|
|
74
74
|
});
|
|
75
|
+
it("skips the integrator check when no integrator is configured, keeping the api-key check", async () => {
|
|
76
|
+
const noIntegrator = config();
|
|
77
|
+
noIntegrator.demo = false;
|
|
78
|
+
noIntegrator.statusOptions = {
|
|
79
|
+
integratorMinPerpsMakerFee: 500,
|
|
80
|
+
integratorMinPerpsTakerFee: 600
|
|
81
|
+
};
|
|
82
|
+
await expect(checkLighterAccountStatus(noIntegrator)).resolves.toEqual({ status: "active", reasons: [] });
|
|
83
|
+
expect(checkIntegratorApprovalMock).not.toHaveBeenCalled();
|
|
84
|
+
expect(fetchAccountMetadataMock).toHaveBeenCalled();
|
|
85
|
+
});
|
|
75
86
|
});
|
package/dist/syncer/base.js
CHANGED
|
@@ -19,7 +19,7 @@ class BaseAccountSyncer {
|
|
|
19
19
|
const [{ positions: openPositions }, { fills: provisionalFills }, { instruments }, watermark] = await Promise.all([
|
|
20
20
|
this.sdk.core.positions.openLite(),
|
|
21
21
|
this.sdk.core.fills.provisionals(),
|
|
22
|
-
this.sdk.core.instruments.
|
|
22
|
+
this.sdk.core.instruments.lite({ connector: this.connector }),
|
|
23
23
|
this.sdk.core.sync.fills.since({ overlapMs: OVERLAP_MS })
|
|
24
24
|
]);
|
|
25
25
|
const entryBySymbol = await this.loadEntryBySymbol(openPositions);
|
|
@@ -111,7 +111,7 @@ class BaseAccountSyncer {
|
|
|
111
111
|
*/
|
|
112
112
|
async synchronizeFunding() {
|
|
113
113
|
const [{ instruments }, watermark] = await Promise.all([
|
|
114
|
-
this.sdk.core.instruments.
|
|
114
|
+
this.sdk.core.instruments.lite({ connector: this.connector }),
|
|
115
115
|
this.sdk.core.sync.funding.since({ overlapMs: 6e4 })
|
|
116
116
|
]);
|
|
117
117
|
const payments = await this.listExternalFunding({ since: new Date(watermark.sinceMs), instruments });
|
package/dist/syncer/base.test.js
CHANGED
|
@@ -50,7 +50,7 @@ function makeSyncer(args) {
|
|
|
50
50
|
core: {
|
|
51
51
|
positions: { openLite: vi.fn().mockResolvedValue({ positions: [] }) },
|
|
52
52
|
fills: { provisionals: vi.fn().mockResolvedValue({ fills: [] }), details: vi.fn() },
|
|
53
|
-
instruments: {
|
|
53
|
+
instruments: { lite: vi.fn().mockResolvedValue({ instruments: [] }) },
|
|
54
54
|
sync
|
|
55
55
|
}
|
|
56
56
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pear-protocol/exchanges-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Pear Protocol Exchanges SDK",
|
|
5
5
|
"private": false,
|
|
6
6
|
"type": "module",
|
|
@@ -27,9 +27,9 @@
|
|
|
27
27
|
"test:watch": "vitest"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@pear-protocol/core-sdk": "^1.
|
|
31
|
-
"@pear-protocol/types": "^1.
|
|
32
|
-
"@pear-protocol/utils": "^0.
|
|
30
|
+
"@pear-protocol/core-sdk": "^1.12.0",
|
|
31
|
+
"@pear-protocol/types": "^1.15.0",
|
|
32
|
+
"@pear-protocol/utils": "^0.4.0",
|
|
33
33
|
"bignumber.js": "^11.1.1",
|
|
34
34
|
"partysocket": "^1.0.3",
|
|
35
35
|
"viem": "^2.48.8"
|