@ghostgate/sdk 0.1.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ghost Protocol
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @ghostgate/sdk
2
+
3
+ Node.js SDK for Ghost Protocol gate access, fulfillment, telemetry, and canary helpers.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @ghostgate/sdk
9
+ ```
10
+
11
+ Until the package is published to npm, you can also test locally from this repo:
12
+
13
+ ```bash
14
+ npm run build:sdk
15
+ npm install ../GHOST_PROTOCOL/packages/sdk
16
+ ```
17
+
18
+ ## Surface
19
+
20
+ - `GhostAgent`
21
+ - `connect()`
22
+ - `pulse()`
23
+ - `outcome()`
24
+ - `startHeartbeat()`
25
+ - `GhostFulfillmentConsumer`
26
+ - `GhostFulfillmentMerchant`
27
+ - `GhostMerchant`
28
+ - `buildCanaryPayload()`
29
+ - `createCanaryHandler()`
30
+
31
+ ## Example
32
+
33
+ ```ts
34
+ import { GhostAgent, GhostMerchant } from "@ghostgate/sdk";
35
+
36
+ const sdk = new GhostAgent({
37
+ apiKey: process.env.GHOST_API_KEY,
38
+ privateKey: process.env.GHOST_SIGNER_PRIVATE_KEY as `0x${string}`,
39
+ baseUrl: process.env.GHOST_BASE_URL,
40
+ serviceSlug: "agent-18755",
41
+ });
42
+
43
+ await sdk.connect();
44
+ await sdk.pulse();
45
+ ```
@@ -0,0 +1,302 @@
1
+ import { type Hex } from "viem";
2
+ import { FULFILLMENT_EIP712_DOMAIN_NAME, FULFILLMENT_EIP712_DOMAIN_VERSION, FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL, type FulfillmentDeliveryProofEnvelope, type FulfillmentDeliveryProofMessage, type FulfillmentTicketEnvelope, type FulfillmentTicketHeaderRecord, type FulfillmentTicketMessage, type FulfillmentTicketRequestAuthMessage, type Hex32 } from "./fulfillment-types.js";
3
+ type UintLike = bigint | number | string;
4
+ type MaybeString = string | null | undefined;
5
+ export declare const FULFILLMENT_EIP712_TYPES: {
6
+ readonly FulfillmentTicket: readonly [{
7
+ readonly name: "ticketId";
8
+ readonly type: "bytes32";
9
+ }, {
10
+ readonly name: "consumer";
11
+ readonly type: "address";
12
+ }, {
13
+ readonly name: "merchantOwner";
14
+ readonly type: "address";
15
+ }, {
16
+ readonly name: "gatewayConfigIdHash";
17
+ readonly type: "bytes32";
18
+ }, {
19
+ readonly name: "serviceSlug";
20
+ readonly type: "string";
21
+ }, {
22
+ readonly name: "method";
23
+ readonly type: "string";
24
+ }, {
25
+ readonly name: "path";
26
+ readonly type: "string";
27
+ }, {
28
+ readonly name: "queryHash";
29
+ readonly type: "bytes32";
30
+ }, {
31
+ readonly name: "bodyHash";
32
+ readonly type: "bytes32";
33
+ }, {
34
+ readonly name: "cost";
35
+ readonly type: "uint256";
36
+ }, {
37
+ readonly name: "issuedAt";
38
+ readonly type: "uint256";
39
+ }, {
40
+ readonly name: "expiresAt";
41
+ readonly type: "uint256";
42
+ }];
43
+ readonly FulfillmentDeliveryProof: readonly [{
44
+ readonly name: "ticketId";
45
+ readonly type: "bytes32";
46
+ }, {
47
+ readonly name: "deliveryProofId";
48
+ readonly type: "bytes32";
49
+ }, {
50
+ readonly name: "merchantSigner";
51
+ readonly type: "address";
52
+ }, {
53
+ readonly name: "serviceSlug";
54
+ readonly type: "string";
55
+ }, {
56
+ readonly name: "completedAt";
57
+ readonly type: "uint256";
58
+ }, {
59
+ readonly name: "statusCode";
60
+ readonly type: "uint256";
61
+ }, {
62
+ readonly name: "latencyMs";
63
+ readonly type: "uint256";
64
+ }, {
65
+ readonly name: "responseHash";
66
+ readonly type: "bytes32";
67
+ }];
68
+ readonly FulfillmentTicketRequestAuth: readonly [{
69
+ readonly name: "action";
70
+ readonly type: "string";
71
+ }, {
72
+ readonly name: "serviceSlug";
73
+ readonly type: "string";
74
+ }, {
75
+ readonly name: "method";
76
+ readonly type: "string";
77
+ }, {
78
+ readonly name: "path";
79
+ readonly type: "string";
80
+ }, {
81
+ readonly name: "queryHash";
82
+ readonly type: "bytes32";
83
+ }, {
84
+ readonly name: "bodyHash";
85
+ readonly type: "bytes32";
86
+ }, {
87
+ readonly name: "cost";
88
+ readonly type: "uint256";
89
+ }, {
90
+ readonly name: "issuedAt";
91
+ readonly type: "uint256";
92
+ }, {
93
+ readonly name: "nonce";
94
+ readonly type: "string";
95
+ }];
96
+ };
97
+ export declare const buildFulfillmentEip712Domain: (chainId?: number) => {
98
+ name: typeof FULFILLMENT_EIP712_DOMAIN_NAME;
99
+ version: typeof FULFILLMENT_EIP712_DOMAIN_VERSION;
100
+ chainId: number;
101
+ verifyingContract: typeof FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL;
102
+ };
103
+ export declare const normalizeFulfillmentTicketMessage: (input: {
104
+ ticketId: string;
105
+ consumer: string;
106
+ merchantOwner: string;
107
+ gatewayConfigIdHash: string;
108
+ serviceSlug: string;
109
+ method: string;
110
+ path: string;
111
+ queryHash: string;
112
+ bodyHash: string;
113
+ cost: UintLike;
114
+ issuedAt: UintLike;
115
+ expiresAt: UintLike;
116
+ }) => FulfillmentTicketMessage;
117
+ export declare const normalizeFulfillmentDeliveryProofMessage: (input: {
118
+ ticketId: string;
119
+ deliveryProofId: string;
120
+ merchantSigner: string;
121
+ serviceSlug: string;
122
+ completedAt: UintLike;
123
+ statusCode: UintLike;
124
+ latencyMs: UintLike;
125
+ responseHash?: string | null;
126
+ }) => FulfillmentDeliveryProofMessage;
127
+ export declare const normalizeFulfillmentTicketRequestAuthMessage: (input: {
128
+ action?: string;
129
+ serviceSlug: string;
130
+ method: string;
131
+ path: string;
132
+ queryHash: string;
133
+ bodyHash: string;
134
+ cost: UintLike;
135
+ issuedAt: UintLike;
136
+ nonce: string;
137
+ }) => FulfillmentTicketRequestAuthMessage;
138
+ export declare const buildFulfillmentTicketTypedData: (message: FulfillmentTicketMessage, options?: {
139
+ chainId?: number;
140
+ }) => {
141
+ domain: {
142
+ name: typeof FULFILLMENT_EIP712_DOMAIN_NAME;
143
+ version: typeof FULFILLMENT_EIP712_DOMAIN_VERSION;
144
+ chainId: number;
145
+ verifyingContract: typeof FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL;
146
+ };
147
+ types: {
148
+ FulfillmentTicket: readonly [{
149
+ readonly name: "ticketId";
150
+ readonly type: "bytes32";
151
+ }, {
152
+ readonly name: "consumer";
153
+ readonly type: "address";
154
+ }, {
155
+ readonly name: "merchantOwner";
156
+ readonly type: "address";
157
+ }, {
158
+ readonly name: "gatewayConfigIdHash";
159
+ readonly type: "bytes32";
160
+ }, {
161
+ readonly name: "serviceSlug";
162
+ readonly type: "string";
163
+ }, {
164
+ readonly name: "method";
165
+ readonly type: "string";
166
+ }, {
167
+ readonly name: "path";
168
+ readonly type: "string";
169
+ }, {
170
+ readonly name: "queryHash";
171
+ readonly type: "bytes32";
172
+ }, {
173
+ readonly name: "bodyHash";
174
+ readonly type: "bytes32";
175
+ }, {
176
+ readonly name: "cost";
177
+ readonly type: "uint256";
178
+ }, {
179
+ readonly name: "issuedAt";
180
+ readonly type: "uint256";
181
+ }, {
182
+ readonly name: "expiresAt";
183
+ readonly type: "uint256";
184
+ }];
185
+ };
186
+ primaryType: "FulfillmentTicket";
187
+ message: FulfillmentTicketMessage;
188
+ };
189
+ export declare const buildFulfillmentDeliveryProofTypedData: (message: FulfillmentDeliveryProofMessage, options?: {
190
+ chainId?: number;
191
+ }) => {
192
+ domain: {
193
+ name: typeof FULFILLMENT_EIP712_DOMAIN_NAME;
194
+ version: typeof FULFILLMENT_EIP712_DOMAIN_VERSION;
195
+ chainId: number;
196
+ verifyingContract: typeof FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL;
197
+ };
198
+ types: {
199
+ FulfillmentDeliveryProof: readonly [{
200
+ readonly name: "ticketId";
201
+ readonly type: "bytes32";
202
+ }, {
203
+ readonly name: "deliveryProofId";
204
+ readonly type: "bytes32";
205
+ }, {
206
+ readonly name: "merchantSigner";
207
+ readonly type: "address";
208
+ }, {
209
+ readonly name: "serviceSlug";
210
+ readonly type: "string";
211
+ }, {
212
+ readonly name: "completedAt";
213
+ readonly type: "uint256";
214
+ }, {
215
+ readonly name: "statusCode";
216
+ readonly type: "uint256";
217
+ }, {
218
+ readonly name: "latencyMs";
219
+ readonly type: "uint256";
220
+ }, {
221
+ readonly name: "responseHash";
222
+ readonly type: "bytes32";
223
+ }];
224
+ };
225
+ primaryType: "FulfillmentDeliveryProof";
226
+ message: FulfillmentDeliveryProofMessage;
227
+ };
228
+ export declare const buildFulfillmentTicketRequestAuthTypedData: (message: FulfillmentTicketRequestAuthMessage, options?: {
229
+ chainId?: number;
230
+ }) => {
231
+ domain: {
232
+ name: typeof FULFILLMENT_EIP712_DOMAIN_NAME;
233
+ version: typeof FULFILLMENT_EIP712_DOMAIN_VERSION;
234
+ chainId: number;
235
+ verifyingContract: typeof FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL;
236
+ };
237
+ types: {
238
+ FulfillmentTicketRequestAuth: readonly [{
239
+ readonly name: "action";
240
+ readonly type: "string";
241
+ }, {
242
+ readonly name: "serviceSlug";
243
+ readonly type: "string";
244
+ }, {
245
+ readonly name: "method";
246
+ readonly type: "string";
247
+ }, {
248
+ readonly name: "path";
249
+ readonly type: "string";
250
+ }, {
251
+ readonly name: "queryHash";
252
+ readonly type: "bytes32";
253
+ }, {
254
+ readonly name: "bodyHash";
255
+ readonly type: "bytes32";
256
+ }, {
257
+ readonly name: "cost";
258
+ readonly type: "uint256";
259
+ }, {
260
+ readonly name: "issuedAt";
261
+ readonly type: "uint256";
262
+ }, {
263
+ readonly name: "nonce";
264
+ readonly type: "string";
265
+ }];
266
+ };
267
+ primaryType: "FulfillmentTicketRequestAuth";
268
+ message: FulfillmentTicketRequestAuthMessage;
269
+ };
270
+ export declare const hashFulfillmentTicketTypedData: (message: FulfillmentTicketMessage, options?: {
271
+ chainId?: number;
272
+ }) => Hex32;
273
+ export declare const hashFulfillmentDeliveryProofTypedData: (message: FulfillmentDeliveryProofMessage, options?: {
274
+ chainId?: number;
275
+ }) => Hex32;
276
+ export declare const hashFulfillmentTicketRequestAuthTypedData: (message: FulfillmentTicketRequestAuthMessage, options?: {
277
+ chainId?: number;
278
+ }) => Hex32;
279
+ export declare const encodeTypedPayloadBase64Url: <T extends object>(payload: T) => string;
280
+ export declare const decodeTypedPayloadBase64Url: <T = unknown>(encoded: string) => T;
281
+ export declare const buildFulfillmentTicketEnvelope: (payload: FulfillmentTicketMessage, signature: string) => FulfillmentTicketEnvelope;
282
+ export declare const buildFulfillmentDeliveryProofEnvelope: (payload: FulfillmentDeliveryProofMessage, signature: string) => FulfillmentDeliveryProofEnvelope;
283
+ export declare const buildFulfillmentTicketHeaders: (input: {
284
+ ticketId: string;
285
+ ticket: FulfillmentTicketEnvelope;
286
+ clientRequestId?: MaybeString;
287
+ }) => FulfillmentTicketHeaderRecord;
288
+ type HeadersLike = Pick<Headers, "get"> | Record<string, string | undefined | null>;
289
+ export declare const parseFulfillmentTicketHeaders: (headers: HeadersLike) => {
290
+ ticketId: Hex32;
291
+ ticket: FulfillmentTicketEnvelope;
292
+ clientRequestId: string | null;
293
+ } | null;
294
+ export declare const redactFulfillmentTicketEnvelopeDebug: (envelope: FulfillmentTicketEnvelope) => Record<string, unknown>;
295
+ export declare const redactFulfillmentDeliveryProofEnvelopeDebug: (envelope: FulfillmentDeliveryProofEnvelope) => Record<string, unknown>;
296
+ export declare const parseWireFulfillmentTicketMessage: (encodedPayload: string) => FulfillmentTicketMessage;
297
+ export declare const parseWireFulfillmentDeliveryProofMessage: (encodedPayload: string) => FulfillmentDeliveryProofMessage;
298
+ export declare const parseWireFulfillmentTicketRequestAuthMessage: (encodedPayload: string) => FulfillmentTicketRequestAuthMessage;
299
+ export type FulfillmentFixtureDomain = ReturnType<typeof buildFulfillmentEip712Domain>;
300
+ export type FulfillmentTicketTypedHash = Hex;
301
+ export {};
302
+ //# sourceMappingURL=fulfillment-eip712.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulfillment-eip712.d.ts","sourceRoot":"","sources":["../src/fulfillment-eip712.ts"],"names":[],"mappings":"AACA,OAAO,EAAiB,KAAK,GAAG,EAAE,MAAM,MAAM,CAAC;AAC/C,OAAO,EAIL,8BAA8B,EAC9B,iCAAiC,EACjC,+CAA+C,EAS/C,KAAK,gCAAgC,EACrC,KAAK,+BAA+B,EACpC,KAAK,yBAAyB,EAC9B,KAAK,6BAA6B,EAClC,KAAK,wBAAwB,EAC7B,KAAK,mCAAmC,EACxC,KAAK,KAAK,EAEX,MAAM,wBAAwB,CAAC;AAGhC,KAAK,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;AACzC,KAAK,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;AAsD7C,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoC3B,CAAC;AAEX,eAAO,MAAM,4BAA4B,GAAI,UAAS,MAA2C,KAAG;IAClG,IAAI,EAAE,OAAO,8BAA8B,CAAC;IAC5C,OAAO,EAAE,OAAO,iCAAiC,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,OAAO,+CAA+C,CAAC;CAM1E,CAAC;AAEH,eAAO,MAAM,iCAAiC,GAAI,OAAO;IACvD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,EAAE,QAAQ,CAAC;CACrB,KAAG,wBAaF,CAAC;AAEH,eAAO,MAAM,wCAAwC,GAAI,OAAO;IAC9D,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,QAAQ,CAAC;IACtB,UAAU,EAAE,QAAQ,CAAC;IACrB,SAAS,EAAE,QAAQ,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,KAAG,+BAUF,CAAC;AAEH,eAAO,MAAM,4CAA4C,GAAI,OAAO;IAClE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf,KAAG,mCAUF,CAAC;AAEH,eAAO,MAAM,+BAA+B,GAAI,SAAS,wBAAwB,EAAE,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;cAlFzG,OAAO,8BAA8B;iBAClC,OAAO,iCAAiC;iBACxC,MAAM;2BACI,OAAO,+CAA+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAoFzE,CAAC;AAEH,eAAO,MAAM,sCAAsC,GACjD,SAAS,+BAA+B,EACxC,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;cA3FxB,OAAO,8BAA8B;iBAClC,OAAO,iCAAiC;iBACxC,MAAM;2BACI,OAAO,+CAA+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8FzE,CAAC;AAEH,eAAO,MAAM,0CAA0C,GACrD,SAAS,mCAAmC,EAC5C,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE;;cArGxB,OAAO,8BAA8B;iBAClC,OAAO,iCAAiC;iBACxC,MAAM;2BACI,OAAO,+CAA+C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwGzE,CAAC;AAEH,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,EAAE,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAAG,KACD,CAAC;AAEnH,eAAO,MAAM,qCAAqC,GAChD,SAAS,+BAA+B,EACxC,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7B,KAAgI,CAAC;AAEpI,eAAO,MAAM,yCAAyC,GACpD,SAAS,mCAAmC,EAC5C,UAAU;IAAE,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,KAC7B,KAAoI,CAAC;AAExI,eAAO,MAAM,2BAA2B,GAAI,CAAC,SAAS,MAAM,EAAE,SAAS,CAAC,KAAG,MACF,CAAC;AAE1E,eAAO,MAAM,2BAA2B,GAAI,CAAC,GAAG,OAAO,EAAE,SAAS,MAAM,KAAG,CAAqC,CAAC;AAEjH,eAAO,MAAM,8BAA8B,GAAI,SAAS,wBAAwB,EAAE,WAAW,MAAM,KAAG,yBAIpG,CAAC;AAEH,eAAO,MAAM,qCAAqC,GAChD,SAAS,+BAA+B,EACxC,WAAW,MAAM,KAChB,gCAGD,CAAC;AAEH,eAAO,MAAM,6BAA6B,GAAI,OAAO;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,yBAAyB,CAAC;IAClC,eAAe,CAAC,EAAE,WAAW,CAAC;CAC/B,KAAG,6BAYH,CAAC;AAEF,KAAK,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC,CAAC;AAapF,eAAO,MAAM,6BAA6B,GACxC,SAAS,WAAW,KACnB;IACD,QAAQ,EAAE,KAAK,CAAC;IAChB,MAAM,EAAE,yBAAyB,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,GAAG,IAqBH,CAAC;AAEF,eAAO,MAAM,oCAAoC,GAAI,UAAU,yBAAyB,KAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAI/G,CAAC;AAEH,eAAO,MAAM,2CAA2C,GACtD,UAAU,gCAAgC,KACzC,MAAM,CAAC,MAAM,EAAE,OAAO,CAGvB,CAAC;AAEH,eAAO,MAAM,iCAAiC,GAAI,gBAAgB,MAAM,KAAG,wBAgB1E,CAAC;AAEF,eAAO,MAAM,wCAAwC,GAAI,gBAAgB,MAAM,KAAG,+BAYjF,CAAC;AAEF,eAAO,MAAM,4CAA4C,GACvD,gBAAgB,MAAM,KACrB,mCAaF,CAAC;AAEF,MAAM,MAAM,wBAAwB,GAAG,UAAU,CAAC,OAAO,4BAA4B,CAAC,CAAC;AACvF,MAAM,MAAM,0BAA0B,GAAG,GAAG,CAAC"}
@@ -0,0 +1,267 @@
1
+ import { Buffer } from "node:buffer";
2
+ import { hashTypedData } from "viem";
3
+ import { assertFulfillmentPath, FULFILLMENT_API_VERSION, FULFILLMENT_DEFAULT_CHAIN_ID, FULFILLMENT_EIP712_DOMAIN_NAME, FULFILLMENT_EIP712_DOMAIN_VERSION, FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL, FULFILLMENT_TICKET_HEADER_CLIENT_REQUEST_ID, FULFILLMENT_TICKET_HEADER_PAYLOAD, FULFILLMENT_TICKET_HEADER_SIGNATURE, FULFILLMENT_TICKET_HEADER_TICKET_ID, FULFILLMENT_TICKET_HEADER_VERSION, FULFILLMENT_TICKET_REQUEST_ACTION, normalizeFulfillmentMethod, parseFulfillmentChainId, } from "./fulfillment-types.js";
4
+ import { FULFILLMENT_ZERO_HASH_32 } from "./fulfillment-hash.js";
5
+ const LOWER_HEX32_PATTERN = /^0x[a-f0-9]{64}$/;
6
+ const LOWER_HEX_PATTERN = /^0x[a-f0-9]+$/;
7
+ const ADDRESS_PATTERN = /^0x[a-f0-9]{40}$/;
8
+ const parseUintLike = (value, field) => {
9
+ if (typeof value === "bigint") {
10
+ if (value < 0n)
11
+ throw new Error(`${field} must be non-negative.`);
12
+ return value;
13
+ }
14
+ if (typeof value === "number") {
15
+ if (!Number.isInteger(value) || value < 0)
16
+ throw new Error(`${field} must be a non-negative integer.`);
17
+ return BigInt(value);
18
+ }
19
+ const trimmed = value.trim();
20
+ if (!/^\d+$/.test(trimmed))
21
+ throw new Error(`${field} must be an unsigned integer string.`);
22
+ return BigInt(trimmed);
23
+ };
24
+ const normalizeLowerHex = (value, field) => {
25
+ const normalized = value.trim().toLowerCase();
26
+ if (!LOWER_HEX_PATTERN.test(normalized))
27
+ throw new Error(`${field} must be a lowercase 0x-prefixed hex string.`);
28
+ return normalized;
29
+ };
30
+ const normalizeAddress = (value, field) => {
31
+ const normalized = value.trim().toLowerCase();
32
+ if (!ADDRESS_PATTERN.test(normalized))
33
+ throw new Error(`${field} must be a lowercase 20-byte address.`);
34
+ return normalized;
35
+ };
36
+ const normalizeHex32 = (value, field) => {
37
+ const normalized = value.trim().toLowerCase();
38
+ if (!LOWER_HEX32_PATTERN.test(normalized))
39
+ throw new Error(`${field} must be a lowercase bytes32 hex string.`);
40
+ return normalized;
41
+ };
42
+ const normalizeAsciiString = (value, field, maxLen) => {
43
+ const trimmed = value.trim();
44
+ if (!trimmed)
45
+ throw new Error(`${field} is required.`);
46
+ if (trimmed.length > maxLen)
47
+ throw new Error(`${field} exceeds max length ${maxLen}.`);
48
+ if (!/^[\x20-\x7E]+$/.test(trimmed))
49
+ throw new Error(`${field} must be printable ASCII.`);
50
+ return trimmed;
51
+ };
52
+ const serializeBigIntJson = (value) => JSON.stringify(value, (_key, entryValue) => (typeof entryValue === "bigint" ? entryValue.toString() : entryValue));
53
+ const parseBase64UrlJson = (payload) => {
54
+ const text = Buffer.from(payload, "base64url").toString("utf8");
55
+ return JSON.parse(text);
56
+ };
57
+ export const FULFILLMENT_EIP712_TYPES = {
58
+ FulfillmentTicket: [
59
+ { name: "ticketId", type: "bytes32" },
60
+ { name: "consumer", type: "address" },
61
+ { name: "merchantOwner", type: "address" },
62
+ { name: "gatewayConfigIdHash", type: "bytes32" },
63
+ { name: "serviceSlug", type: "string" },
64
+ { name: "method", type: "string" },
65
+ { name: "path", type: "string" },
66
+ { name: "queryHash", type: "bytes32" },
67
+ { name: "bodyHash", type: "bytes32" },
68
+ { name: "cost", type: "uint256" },
69
+ { name: "issuedAt", type: "uint256" },
70
+ { name: "expiresAt", type: "uint256" },
71
+ ],
72
+ FulfillmentDeliveryProof: [
73
+ { name: "ticketId", type: "bytes32" },
74
+ { name: "deliveryProofId", type: "bytes32" },
75
+ { name: "merchantSigner", type: "address" },
76
+ { name: "serviceSlug", type: "string" },
77
+ { name: "completedAt", type: "uint256" },
78
+ { name: "statusCode", type: "uint256" },
79
+ { name: "latencyMs", type: "uint256" },
80
+ { name: "responseHash", type: "bytes32" },
81
+ ],
82
+ FulfillmentTicketRequestAuth: [
83
+ { name: "action", type: "string" },
84
+ { name: "serviceSlug", type: "string" },
85
+ { name: "method", type: "string" },
86
+ { name: "path", type: "string" },
87
+ { name: "queryHash", type: "bytes32" },
88
+ { name: "bodyHash", type: "bytes32" },
89
+ { name: "cost", type: "uint256" },
90
+ { name: "issuedAt", type: "uint256" },
91
+ { name: "nonce", type: "string" },
92
+ ],
93
+ };
94
+ export const buildFulfillmentEip712Domain = (chainId = parseFulfillmentChainId(undefined)) => ({
95
+ name: FULFILLMENT_EIP712_DOMAIN_NAME,
96
+ version: FULFILLMENT_EIP712_DOMAIN_VERSION,
97
+ chainId,
98
+ verifyingContract: FULFILLMENT_EIP712_VERIFIYING_CONTRACT_SENTINEL,
99
+ });
100
+ export const normalizeFulfillmentTicketMessage = (input) => ({
101
+ ticketId: normalizeHex32(input.ticketId, "ticketId"),
102
+ consumer: normalizeAddress(input.consumer, "consumer"),
103
+ merchantOwner: normalizeAddress(input.merchantOwner, "merchantOwner"),
104
+ gatewayConfigIdHash: normalizeHex32(input.gatewayConfigIdHash, "gatewayConfigIdHash"),
105
+ serviceSlug: normalizeAsciiString(input.serviceSlug, "serviceSlug", 256),
106
+ method: normalizeFulfillmentMethod(input.method),
107
+ path: assertFulfillmentPath(input.path),
108
+ queryHash: normalizeHex32(input.queryHash, "queryHash"),
109
+ bodyHash: normalizeHex32(input.bodyHash, "bodyHash"),
110
+ cost: parseUintLike(input.cost, "cost"),
111
+ issuedAt: parseUintLike(input.issuedAt, "issuedAt"),
112
+ expiresAt: parseUintLike(input.expiresAt, "expiresAt"),
113
+ });
114
+ export const normalizeFulfillmentDeliveryProofMessage = (input) => ({
115
+ ticketId: normalizeHex32(input.ticketId, "ticketId"),
116
+ deliveryProofId: normalizeHex32(input.deliveryProofId, "deliveryProofId"),
117
+ merchantSigner: normalizeAddress(input.merchantSigner, "merchantSigner"),
118
+ serviceSlug: normalizeAsciiString(input.serviceSlug, "serviceSlug", 256),
119
+ completedAt: parseUintLike(input.completedAt, "completedAt"),
120
+ statusCode: parseUintLike(input.statusCode, "statusCode"),
121
+ latencyMs: parseUintLike(input.latencyMs, "latencyMs"),
122
+ responseHash: input.responseHash == null ? FULFILLMENT_ZERO_HASH_32 : normalizeHex32(input.responseHash, "responseHash"),
123
+ });
124
+ export const normalizeFulfillmentTicketRequestAuthMessage = (input) => ({
125
+ action: FULFILLMENT_TICKET_REQUEST_ACTION,
126
+ serviceSlug: normalizeAsciiString(input.serviceSlug, "serviceSlug", 256),
127
+ method: normalizeFulfillmentMethod(input.method),
128
+ path: assertFulfillmentPath(input.path),
129
+ queryHash: normalizeHex32(input.queryHash, "queryHash"),
130
+ bodyHash: normalizeHex32(input.bodyHash, "bodyHash"),
131
+ cost: parseUintLike(input.cost, "cost"),
132
+ issuedAt: parseUintLike(input.issuedAt, "issuedAt"),
133
+ nonce: normalizeAsciiString(input.nonce, "nonce", 256),
134
+ });
135
+ export const buildFulfillmentTicketTypedData = (message, options) => ({
136
+ domain: buildFulfillmentEip712Domain(options?.chainId ?? FULFILLMENT_DEFAULT_CHAIN_ID),
137
+ types: { FulfillmentTicket: FULFILLMENT_EIP712_TYPES.FulfillmentTicket },
138
+ primaryType: "FulfillmentTicket",
139
+ message,
140
+ });
141
+ export const buildFulfillmentDeliveryProofTypedData = (message, options) => ({
142
+ domain: buildFulfillmentEip712Domain(options?.chainId ?? FULFILLMENT_DEFAULT_CHAIN_ID),
143
+ types: { FulfillmentDeliveryProof: FULFILLMENT_EIP712_TYPES.FulfillmentDeliveryProof },
144
+ primaryType: "FulfillmentDeliveryProof",
145
+ message,
146
+ });
147
+ export const buildFulfillmentTicketRequestAuthTypedData = (message, options) => ({
148
+ domain: buildFulfillmentEip712Domain(options?.chainId ?? FULFILLMENT_DEFAULT_CHAIN_ID),
149
+ types: { FulfillmentTicketRequestAuth: FULFILLMENT_EIP712_TYPES.FulfillmentTicketRequestAuth },
150
+ primaryType: "FulfillmentTicketRequestAuth",
151
+ message,
152
+ });
153
+ export const hashFulfillmentTicketTypedData = (message, options) => hashTypedData(buildFulfillmentTicketTypedData(message, options));
154
+ export const hashFulfillmentDeliveryProofTypedData = (message, options) => hashTypedData(buildFulfillmentDeliveryProofTypedData(message, options));
155
+ export const hashFulfillmentTicketRequestAuthTypedData = (message, options) => hashTypedData(buildFulfillmentTicketRequestAuthTypedData(message, options));
156
+ export const encodeTypedPayloadBase64Url = (payload) => Buffer.from(serializeBigIntJson(payload), "utf8").toString("base64url");
157
+ export const decodeTypedPayloadBase64Url = (encoded) => parseBase64UrlJson(encoded);
158
+ export const buildFulfillmentTicketEnvelope = (payload, signature) => ({
159
+ version: FULFILLMENT_API_VERSION,
160
+ payload: encodeTypedPayloadBase64Url(payload),
161
+ signature: normalizeLowerHex(signature, "ticket.signature"),
162
+ });
163
+ export const buildFulfillmentDeliveryProofEnvelope = (payload, signature) => ({
164
+ payload: encodeTypedPayloadBase64Url(payload),
165
+ signature: normalizeLowerHex(signature, "deliveryProof.signature"),
166
+ });
167
+ export const buildFulfillmentTicketHeaders = (input) => {
168
+ const headers = {
169
+ [FULFILLMENT_TICKET_HEADER_VERSION]: String(input.ticket.version),
170
+ [FULFILLMENT_TICKET_HEADER_PAYLOAD]: input.ticket.payload,
171
+ [FULFILLMENT_TICKET_HEADER_SIGNATURE]: normalizeLowerHex(input.ticket.signature, "ticket.signature"),
172
+ [FULFILLMENT_TICKET_HEADER_TICKET_ID]: normalizeHex32(input.ticketId, "ticketId"),
173
+ };
174
+ const clientRequestId = input.clientRequestId?.trim();
175
+ if (clientRequestId) {
176
+ headers[FULFILLMENT_TICKET_HEADER_CLIENT_REQUEST_ID] = clientRequestId;
177
+ }
178
+ return headers;
179
+ };
180
+ const getHeaderValue = (headers, key) => {
181
+ if (typeof headers.get === "function") {
182
+ return headers.get(key);
183
+ }
184
+ const record = headers;
185
+ const direct = record[key];
186
+ if (typeof direct === "string")
187
+ return direct;
188
+ const fallback = record[key.toLowerCase()];
189
+ return typeof fallback === "string" ? fallback : null;
190
+ };
191
+ export const parseFulfillmentTicketHeaders = (headers) => {
192
+ const versionRaw = getHeaderValue(headers, FULFILLMENT_TICKET_HEADER_VERSION)?.trim();
193
+ const payloadRaw = getHeaderValue(headers, FULFILLMENT_TICKET_HEADER_PAYLOAD)?.trim();
194
+ const sigRaw = getHeaderValue(headers, FULFILLMENT_TICKET_HEADER_SIGNATURE)?.trim();
195
+ const ticketIdRaw = getHeaderValue(headers, FULFILLMENT_TICKET_HEADER_TICKET_ID)?.trim();
196
+ if (!versionRaw || !payloadRaw || !sigRaw || !ticketIdRaw)
197
+ return null;
198
+ if (versionRaw !== String(FULFILLMENT_API_VERSION))
199
+ return null;
200
+ try {
201
+ return {
202
+ ticketId: normalizeHex32(ticketIdRaw, "ticketId"),
203
+ ticket: {
204
+ version: FULFILLMENT_API_VERSION,
205
+ payload: payloadRaw,
206
+ signature: normalizeLowerHex(sigRaw, "ticket.signature"),
207
+ },
208
+ clientRequestId: getHeaderValue(headers, FULFILLMENT_TICKET_HEADER_CLIENT_REQUEST_ID)?.trim() ?? null,
209
+ };
210
+ }
211
+ catch {
212
+ return null;
213
+ }
214
+ };
215
+ export const redactFulfillmentTicketEnvelopeDebug = (envelope) => ({
216
+ version: envelope.version,
217
+ payloadLength: envelope.payload.length,
218
+ signaturePrefix: envelope.signature.slice(0, 10),
219
+ });
220
+ export const redactFulfillmentDeliveryProofEnvelopeDebug = (envelope) => ({
221
+ payloadLength: envelope.payload.length,
222
+ signaturePrefix: envelope.signature.slice(0, 10),
223
+ });
224
+ export const parseWireFulfillmentTicketMessage = (encodedPayload) => {
225
+ const decoded = decodeTypedPayloadBase64Url(encodedPayload);
226
+ return normalizeFulfillmentTicketMessage({
227
+ ticketId: String(decoded.ticketId ?? ""),
228
+ consumer: String(decoded.consumer ?? ""),
229
+ merchantOwner: String(decoded.merchantOwner ?? ""),
230
+ gatewayConfigIdHash: String(decoded.gatewayConfigIdHash ?? ""),
231
+ serviceSlug: String(decoded.serviceSlug ?? ""),
232
+ method: String(decoded.method ?? ""),
233
+ path: String(decoded.path ?? ""),
234
+ queryHash: String(decoded.queryHash ?? ""),
235
+ bodyHash: String(decoded.bodyHash ?? ""),
236
+ cost: decoded.cost,
237
+ issuedAt: decoded.issuedAt,
238
+ expiresAt: decoded.expiresAt,
239
+ });
240
+ };
241
+ export const parseWireFulfillmentDeliveryProofMessage = (encodedPayload) => {
242
+ const decoded = decodeTypedPayloadBase64Url(encodedPayload);
243
+ return normalizeFulfillmentDeliveryProofMessage({
244
+ ticketId: String(decoded.ticketId ?? ""),
245
+ deliveryProofId: String(decoded.deliveryProofId ?? ""),
246
+ merchantSigner: String(decoded.merchantSigner ?? ""),
247
+ serviceSlug: String(decoded.serviceSlug ?? ""),
248
+ completedAt: decoded.completedAt,
249
+ statusCode: decoded.statusCode,
250
+ latencyMs: decoded.latencyMs,
251
+ responseHash: typeof decoded.responseHash === "string" ? decoded.responseHash : undefined,
252
+ });
253
+ };
254
+ export const parseWireFulfillmentTicketRequestAuthMessage = (encodedPayload) => {
255
+ const decoded = decodeTypedPayloadBase64Url(encodedPayload);
256
+ return normalizeFulfillmentTicketRequestAuthMessage({
257
+ action: typeof decoded.action === "string" ? decoded.action : undefined,
258
+ serviceSlug: String(decoded.serviceSlug ?? ""),
259
+ method: String(decoded.method ?? ""),
260
+ path: String(decoded.path ?? ""),
261
+ queryHash: String(decoded.queryHash ?? ""),
262
+ bodyHash: String(decoded.bodyHash ?? ""),
263
+ cost: decoded.cost,
264
+ issuedAt: decoded.issuedAt,
265
+ nonce: String(decoded.nonce ?? ""),
266
+ });
267
+ };
@@ -0,0 +1,20 @@
1
+ import type { Hex32 } from "./fulfillment-types.js";
2
+ export declare class FulfillmentCanonicalizationError extends Error {
3
+ code: string;
4
+ constructor(code: string, message: string);
5
+ }
6
+ export declare const FULFILLMENT_ZERO_HASH_32: "0x0000000000000000000000000000000000000000000000000000000000000000";
7
+ export declare const canonicalizeJsonJcs: (value: unknown) => string;
8
+ export declare const sha256HexUtf8: (value: string) => Hex32;
9
+ export declare const hashCanonicalJsonJcs: (value: unknown) => Hex32;
10
+ export type FulfillmentCanonicalQueryPair = {
11
+ key: string;
12
+ value: string;
13
+ };
14
+ export declare const canonicalizeFulfillmentQuery: (rawQuery: string | null | undefined) => {
15
+ canonical: string;
16
+ pairs: FulfillmentCanonicalQueryPair[];
17
+ };
18
+ export declare const hashCanonicalFulfillmentQuery: (rawQuery: string | null | undefined) => Hex32;
19
+ export declare const hashCanonicalFulfillmentBodyJson: (payload: unknown) => Hex32;
20
+ //# sourceMappingURL=fulfillment-hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fulfillment-hash.d.ts","sourceRoot":"","sources":["../src/fulfillment-hash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAC;AAEpD,qBAAa,gCAAiC,SAAQ,KAAK;IACzD,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM;CAK1C;AAED,eAAO,MAAM,wBAAwB,sEAC0D,CAAC;AAuEhG,eAAO,MAAM,mBAAmB,GAAI,OAAO,OAAO,KAAG,MAAsC,CAAC;AAI5F,eAAO,MAAM,aAAa,GAAI,OAAO,MAAM,KAAG,KAAkD,CAAC;AAEjG,eAAO,MAAM,oBAAoB,GAAI,OAAO,OAAO,KAAG,KAAkD,CAAC;AA6BzG,MAAM,MAAM,6BAA6B,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE3E,eAAO,MAAM,4BAA4B,GACvC,UAAU,MAAM,GAAG,IAAI,GAAG,SAAS,KAClC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,6BAA6B,EAAE,CAAA;CAiD7D,CAAC;AAEF,eAAO,MAAM,6BAA6B,GAAI,UAAU,MAAM,GAAG,IAAI,GAAG,SAAS,KAAG,KAGnF,CAAC;AAEF,eAAO,MAAM,gCAAgC,GAAI,SAAS,OAAO,KAAG,KAAsC,CAAC"}