@pear-protocol/exchanges-sdk 0.5.1 → 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 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.Instrument[];
370
+ instruments: HttpTypes.InstrumentLite[];
372
371
  openPositions: HttpTypes.OpenPositionLite[];
373
372
  provisionalFills: HttpTypes.ProvisionalFill[];
374
373
  }
375
374
  interface ListExternalPositionsParams {
376
- instruments: HttpTypes.Instrument[];
375
+ instruments: HttpTypes.InstrumentLite[];
377
376
  }
378
377
  interface ListExternalFundingParams {
379
378
  since: Date;
380
- instruments: HttpTypes.Instrument[];
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
- status(connection: ExchangeConnection): Promise<AccountStatus>;
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
- async status(connection) {
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: this.options,
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
- if (!options?.builderAddress || !options.builderFeeTenthsBps) {
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
- try {
38
- const approved = await helper.checkBuilderFeeApproval(
39
- approvalAddress,
40
- options.builderAddress,
41
- options.builderFeeTenthsBps
42
- );
43
- if (!approved) reasons.push("hyperliquid_builder_fee_not_approved");
44
- } catch {
45
- reasons.push("hyperliquid_builder_fee_not_approved");
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
- if (options?.integratorAccountIndex === void 0 || options.integratorMinPerpsMakerFee === void 0 || options.integratorMinPerpsTakerFee === void 0) {
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
- try {
28
- const helper = createExchangeHelper({ exchange: "lighter", sdk: config.sdk, demo: config.demo });
29
- const approved = await helper.checkIntegratorApproval(accountIndex, options.integratorAccountIndex, {
30
- minPerpsMakerFee: options.integratorMinPerpsMakerFee,
31
- minPerpsTakerFee: options.integratorMinPerpsTakerFee
32
- });
33
- if (!approved) reasons.push("lighter_integrator_not_approved");
34
- } catch {
35
- reasons.push("lighter_integrator_not_approved");
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
  });
@@ -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.list({ connector: this.connector }),
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.list({ connector: this.connector }),
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 });
@@ -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: { list: vi.fn().mockResolvedValue({ 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.5.1",
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.7.0",
31
- "@pear-protocol/types": "^1.9.0",
32
- "@pear-protocol/utils": "^0.2.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"