@layer-drone/protocol 0.5.0 → 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.
@@ -17,6 +17,10 @@ export type CreateApiTokenResponseDto = {
17
17
  | "manageContracts"
18
18
  | "redrive"
19
19
  | "updateOnChainData"
20
+ | "syncEventProcessorState"
21
+ | "manageZones"
22
+ | "manageProposals"
23
+ | "approveProposals"
20
24
  | "all"
21
25
  >;
22
26
  };
@@ -34,6 +38,10 @@ export type CreateApiTokenRequestDto = {
34
38
  | "manageContracts"
35
39
  | "redrive"
36
40
  | "updateOnChainData"
41
+ | "syncEventProcessorState"
42
+ | "manageZones"
43
+ | "manageProposals"
44
+ | "approveProposals"
37
45
  | "all"
38
46
  >;
39
47
  orgId: number;
@@ -52,6 +60,10 @@ export type UpdateApiTokenRequestDto = {
52
60
  | "manageContracts"
53
61
  | "redrive"
54
62
  | "updateOnChainData"
63
+ | "syncEventProcessorState"
64
+ | "manageZones"
65
+ | "manageProposals"
66
+ | "approveProposals"
55
67
  | "all"
56
68
  >;
57
69
  };
@@ -73,6 +85,10 @@ export type UpdateApiTokenResponseDto = {
73
85
  | "manageContracts"
74
86
  | "redrive"
75
87
  | "updateOnChainData"
88
+ | "syncEventProcessorState"
89
+ | "manageZones"
90
+ | "manageProposals"
91
+ | "approveProposals"
76
92
  | "all"
77
93
  >;
78
94
  };
@@ -300,6 +316,16 @@ export type FlightDto = {
300
316
  block_number: number;
301
317
  block_timestamp: number;
302
318
  transaction_hash: string;
319
+ payout: {
320
+ id: string;
321
+ missionId: string;
322
+ flightReviewId: string;
323
+ recipient: string;
324
+ amount: string;
325
+ tokenContractAddress: string;
326
+ chainId: number;
327
+ createdAt: Date;
328
+ };
303
329
  };
304
330
 
305
331
  export type CreateFileRequestDto = {
@@ -571,26 +597,30 @@ export type GetProvenanceCryptoKeyResponse = {
571
597
 
572
598
  export type GetMissionResponseDto = {
573
599
  id: string;
574
- mission: {
600
+ vaultId: string;
601
+ requestor: string;
602
+ zoneId: string;
603
+ isOpen: boolean;
604
+ flightPlanId: number;
605
+ isReservable: boolean;
606
+ tags: {
607
+ [key: string]: string;
608
+ };
609
+ captureStartTime: Date;
610
+ captureEndTime: Date;
611
+ campaigns: Array<string>;
612
+ contractAddress: string;
613
+ chainId: number;
614
+ createdAt: Date;
615
+ updatedAt: Date;
616
+ payout: {
575
617
  id: string;
576
- vaultId: string;
577
- requestor: string;
578
- zoneId: string;
579
- isOpen: boolean;
580
- flightPlanId: number;
581
- isReservable: boolean;
582
- tags: {
583
- [key: string]: string;
584
- };
585
- captureStartTime: Date;
586
- captureEndTime: Date;
587
- campaigns: Array<string>;
588
- contractAddress: string;
589
- chainId: number;
618
+ recipient: string;
619
+ amount: string;
620
+ flightReviewId: string;
590
621
  createdAt: Date;
591
- updatedAt: Date;
592
622
  };
593
- vault?: {
623
+ vault: {
594
624
  id: string;
595
625
  amount: string;
596
626
  collateralTokenAddress: string;
@@ -600,32 +630,35 @@ export type GetMissionResponseDto = {
600
630
  createdAt: Date;
601
631
  updatedAt: Date;
602
632
  };
603
- payout?: null;
604
633
  };
605
634
 
606
635
  export type GetMissionsResponseDto = {
607
636
  missions: Array<{
608
637
  id: string;
609
- mission: {
638
+ vaultId: string;
639
+ requestor: string;
640
+ zoneId: string;
641
+ isOpen: boolean;
642
+ flightPlanId: number;
643
+ isReservable: boolean;
644
+ tags: {
645
+ [key: string]: string;
646
+ };
647
+ captureStartTime: Date;
648
+ captureEndTime: Date;
649
+ campaigns: Array<string>;
650
+ contractAddress: string;
651
+ chainId: number;
652
+ createdAt: Date;
653
+ updatedAt: Date;
654
+ payout: {
610
655
  id: string;
611
- vaultId: string;
612
- requestor: string;
613
- zoneId: string;
614
- isOpen: boolean;
615
- flightPlanId: number;
616
- isReservable: boolean;
617
- tags: {
618
- [key: string]: string;
619
- };
620
- captureStartTime: Date;
621
- captureEndTime: Date;
622
- campaigns: Array<string>;
623
- contractAddress: string;
624
- chainId: number;
656
+ recipient: string;
657
+ amount: string;
658
+ flightReviewId: string;
625
659
  createdAt: Date;
626
- updatedAt: Date;
627
660
  };
628
- vault?: {
661
+ vault: {
629
662
  id: string;
630
663
  amount: string;
631
664
  collateralTokenAddress: string;
@@ -635,7 +668,6 @@ export type GetMissionsResponseDto = {
635
668
  createdAt: Date;
636
669
  updatedAt: Date;
637
670
  };
638
- payout?: null;
639
671
  }>;
640
672
  hasNextPage: boolean;
641
673
  };
@@ -791,6 +823,7 @@ export type GetWebhooksResponse = Array<{
791
823
  active: boolean;
792
824
  failure_count: number;
793
825
  last_failure_reason: string;
826
+ previous_secret_expires_at: Date;
794
827
  created_at: string;
795
828
  updated_at: string;
796
829
  }>;
@@ -811,6 +844,7 @@ export type GetWebhookResponse = {
811
844
  active: boolean;
812
845
  failure_count: number;
813
846
  last_failure_reason: string;
847
+ previous_secret_expires_at: Date;
814
848
  created_at: string;
815
849
  updated_at: string;
816
850
  };
@@ -846,6 +880,7 @@ export type UpdateWebhookResponse = {
846
880
  active: boolean;
847
881
  failure_count: number;
848
882
  last_failure_reason: string;
883
+ previous_secret_expires_at: Date;
849
884
  created_at: string;
850
885
  updated_at: string;
851
886
  };
@@ -854,8 +889,15 @@ export type DeleteWebhookResponse = {
854
889
  message: string;
855
890
  };
856
891
 
892
+ export type RegenerateWebhookSecretBody =
893
+ | {
894
+ previous_secret_expires_at?: Date;
895
+ }
896
+ | unknown;
897
+
857
898
  export type RegenerateWebhookSecretResponseDto = {
858
899
  secret: string;
900
+ previous_secret_expires_at: Date;
859
901
  };
860
902
 
861
903
  export type TestWebhookResponse = {
@@ -867,6 +909,51 @@ export type GetWebhookStatusResponse = {
867
909
  error?: string;
868
910
  };
869
911
 
912
+ export type CreateZoneDto = {
913
+ id: string;
914
+ region_id: number;
915
+ is_flyable?: boolean;
916
+ };
917
+
918
+ export type ZoneResponseDto = {
919
+ id: string;
920
+ region_id: number;
921
+ is_flyable: boolean;
922
+ created_at: Date;
923
+ updated_at: Date;
924
+ };
925
+
926
+ export type UpdateZoneDto = {
927
+ is_flyable: boolean;
928
+ };
929
+
930
+ export type SyncEventProcessorMissionsRequestDto = {
931
+ missionIds: Array<string>;
932
+ missionDataContractAddress: string;
933
+ missionEscrowContractAddress: string;
934
+ };
935
+
936
+ export type SyncEventProcessorMissionsResponseDto = {
937
+ succeeded: Array<string>;
938
+ failed: Array<{
939
+ missionId: string;
940
+ error: string;
941
+ }>;
942
+ };
943
+
944
+ export type SyncEventProcessorFlightReviewsRequestDto = {
945
+ flightIds: Array<string>;
946
+ flightReviewContractAddress: string;
947
+ };
948
+
949
+ export type SyncEventProcessorFlightReviewsResponseDto = {
950
+ succeeded: Array<string>;
951
+ failed: Array<{
952
+ flightId: string;
953
+ error: string;
954
+ }>;
955
+ };
956
+
870
957
  export type AlchemyWebhookBodyDto = {
871
958
  [key: string]: unknown;
872
959
  };
@@ -1501,7 +1588,13 @@ export type MissionsControllerGetMissionData = {
1501
1588
  path: {
1502
1589
  id: string;
1503
1590
  };
1504
- query?: never;
1591
+ query?: {
1592
+ relations?:
1593
+ | "vault"
1594
+ | "payout"
1595
+ | "campaigns"
1596
+ | Array<"vault" | "payout" | "campaigns">;
1597
+ };
1505
1598
  url: "/missions/{id}";
1506
1599
  };
1507
1600
 
@@ -1533,6 +1626,11 @@ export type MissionsControllerGetMissionsData = {
1533
1626
  zoneIds?: string | Array<string>;
1534
1627
  flightPlanId?: number;
1535
1628
  page?: number;
1629
+ relations?:
1630
+ | "vault"
1631
+ | "payout"
1632
+ | "campaigns"
1633
+ | Array<"vault" | "payout" | "campaigns">;
1536
1634
  };
1537
1635
  url: "/missions";
1538
1636
  };
@@ -1845,7 +1943,7 @@ export type WebhooksControllerUpdateResponse =
1845
1943
  WebhooksControllerUpdateResponses[keyof WebhooksControllerUpdateResponses];
1846
1944
 
1847
1945
  export type WebhooksControllerRegenerateSecretData = {
1848
- body?: never;
1946
+ body: RegenerateWebhookSecretBody;
1849
1947
  path: {
1850
1948
  org_id: number;
1851
1949
  id: number;
@@ -1910,6 +2008,132 @@ export type WebhooksControllerGetStatusResponses = {
1910
2008
  export type WebhooksControllerGetStatusResponse =
1911
2009
  WebhooksControllerGetStatusResponses[keyof WebhooksControllerGetStatusResponses];
1912
2010
 
2011
+ export type ZonesControllerCreateZoneData = {
2012
+ body: CreateZoneDto;
2013
+ path?: never;
2014
+ query?: never;
2015
+ url: "/zones";
2016
+ };
2017
+
2018
+ export type ZonesControllerCreateZoneErrors = {
2019
+ /**
2020
+ * Invalid zone id (must be a valid level-9 H3 cell)
2021
+ */
2022
+ 400: unknown;
2023
+ 401: unknown;
2024
+ 403: unknown;
2025
+ /**
2026
+ * Zone already exists
2027
+ */
2028
+ 409: unknown;
2029
+ };
2030
+
2031
+ export type ZonesControllerCreateZoneResponses = {
2032
+ 201: ZoneResponseDto;
2033
+ };
2034
+
2035
+ export type ZonesControllerCreateZoneResponse =
2036
+ ZonesControllerCreateZoneResponses[keyof ZonesControllerCreateZoneResponses];
2037
+
2038
+ export type ZonesControllerDeleteZoneData = {
2039
+ body?: never;
2040
+ path: {
2041
+ id: string;
2042
+ };
2043
+ query?: never;
2044
+ url: "/zones/{id}";
2045
+ };
2046
+
2047
+ export type ZonesControllerDeleteZoneErrors = {
2048
+ 401: unknown;
2049
+ 403: unknown;
2050
+ /**
2051
+ * Zone not found
2052
+ */
2053
+ 404: unknown;
2054
+ };
2055
+
2056
+ export type ZonesControllerDeleteZoneResponses = {
2057
+ /**
2058
+ * Zone deleted
2059
+ */
2060
+ 204: void;
2061
+ };
2062
+
2063
+ export type ZonesControllerDeleteZoneResponse =
2064
+ ZonesControllerDeleteZoneResponses[keyof ZonesControllerDeleteZoneResponses];
2065
+
2066
+ export type ZonesControllerUpdateZoneData = {
2067
+ body: UpdateZoneDto;
2068
+ path: {
2069
+ id: string;
2070
+ };
2071
+ query?: never;
2072
+ url: "/zones/{id}";
2073
+ };
2074
+
2075
+ export type ZonesControllerUpdateZoneErrors = {
2076
+ 401: unknown;
2077
+ 403: unknown;
2078
+ /**
2079
+ * Zone not found
2080
+ */
2081
+ 404: unknown;
2082
+ };
2083
+
2084
+ export type ZonesControllerUpdateZoneResponses = {
2085
+ 200: ZoneResponseDto;
2086
+ };
2087
+
2088
+ export type ZonesControllerUpdateZoneResponse =
2089
+ ZonesControllerUpdateZoneResponses[keyof ZonesControllerUpdateZoneResponses];
2090
+
2091
+ export type EventProcessorMissionsSyncControllerResyncMissionsData = {
2092
+ body: SyncEventProcessorMissionsRequestDto;
2093
+ path?: never;
2094
+ query?: never;
2095
+ url: "/event-processor/missions/sync";
2096
+ };
2097
+
2098
+ export type EventProcessorMissionsSyncControllerResyncMissionsErrors = {
2099
+ 401: unknown;
2100
+ 403: unknown;
2101
+ };
2102
+
2103
+ export type EventProcessorMissionsSyncControllerResyncMissionsResponses = {
2104
+ /**
2105
+ * Per-mission success or failure.
2106
+ */
2107
+ 200: SyncEventProcessorMissionsResponseDto;
2108
+ };
2109
+
2110
+ export type EventProcessorMissionsSyncControllerResyncMissionsResponse =
2111
+ EventProcessorMissionsSyncControllerResyncMissionsResponses[keyof EventProcessorMissionsSyncControllerResyncMissionsResponses];
2112
+
2113
+ export type EventProcessorFlightReviewsSyncControllerResyncFlightReviewsData = {
2114
+ body: SyncEventProcessorFlightReviewsRequestDto;
2115
+ path?: never;
2116
+ query?: never;
2117
+ url: "/event-processor/flightreviews";
2118
+ };
2119
+
2120
+ export type EventProcessorFlightReviewsSyncControllerResyncFlightReviewsErrors =
2121
+ {
2122
+ 401: unknown;
2123
+ 403: unknown;
2124
+ };
2125
+
2126
+ export type EventProcessorFlightReviewsSyncControllerResyncFlightReviewsResponses =
2127
+ {
2128
+ /**
2129
+ * Per-flight success or failure.
2130
+ */
2131
+ 200: SyncEventProcessorFlightReviewsResponseDto;
2132
+ };
2133
+
2134
+ export type EventProcessorFlightReviewsSyncControllerResyncFlightReviewsResponse =
2135
+ EventProcessorFlightReviewsSyncControllerResyncFlightReviewsResponses[keyof EventProcessorFlightReviewsSyncControllerResyncFlightReviewsResponses];
2136
+
1913
2137
  export type AlchemyControllerHandleWebhookData = {
1914
2138
  body: AlchemyWebhookBodyDto;
1915
2139
  path?: never;
@@ -2,28 +2,80 @@ import type { Request } from "express";
2
2
 
3
3
  import { Event } from "./types.gen";
4
4
 
5
- const WEBHOOK_SECRET_HEADER = "X-Webhook-Secret";
5
+ /** Primary signing secret for this delivery (current secret). */
6
+ export const WEBHOOK_SECRET_HEADER = "X-Webhook-Secret";
6
7
 
8
+ /**
9
+ * During rotation, the prior secret value still valid for receivers who have not
10
+ * updated their stored secret yet (must be sent together with the expiry header).
11
+ */
12
+ export const WEBHOOK_PREVIOUS_SECRET_HEADER = "X-Webhook-Previous-Secret";
13
+
14
+ /**
15
+ * ISO-8601 instant; after this time the previous secret must no longer be accepted.
16
+ */
17
+ export const WEBHOOK_PREVIOUS_SECRET_EXPIRES_AT_HEADER =
18
+ "X-Webhook-Previous-Secret-Expires-At";
19
+
20
+ /**
21
+ * Verify an inbound webhook using the signing secret your app has configured.
22
+ *
23
+ * Compares `X-Webhook-Secret` to `webhookSecret`. If that fails, checks optional
24
+ * rotation headers from LayerDrone: when both `X-Webhook-Previous-Secret` and
25
+ * `X-Webhook-Previous-Secret-Expires-At` are present, the request is accepted if
26
+ * your configured secret still matches the announced previous secret (same value
27
+ * as in `X-Webhook-Previous-Secret`) and the expiry has not passed.
28
+ */
7
29
  export function parseWebhookEvent(req: Request, webhookSecret: string) {
8
- const secret = getSecretHeader(req.headers);
9
- if (typeof secret !== "string") {
30
+ const incoming = getFirstHeader(req.headers, WEBHOOK_SECRET_HEADER);
31
+ if (typeof incoming !== "string") {
10
32
  throw new Error("Webhook request signature is not a string");
11
33
  }
12
34
  if (!Buffer.isBuffer(req.body)) {
13
35
  throw new Error("Webhook request body is not a Buffer");
14
36
  }
15
- if (secret !== webhookSecret) {
37
+
38
+ if (incoming === webhookSecret) {
39
+ return Event.parse(JSON.parse(req.body.toString("utf-8")));
40
+ }
41
+
42
+ const announcedPrevious = getFirstHeader(
43
+ req.headers,
44
+ WEBHOOK_PREVIOUS_SECRET_HEADER,
45
+ );
46
+ const expiresRaw = getFirstHeader(
47
+ req.headers,
48
+ WEBHOOK_PREVIOUS_SECRET_EXPIRES_AT_HEADER,
49
+ );
50
+
51
+ if (announcedPrevious === undefined || expiresRaw === undefined) {
16
52
  throw new Error("Invalid webhook secret");
17
53
  }
18
- return Event.parse(JSON.parse(req.body.toString("utf-8")));
54
+
55
+ const expiresAt = new Date(expiresRaw);
56
+ if (Number.isNaN(expiresAt.getTime())) {
57
+ throw new Error("Invalid webhook secret");
58
+ }
59
+
60
+ if (Date.now() >= expiresAt.getTime()) {
61
+ throw new Error("Invalid webhook secret");
62
+ }
63
+
64
+ if (webhookSecret === announcedPrevious) {
65
+ return Event.parse(JSON.parse(req.body.toString("utf-8")));
66
+ }
67
+
68
+ throw new Error("Invalid webhook secret");
19
69
  }
20
70
 
21
- function getSecretHeader(headers: Request["headers"]) {
22
- let value =
23
- headers[WEBHOOK_SECRET_HEADER] ??
24
- headers[WEBHOOK_SECRET_HEADER.toLowerCase()];
71
+ function getFirstHeader(
72
+ headers: Request["headers"],
73
+ canonicalName: string,
74
+ ): string | undefined {
75
+ const lower = canonicalName.toLowerCase();
76
+ let value = headers[canonicalName] ?? headers[lower];
25
77
  if (Array.isArray(value)) {
26
78
  value = value[0];
27
79
  }
28
- return value;
80
+ return typeof value === "string" ? value : undefined;
29
81
  }