pmxtjs 2.34.1 → 2.34.3

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/pmxt/client.ts CHANGED
@@ -453,6 +453,54 @@ export abstract class Exchange {
453
453
  return headers;
454
454
  }
455
455
 
456
+ /**
457
+ * Resolve the current sidecar base URL.
458
+ *
459
+ * For hosted mode the configured basePath is returned as-is.
460
+ * For local mode the port is re-read from the lock file on every
461
+ * call so we pick up sidecar restarts that land on a different port.
462
+ */
463
+ private resolveBaseUrl(): string {
464
+ if (this.isHosted) return this.config.basePath;
465
+ const port = this.serverManager.getRunningPort();
466
+ return `http://localhost:${port}`;
467
+ }
468
+
469
+ /**
470
+ * Execute a fetch with retry on connection failures.
471
+ *
472
+ * Only retries on connection-level errors (ECONNREFUSED, ECONNRESET) —
473
+ * never on HTTP responses (4xx, 5xx). On first connection failure,
474
+ * attempts to restart the sidecar.
475
+ */
476
+ private async fetchWithRetry(
477
+ input: RequestInfo | URL,
478
+ init?: RequestInit,
479
+ ): Promise<Response> {
480
+ const delays = [200, 500, 1000];
481
+ let lastError: unknown;
482
+
483
+ for (let attempt = 0; attempt <= delays.length; attempt++) {
484
+ try {
485
+ return await fetch(input, init);
486
+ } catch (error) {
487
+ lastError = error;
488
+ if (attempt >= delays.length) break;
489
+
490
+ // Connection failed — try to restart the sidecar on first failure
491
+ if (attempt === 0 && !this.isHosted) {
492
+ try {
493
+ await this.serverManager.ensureServerRunning();
494
+ } catch {
495
+ // Restart failed — continue retrying anyway
496
+ }
497
+ }
498
+ await new Promise(resolve => setTimeout(resolve, delays[attempt]));
499
+ }
500
+ }
501
+ throw lastError;
502
+ }
503
+
456
504
  // Low-Level API Access
457
505
 
458
506
  /**
@@ -473,14 +521,14 @@ export abstract class Exchange {
473
521
  async callApi(operationId: string, params?: Record<string, any>): Promise<any> {
474
522
  await this.initPromise;
475
523
  try {
476
- const url = `${this.config.basePath}/api/${this.exchangeName}/callApi`;
524
+ const url = `${this.resolveBaseUrl()}/api/${this.exchangeName}/callApi`;
477
525
 
478
526
  const requestBody: any = {
479
527
  args: [operationId, params],
480
528
  credentials: this.getCredentials()
481
529
  };
482
530
 
483
- const response = await fetch(url, {
531
+ const response = await this.fetchWithRetry(url, {
484
532
  method: 'POST',
485
533
  headers: {
486
534
  'Content-Type': 'application/json',
@@ -529,13 +577,14 @@ export abstract class Exchange {
529
577
  query: Record<string, unknown>,
530
578
  args: unknown[],
531
579
  ): Promise<any> {
532
- const baseUrl = `${this.config.basePath}/api/${this.exchangeName}/${methodName}`;
580
+ const resolvedBase = this.resolveBaseUrl();
581
+ const baseUrl = `${resolvedBase}/api/${this.exchangeName}/${methodName}`;
533
582
  const hasCredentials = this.getCredentials() !== undefined;
534
583
 
535
584
  if (!hasCredentials && !this._getReadsUnsupported && !queryHasNestedObject(query)) {
536
585
  const qs = buildSidecarQueryString(query);
537
586
  const getUrl = qs ? `${baseUrl}?${qs}` : baseUrl;
538
- const response = await fetch(getUrl, {
587
+ const response = await this.fetchWithRetry(getUrl, {
539
588
  method: 'GET',
540
589
  headers: this.getAuthHeaders(),
541
590
  });
@@ -559,7 +608,7 @@ export abstract class Exchange {
559
608
  }
560
609
 
561
610
  // POST fallback — identical to the original per-method template.
562
- const response = await fetch(baseUrl, {
611
+ const response = await this.fetchWithRetry(baseUrl, {
563
612
  method: 'POST',
564
613
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
565
614
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
@@ -581,14 +630,17 @@ export abstract class Exchange {
581
630
  try {
582
631
  const args: any[] = [];
583
632
  args.push(reload);
584
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/loadMarkets`, {
633
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/loadMarkets`, {
585
634
  method: 'POST',
586
635
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
587
636
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
588
637
  });
589
638
  if (!response.ok) {
590
- const error = await response.json().catch(() => ({}));
591
- throw new Error(error.error?.message || response.statusText);
639
+ const body = await response.json().catch(() => ({}));
640
+ if (body.error && typeof body.error === "object") {
641
+ throw fromServerError(body.error);
642
+ }
643
+ throw new PmxtError(body.error?.message || response.statusText);
592
644
  }
593
645
  const json = await response.json();
594
646
  const data = this.handleResponse(json);
@@ -598,7 +650,8 @@ export abstract class Exchange {
598
650
  }
599
651
  return result;
600
652
  } catch (error) {
601
- throw new Error(`Failed to loadMarkets: ${error}`);
653
+ if (error instanceof PmxtError) throw error;
654
+ throw new PmxtError(`Failed to loadMarkets: ${error}`);
602
655
  }
603
656
  }
604
657
 
@@ -607,20 +660,24 @@ export abstract class Exchange {
607
660
  try {
608
661
  const args: any[] = [];
609
662
  if (params !== undefined) args.push(params);
610
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchMarkets`, {
663
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMarkets`, {
611
664
  method: 'POST',
612
665
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
613
666
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
614
667
  });
615
668
  if (!response.ok) {
616
- const error = await response.json().catch(() => ({}));
617
- throw new Error(error.error?.message || response.statusText);
669
+ const body = await response.json().catch(() => ({}));
670
+ if (body.error && typeof body.error === "object") {
671
+ throw fromServerError(body.error);
672
+ }
673
+ throw new PmxtError(body.error?.message || response.statusText);
618
674
  }
619
675
  const json = await response.json();
620
676
  const data = this.handleResponse(json);
621
677
  return data.map(convertMarket);
622
678
  } catch (error) {
623
- throw new Error(`Failed to fetchMarkets: ${error}`);
679
+ if (error instanceof PmxtError) throw error;
680
+ throw new PmxtError(`Failed to fetchMarkets: ${error}`);
624
681
  }
625
682
  }
626
683
 
@@ -629,14 +686,17 @@ export abstract class Exchange {
629
686
  try {
630
687
  const args: any[] = [];
631
688
  if (params !== undefined) args.push(params);
632
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchMarketsPaginated`, {
689
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMarketsPaginated`, {
633
690
  method: 'POST',
634
691
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
635
692
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
636
693
  });
637
694
  if (!response.ok) {
638
- const error = await response.json().catch(() => ({}));
639
- throw new Error(error.error?.message || response.statusText);
695
+ const body = await response.json().catch(() => ({}));
696
+ if (body.error && typeof body.error === "object") {
697
+ throw fromServerError(body.error);
698
+ }
699
+ throw new PmxtError(body.error?.message || response.statusText);
640
700
  }
641
701
  const json = await response.json();
642
702
  const data = this.handleResponse(json);
@@ -646,7 +706,8 @@ export abstract class Exchange {
646
706
  nextCursor: data.nextCursor,
647
707
  };
648
708
  } catch (error) {
649
- throw new Error(`Failed to fetchMarketsPaginated: ${error}`);
709
+ if (error instanceof PmxtError) throw error;
710
+ throw new PmxtError(`Failed to fetchMarketsPaginated: ${error}`);
650
711
  }
651
712
  }
652
713
 
@@ -655,20 +716,24 @@ export abstract class Exchange {
655
716
  try {
656
717
  const args: any[] = [];
657
718
  if (params !== undefined) args.push(params);
658
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchEvents`, {
719
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchEvents`, {
659
720
  method: 'POST',
660
721
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
661
722
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
662
723
  });
663
724
  if (!response.ok) {
664
- const error = await response.json().catch(() => ({}));
665
- throw new Error(error.error?.message || response.statusText);
725
+ const body = await response.json().catch(() => ({}));
726
+ if (body.error && typeof body.error === "object") {
727
+ throw fromServerError(body.error);
728
+ }
729
+ throw new PmxtError(body.error?.message || response.statusText);
666
730
  }
667
731
  const json = await response.json();
668
732
  const data = this.handleResponse(json);
669
733
  return data.map(convertEvent);
670
734
  } catch (error) {
671
- throw new Error(`Failed to fetchEvents: ${error}`);
735
+ if (error instanceof PmxtError) throw error;
736
+ throw new PmxtError(`Failed to fetchEvents: ${error}`);
672
737
  }
673
738
  }
674
739
 
@@ -677,20 +742,24 @@ export abstract class Exchange {
677
742
  try {
678
743
  const args: any[] = [];
679
744
  if (params !== undefined) args.push(params);
680
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchMarket`, {
745
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMarket`, {
681
746
  method: 'POST',
682
747
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
683
748
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
684
749
  });
685
750
  if (!response.ok) {
686
- const error = await response.json().catch(() => ({}));
687
- throw new Error(error.error?.message || response.statusText);
751
+ const body = await response.json().catch(() => ({}));
752
+ if (body.error && typeof body.error === "object") {
753
+ throw fromServerError(body.error);
754
+ }
755
+ throw new PmxtError(body.error?.message || response.statusText);
688
756
  }
689
757
  const json = await response.json();
690
758
  const data = this.handleResponse(json);
691
759
  return convertMarket(data);
692
760
  } catch (error) {
693
- throw new Error(`Failed to fetchMarket: ${error}`);
761
+ if (error instanceof PmxtError) throw error;
762
+ throw new PmxtError(`Failed to fetchMarket: ${error}`);
694
763
  }
695
764
  }
696
765
 
@@ -699,20 +768,24 @@ export abstract class Exchange {
699
768
  try {
700
769
  const args: any[] = [];
701
770
  if (params !== undefined) args.push(params);
702
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchEvent`, {
771
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchEvent`, {
703
772
  method: 'POST',
704
773
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
705
774
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
706
775
  });
707
776
  if (!response.ok) {
708
- const error = await response.json().catch(() => ({}));
709
- throw new Error(error.error?.message || response.statusText);
777
+ const body = await response.json().catch(() => ({}));
778
+ if (body.error && typeof body.error === "object") {
779
+ throw fromServerError(body.error);
780
+ }
781
+ throw new PmxtError(body.error?.message || response.statusText);
710
782
  }
711
783
  const json = await response.json();
712
784
  const data = this.handleResponse(json);
713
785
  return convertEvent(data);
714
786
  } catch (error) {
715
- throw new Error(`Failed to fetchEvent: ${error}`);
787
+ if (error instanceof PmxtError) throw error;
788
+ throw new PmxtError(`Failed to fetchEvent: ${error}`);
716
789
  }
717
790
  }
718
791
 
@@ -721,20 +794,24 @@ export abstract class Exchange {
721
794
  try {
722
795
  const args: any[] = [];
723
796
  args.push(id);
724
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchOrderBook`, {
797
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchOrderBook`, {
725
798
  method: 'POST',
726
799
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
727
800
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
728
801
  });
729
802
  if (!response.ok) {
730
- const error = await response.json().catch(() => ({}));
731
- throw new Error(error.error?.message || response.statusText);
803
+ const body = await response.json().catch(() => ({}));
804
+ if (body.error && typeof body.error === "object") {
805
+ throw fromServerError(body.error);
806
+ }
807
+ throw new PmxtError(body.error?.message || response.statusText);
732
808
  }
733
809
  const json = await response.json();
734
810
  const data = this.handleResponse(json);
735
811
  return convertOrderBook(data);
736
812
  } catch (error) {
737
- throw new Error(`Failed to fetchOrderBook: ${error}`);
813
+ if (error instanceof PmxtError) throw error;
814
+ throw new PmxtError(`Failed to fetchOrderBook: ${error}`);
738
815
  }
739
816
  }
740
817
 
@@ -743,20 +820,24 @@ export abstract class Exchange {
743
820
  try {
744
821
  const args: any[] = [];
745
822
  args.push(built);
746
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/submitOrder`, {
823
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/submitOrder`, {
747
824
  method: 'POST',
748
825
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
749
826
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
750
827
  });
751
828
  if (!response.ok) {
752
- const error = await response.json().catch(() => ({}));
753
- throw new Error(error.error?.message || response.statusText);
829
+ const body = await response.json().catch(() => ({}));
830
+ if (body.error && typeof body.error === "object") {
831
+ throw fromServerError(body.error);
832
+ }
833
+ throw new PmxtError(body.error?.message || response.statusText);
754
834
  }
755
835
  const json = await response.json();
756
836
  const data = this.handleResponse(json);
757
837
  return convertOrder(data);
758
838
  } catch (error) {
759
- throw new Error(`Failed to submitOrder: ${error}`);
839
+ if (error instanceof PmxtError) throw error;
840
+ throw new PmxtError(`Failed to submitOrder: ${error}`);
760
841
  }
761
842
  }
762
843
 
@@ -765,20 +846,24 @@ export abstract class Exchange {
765
846
  try {
766
847
  const args: any[] = [];
767
848
  args.push(orderId);
768
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/cancelOrder`, {
849
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/cancelOrder`, {
769
850
  method: 'POST',
770
851
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
771
852
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
772
853
  });
773
854
  if (!response.ok) {
774
- const error = await response.json().catch(() => ({}));
775
- throw new Error(error.error?.message || response.statusText);
855
+ const body = await response.json().catch(() => ({}));
856
+ if (body.error && typeof body.error === "object") {
857
+ throw fromServerError(body.error);
858
+ }
859
+ throw new PmxtError(body.error?.message || response.statusText);
776
860
  }
777
861
  const json = await response.json();
778
862
  const data = this.handleResponse(json);
779
863
  return convertOrder(data);
780
864
  } catch (error) {
781
- throw new Error(`Failed to cancelOrder: ${error}`);
865
+ if (error instanceof PmxtError) throw error;
866
+ throw new PmxtError(`Failed to cancelOrder: ${error}`);
782
867
  }
783
868
  }
784
869
 
@@ -787,20 +872,24 @@ export abstract class Exchange {
787
872
  try {
788
873
  const args: any[] = [];
789
874
  args.push(orderId);
790
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchOrder`, {
875
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchOrder`, {
791
876
  method: 'POST',
792
877
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
793
878
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
794
879
  });
795
880
  if (!response.ok) {
796
- const error = await response.json().catch(() => ({}));
797
- throw new Error(error.error?.message || response.statusText);
881
+ const body = await response.json().catch(() => ({}));
882
+ if (body.error && typeof body.error === "object") {
883
+ throw fromServerError(body.error);
884
+ }
885
+ throw new PmxtError(body.error?.message || response.statusText);
798
886
  }
799
887
  const json = await response.json();
800
888
  const data = this.handleResponse(json);
801
889
  return convertOrder(data);
802
890
  } catch (error) {
803
- throw new Error(`Failed to fetchOrder: ${error}`);
891
+ if (error instanceof PmxtError) throw error;
892
+ throw new PmxtError(`Failed to fetchOrder: ${error}`);
804
893
  }
805
894
  }
806
895
 
@@ -809,20 +898,24 @@ export abstract class Exchange {
809
898
  try {
810
899
  const args: any[] = [];
811
900
  if (marketId !== undefined) args.push(marketId);
812
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchOpenOrders`, {
901
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchOpenOrders`, {
813
902
  method: 'POST',
814
903
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
815
904
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
816
905
  });
817
906
  if (!response.ok) {
818
- const error = await response.json().catch(() => ({}));
819
- throw new Error(error.error?.message || response.statusText);
907
+ const body = await response.json().catch(() => ({}));
908
+ if (body.error && typeof body.error === "object") {
909
+ throw fromServerError(body.error);
910
+ }
911
+ throw new PmxtError(body.error?.message || response.statusText);
820
912
  }
821
913
  const json = await response.json();
822
914
  const data = this.handleResponse(json);
823
915
  return data.map(convertOrder);
824
916
  } catch (error) {
825
- throw new Error(`Failed to fetchOpenOrders: ${error}`);
917
+ if (error instanceof PmxtError) throw error;
918
+ throw new PmxtError(`Failed to fetchOpenOrders: ${error}`);
826
919
  }
827
920
  }
828
921
 
@@ -831,20 +924,24 @@ export abstract class Exchange {
831
924
  try {
832
925
  const args: any[] = [];
833
926
  if (params !== undefined) args.push(params);
834
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchMyTrades`, {
927
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMyTrades`, {
835
928
  method: 'POST',
836
929
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
837
930
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
838
931
  });
839
932
  if (!response.ok) {
840
- const error = await response.json().catch(() => ({}));
841
- throw new Error(error.error?.message || response.statusText);
933
+ const body = await response.json().catch(() => ({}));
934
+ if (body.error && typeof body.error === "object") {
935
+ throw fromServerError(body.error);
936
+ }
937
+ throw new PmxtError(body.error?.message || response.statusText);
842
938
  }
843
939
  const json = await response.json();
844
940
  const data = this.handleResponse(json);
845
941
  return data.map(convertUserTrade);
846
942
  } catch (error) {
847
- throw new Error(`Failed to fetchMyTrades: ${error}`);
943
+ if (error instanceof PmxtError) throw error;
944
+ throw new PmxtError(`Failed to fetchMyTrades: ${error}`);
848
945
  }
849
946
  }
850
947
 
@@ -853,20 +950,24 @@ export abstract class Exchange {
853
950
  try {
854
951
  const args: any[] = [];
855
952
  if (params !== undefined) args.push(params);
856
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchClosedOrders`, {
953
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchClosedOrders`, {
857
954
  method: 'POST',
858
955
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
859
956
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
860
957
  });
861
958
  if (!response.ok) {
862
- const error = await response.json().catch(() => ({}));
863
- throw new Error(error.error?.message || response.statusText);
959
+ const body = await response.json().catch(() => ({}));
960
+ if (body.error && typeof body.error === "object") {
961
+ throw fromServerError(body.error);
962
+ }
963
+ throw new PmxtError(body.error?.message || response.statusText);
864
964
  }
865
965
  const json = await response.json();
866
966
  const data = this.handleResponse(json);
867
967
  return data.map(convertOrder);
868
968
  } catch (error) {
869
- throw new Error(`Failed to fetchClosedOrders: ${error}`);
969
+ if (error instanceof PmxtError) throw error;
970
+ throw new PmxtError(`Failed to fetchClosedOrders: ${error}`);
870
971
  }
871
972
  }
872
973
 
@@ -875,20 +976,24 @@ export abstract class Exchange {
875
976
  try {
876
977
  const args: any[] = [];
877
978
  if (params !== undefined) args.push(params);
878
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchAllOrders`, {
979
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchAllOrders`, {
879
980
  method: 'POST',
880
981
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
881
982
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
882
983
  });
883
984
  if (!response.ok) {
884
- const error = await response.json().catch(() => ({}));
885
- throw new Error(error.error?.message || response.statusText);
985
+ const body = await response.json().catch(() => ({}));
986
+ if (body.error && typeof body.error === "object") {
987
+ throw fromServerError(body.error);
988
+ }
989
+ throw new PmxtError(body.error?.message || response.statusText);
886
990
  }
887
991
  const json = await response.json();
888
992
  const data = this.handleResponse(json);
889
993
  return data.map(convertOrder);
890
994
  } catch (error) {
891
- throw new Error(`Failed to fetchAllOrders: ${error}`);
995
+ if (error instanceof PmxtError) throw error;
996
+ throw new PmxtError(`Failed to fetchAllOrders: ${error}`);
892
997
  }
893
998
  }
894
999
 
@@ -897,20 +1002,24 @@ export abstract class Exchange {
897
1002
  try {
898
1003
  const args: any[] = [];
899
1004
  if (address !== undefined) args.push(address);
900
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchPositions`, {
1005
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchPositions`, {
901
1006
  method: 'POST',
902
1007
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
903
1008
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
904
1009
  });
905
1010
  if (!response.ok) {
906
- const error = await response.json().catch(() => ({}));
907
- throw new Error(error.error?.message || response.statusText);
1011
+ const body = await response.json().catch(() => ({}));
1012
+ if (body.error && typeof body.error === "object") {
1013
+ throw fromServerError(body.error);
1014
+ }
1015
+ throw new PmxtError(body.error?.message || response.statusText);
908
1016
  }
909
1017
  const json = await response.json();
910
1018
  const data = this.handleResponse(json);
911
1019
  return data.map(convertPosition);
912
1020
  } catch (error) {
913
- throw new Error(`Failed to fetchPositions: ${error}`);
1021
+ if (error instanceof PmxtError) throw error;
1022
+ throw new PmxtError(`Failed to fetchPositions: ${error}`);
914
1023
  }
915
1024
  }
916
1025
 
@@ -919,20 +1028,24 @@ export abstract class Exchange {
919
1028
  try {
920
1029
  const args: any[] = [];
921
1030
  if (address !== undefined) args.push(address);
922
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/fetchBalance`, {
1031
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchBalance`, {
923
1032
  method: 'POST',
924
1033
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
925
1034
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
926
1035
  });
927
1036
  if (!response.ok) {
928
- const error = await response.json().catch(() => ({}));
929
- throw new Error(error.error?.message || response.statusText);
1037
+ const body = await response.json().catch(() => ({}));
1038
+ if (body.error && typeof body.error === "object") {
1039
+ throw fromServerError(body.error);
1040
+ }
1041
+ throw new PmxtError(body.error?.message || response.statusText);
930
1042
  }
931
1043
  const json = await response.json();
932
1044
  const data = this.handleResponse(json);
933
1045
  return data.map(convertBalance);
934
1046
  } catch (error) {
935
- throw new Error(`Failed to fetchBalance: ${error}`);
1047
+ if (error instanceof PmxtError) throw error;
1048
+ throw new PmxtError(`Failed to fetchBalance: ${error}`);
936
1049
  }
937
1050
  }
938
1051
 
@@ -941,19 +1054,23 @@ export abstract class Exchange {
941
1054
  try {
942
1055
  const args: any[] = [];
943
1056
  args.push(id);
944
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/unwatchOrderBook`, {
1057
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/unwatchOrderBook`, {
945
1058
  method: 'POST',
946
1059
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
947
1060
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
948
1061
  });
949
1062
  if (!response.ok) {
950
- const error = await response.json().catch(() => ({}));
951
- throw new Error(error.error?.message || response.statusText);
1063
+ const body = await response.json().catch(() => ({}));
1064
+ if (body.error && typeof body.error === "object") {
1065
+ throw fromServerError(body.error);
1066
+ }
1067
+ throw new PmxtError(body.error?.message || response.statusText);
952
1068
  }
953
1069
  const json = await response.json();
954
1070
  this.handleResponse(json);
955
1071
  } catch (error) {
956
- throw new Error(`Failed to unwatchOrderBook: ${error}`);
1072
+ if (error instanceof PmxtError) throw error;
1073
+ throw new PmxtError(`Failed to unwatchOrderBook: ${error}`);
957
1074
  }
958
1075
  }
959
1076
 
@@ -962,19 +1079,23 @@ export abstract class Exchange {
962
1079
  try {
963
1080
  const args: any[] = [];
964
1081
  args.push(address);
965
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/unwatchAddress`, {
1082
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/unwatchAddress`, {
966
1083
  method: 'POST',
967
1084
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
968
1085
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
969
1086
  });
970
1087
  if (!response.ok) {
971
- const error = await response.json().catch(() => ({}));
972
- throw new Error(error.error?.message || response.statusText);
1088
+ const body = await response.json().catch(() => ({}));
1089
+ if (body.error && typeof body.error === "object") {
1090
+ throw fromServerError(body.error);
1091
+ }
1092
+ throw new PmxtError(body.error?.message || response.statusText);
973
1093
  }
974
1094
  const json = await response.json();
975
1095
  this.handleResponse(json);
976
1096
  } catch (error) {
977
- throw new Error(`Failed to unwatchAddress: ${error}`);
1097
+ if (error instanceof PmxtError) throw error;
1098
+ throw new PmxtError(`Failed to unwatchAddress: ${error}`);
978
1099
  }
979
1100
  }
980
1101
 
@@ -982,19 +1103,173 @@ export abstract class Exchange {
982
1103
  await this.initPromise;
983
1104
  try {
984
1105
  const args: any[] = [];
985
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/close`, {
1106
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/close`, {
986
1107
  method: 'POST',
987
1108
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
988
1109
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
989
1110
  });
990
1111
  if (!response.ok) {
991
- const error = await response.json().catch(() => ({}));
992
- throw new Error(error.error?.message || response.statusText);
1112
+ const body = await response.json().catch(() => ({}));
1113
+ if (body.error && typeof body.error === "object") {
1114
+ throw fromServerError(body.error);
1115
+ }
1116
+ throw new PmxtError(body.error?.message || response.statusText);
993
1117
  }
994
1118
  const json = await response.json();
995
1119
  this.handleResponse(json);
996
1120
  } catch (error) {
997
- throw new Error(`Failed to close: ${error}`);
1121
+ if (error instanceof PmxtError) throw error;
1122
+ throw new PmxtError(`Failed to close: ${error}`);
1123
+ }
1124
+ }
1125
+
1126
+ async fetchMarketMatches(params: any): Promise<any[]> {
1127
+ await this.initPromise;
1128
+ try {
1129
+ const args: any[] = [];
1130
+ args.push(params);
1131
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMarketMatches`, {
1132
+ method: 'POST',
1133
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1134
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1135
+ });
1136
+ if (!response.ok) {
1137
+ const body = await response.json().catch(() => ({}));
1138
+ if (body.error && typeof body.error === "object") {
1139
+ throw fromServerError(body.error);
1140
+ }
1141
+ throw new PmxtError(body.error?.message || response.statusText);
1142
+ }
1143
+ const json = await response.json();
1144
+ return this.handleResponse(json);
1145
+ } catch (error) {
1146
+ if (error instanceof PmxtError) throw error;
1147
+ throw new PmxtError(`Failed to fetchMarketMatches: ${error}`);
1148
+ }
1149
+ }
1150
+
1151
+ async fetchMatches(params: any): Promise<any[]> {
1152
+ await this.initPromise;
1153
+ try {
1154
+ const args: any[] = [];
1155
+ args.push(params);
1156
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchMatches`, {
1157
+ method: 'POST',
1158
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1159
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1160
+ });
1161
+ if (!response.ok) {
1162
+ const body = await response.json().catch(() => ({}));
1163
+ if (body.error && typeof body.error === "object") {
1164
+ throw fromServerError(body.error);
1165
+ }
1166
+ throw new PmxtError(body.error?.message || response.statusText);
1167
+ }
1168
+ const json = await response.json();
1169
+ return this.handleResponse(json);
1170
+ } catch (error) {
1171
+ if (error instanceof PmxtError) throw error;
1172
+ throw new PmxtError(`Failed to fetchMatches: ${error}`);
1173
+ }
1174
+ }
1175
+
1176
+ async fetchEventMatches(params: any): Promise<any[]> {
1177
+ await this.initPromise;
1178
+ try {
1179
+ const args: any[] = [];
1180
+ args.push(params);
1181
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchEventMatches`, {
1182
+ method: 'POST',
1183
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1184
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1185
+ });
1186
+ if (!response.ok) {
1187
+ const body = await response.json().catch(() => ({}));
1188
+ if (body.error && typeof body.error === "object") {
1189
+ throw fromServerError(body.error);
1190
+ }
1191
+ throw new PmxtError(body.error?.message || response.statusText);
1192
+ }
1193
+ const json = await response.json();
1194
+ return this.handleResponse(json);
1195
+ } catch (error) {
1196
+ if (error instanceof PmxtError) throw error;
1197
+ throw new PmxtError(`Failed to fetchEventMatches: ${error}`);
1198
+ }
1199
+ }
1200
+
1201
+ async compareMarketPrices(params: any): Promise<any[]> {
1202
+ await this.initPromise;
1203
+ try {
1204
+ const args: any[] = [];
1205
+ args.push(params);
1206
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/compareMarketPrices`, {
1207
+ method: 'POST',
1208
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1209
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1210
+ });
1211
+ if (!response.ok) {
1212
+ const body = await response.json().catch(() => ({}));
1213
+ if (body.error && typeof body.error === "object") {
1214
+ throw fromServerError(body.error);
1215
+ }
1216
+ throw new PmxtError(body.error?.message || response.statusText);
1217
+ }
1218
+ const json = await response.json();
1219
+ return this.handleResponse(json);
1220
+ } catch (error) {
1221
+ if (error instanceof PmxtError) throw error;
1222
+ throw new PmxtError(`Failed to compareMarketPrices: ${error}`);
1223
+ }
1224
+ }
1225
+
1226
+ async fetchHedges(params: any): Promise<any[]> {
1227
+ await this.initPromise;
1228
+ try {
1229
+ const args: any[] = [];
1230
+ args.push(params);
1231
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchHedges`, {
1232
+ method: 'POST',
1233
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1234
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1235
+ });
1236
+ if (!response.ok) {
1237
+ const body = await response.json().catch(() => ({}));
1238
+ if (body.error && typeof body.error === "object") {
1239
+ throw fromServerError(body.error);
1240
+ }
1241
+ throw new PmxtError(body.error?.message || response.statusText);
1242
+ }
1243
+ const json = await response.json();
1244
+ return this.handleResponse(json);
1245
+ } catch (error) {
1246
+ if (error instanceof PmxtError) throw error;
1247
+ throw new PmxtError(`Failed to fetchHedges: ${error}`);
1248
+ }
1249
+ }
1250
+
1251
+ async fetchArbitrage(params?: any): Promise<any[]> {
1252
+ await this.initPromise;
1253
+ try {
1254
+ const args: any[] = [];
1255
+ if (params !== undefined) args.push(params);
1256
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/fetchArbitrage`, {
1257
+ method: 'POST',
1258
+ headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1259
+ body: JSON.stringify({ args, credentials: this.getCredentials() }),
1260
+ });
1261
+ if (!response.ok) {
1262
+ const body = await response.json().catch(() => ({}));
1263
+ if (body.error && typeof body.error === "object") {
1264
+ throw fromServerError(body.error);
1265
+ }
1266
+ throw new PmxtError(body.error?.message || response.statusText);
1267
+ }
1268
+ const json = await response.json();
1269
+ return this.handleResponse(json);
1270
+ } catch (error) {
1271
+ if (error instanceof PmxtError) throw error;
1272
+ throw new PmxtError(`Failed to fetchArbitrage: ${error}`);
998
1273
  }
999
1274
  }
1000
1275
 
@@ -1109,7 +1384,7 @@ export abstract class Exchange {
1109
1384
  args.push(limit);
1110
1385
  }
1111
1386
 
1112
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/watchOrderBook`, {
1387
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchOrderBook`, {
1113
1388
  method: 'POST',
1114
1389
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1115
1390
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
@@ -1173,7 +1448,7 @@ export abstract class Exchange {
1173
1448
  args.push(limit);
1174
1449
  }
1175
1450
 
1176
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/watchTrades`, {
1451
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchTrades`, {
1177
1452
  method: 'POST',
1178
1453
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1179
1454
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
@@ -1225,7 +1500,7 @@ export abstract class Exchange {
1225
1500
  if (types !== undefined) {
1226
1501
  args.push(types);
1227
1502
  }
1228
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/watchAddress`, {
1503
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/watchAddress`, {
1229
1504
  method: 'POST',
1230
1505
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1231
1506
  body: JSON.stringify({ args, credentials: this.getCredentials() }),
@@ -1320,7 +1595,7 @@ export abstract class Exchange {
1320
1595
  paramsDict.fee = params.fee;
1321
1596
  }
1322
1597
 
1323
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/buildOrder`, {
1598
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/buildOrder`, {
1324
1599
  method: 'POST',
1325
1600
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1326
1601
  body: JSON.stringify({ args: [paramsDict], credentials: this.getCredentials() }),
@@ -1396,7 +1671,7 @@ export abstract class Exchange {
1396
1671
  paramsDict.fee = params.fee;
1397
1672
  }
1398
1673
 
1399
- const response = await fetch(`${this.config.basePath}/api/${this.exchangeName}/createOrder`, {
1674
+ const response = await this.fetchWithRetry(`${this.resolveBaseUrl()}/api/${this.exchangeName}/createOrder`, {
1400
1675
  method: 'POST',
1401
1676
  headers: { 'Content-Type': 'application/json', ...this.getAuthHeaders() },
1402
1677
  body: JSON.stringify({ args: [paramsDict], credentials: this.getCredentials() }),
@@ -1455,9 +1730,9 @@ export abstract class Exchange {
1455
1730
  body.credentials = credentials;
1456
1731
  }
1457
1732
 
1458
- const url = `${this.config.basePath}/api/${this.exchangeName}/getExecutionPriceDetailed`;
1733
+ const url = `${this.resolveBaseUrl()}/api/${this.exchangeName}/getExecutionPriceDetailed`;
1459
1734
 
1460
- const response = await fetch(url, {
1735
+ const response = await this.fetchWithRetry(url, {
1461
1736
  method: 'POST',
1462
1737
  headers: {
1463
1738
  'Content-Type': 'application/json',