damm-sdk 1.4.30 → 1.4.32

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.
@@ -0,0 +1,300 @@
1
+ /**
2
+ * Client for Circle's iris-api V2 — the attestation service that watches
3
+ * CCTP V2 `depositForBurn` events and publishes signed attestations that
4
+ * the destination chain needs to mint USDC via `receiveMessage`.
5
+ *
6
+ * Only the production endpoint (https://iris-api.circle.com) is supported.
7
+ * Sandbox is intentionally omitted — we do not bridge via sandbox, and
8
+ * keeping a single base URL removes a class of foot-guns.
9
+ *
10
+ * API reference: https://developers.circle.com/stablecoins/iris-api-reference
11
+ */
12
+ import { z } from "zod";
13
+ import type { HexString } from "../../types";
14
+ export declare const CCTP_IRIS_API_BASE: "https://iris-api.circle.com";
15
+ export declare const IrisDecodedBurnBodySchema: z.ZodPipe<z.ZodObject<{
16
+ burnToken: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
17
+ mintRecipient: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
18
+ amount: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
19
+ messageSender: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
20
+ maxFee: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
21
+ feeExecuted: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
22
+ expirationBlock: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
23
+ hookData: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
24
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
25
+ burnToken: `0x${string}`;
26
+ mintRecipient: `0x${string}`;
27
+ amount: bigint;
28
+ messageSender: `0x${string}`;
29
+ maxFee: bigint;
30
+ feeExecuted: bigint;
31
+ expirationBlock: bigint;
32
+ hookData: `0x${string}` | null;
33
+ }>, {
34
+ burnToken: `0x${string}`;
35
+ mintRecipient: `0x${string}`;
36
+ amount: bigint;
37
+ messageSender: `0x${string}`;
38
+ maxFee: bigint;
39
+ feeExecuted: bigint;
40
+ expirationBlock: bigint;
41
+ hookData: `0x${string}` | null;
42
+ }>>;
43
+ export type IrisDecodedBurnBody = z.infer<typeof IrisDecodedBurnBodySchema>;
44
+ export declare const IrisDecodedMessageSchema: z.ZodPipe<z.ZodObject<{
45
+ sourceDomain: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
46
+ destinationDomain: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
47
+ nonce: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
48
+ sender: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
49
+ recipient: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
50
+ destinationCaller: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
51
+ minFinalityThreshold: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
52
+ finalityThresholdExecuted: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
53
+ messageBody: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
54
+ decodedMessageBody: z.ZodNullable<z.ZodPipe<z.ZodObject<{
55
+ burnToken: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
56
+ mintRecipient: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
57
+ amount: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
58
+ messageSender: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
59
+ maxFee: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
60
+ feeExecuted: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
61
+ expirationBlock: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
62
+ hookData: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
63
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
64
+ burnToken: `0x${string}`;
65
+ mintRecipient: `0x${string}`;
66
+ amount: bigint;
67
+ messageSender: `0x${string}`;
68
+ maxFee: bigint;
69
+ feeExecuted: bigint;
70
+ expirationBlock: bigint;
71
+ hookData: `0x${string}` | null;
72
+ }>, {
73
+ burnToken: `0x${string}`;
74
+ mintRecipient: `0x${string}`;
75
+ amount: bigint;
76
+ messageSender: `0x${string}`;
77
+ maxFee: bigint;
78
+ feeExecuted: bigint;
79
+ expirationBlock: bigint;
80
+ hookData: `0x${string}` | null;
81
+ }>>>;
82
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
83
+ sourceDomain: number;
84
+ destinationDomain: number;
85
+ nonce: `0x${string}`;
86
+ sender: `0x${string}`;
87
+ recipient: `0x${string}`;
88
+ destinationCaller: `0x${string}`;
89
+ minFinalityThreshold: number;
90
+ finalityThresholdExecuted: number;
91
+ messageBody: `0x${string}`;
92
+ decodedMessageBody: Readonly<{
93
+ burnToken: `0x${string}`;
94
+ mintRecipient: `0x${string}`;
95
+ amount: bigint;
96
+ messageSender: `0x${string}`;
97
+ maxFee: bigint;
98
+ feeExecuted: bigint;
99
+ expirationBlock: bigint;
100
+ hookData: `0x${string}` | null;
101
+ }> | null;
102
+ }>, {
103
+ sourceDomain: number;
104
+ destinationDomain: number;
105
+ nonce: `0x${string}`;
106
+ sender: `0x${string}`;
107
+ recipient: `0x${string}`;
108
+ destinationCaller: `0x${string}`;
109
+ minFinalityThreshold: number;
110
+ finalityThresholdExecuted: number;
111
+ messageBody: `0x${string}`;
112
+ decodedMessageBody: Readonly<{
113
+ burnToken: `0x${string}`;
114
+ mintRecipient: `0x${string}`;
115
+ amount: bigint;
116
+ messageSender: `0x${string}`;
117
+ maxFee: bigint;
118
+ feeExecuted: bigint;
119
+ expirationBlock: bigint;
120
+ hookData: `0x${string}` | null;
121
+ }> | null;
122
+ }>>;
123
+ export type IrisDecodedMessage = z.infer<typeof IrisDecodedMessageSchema>;
124
+ /**
125
+ * Possible values of the `status` field. Circle does not publish the full
126
+ * enum, so we accept any string and surface the observed common values as
127
+ * a type hint. Known values at time of writing:
128
+ * - `complete` — attestation signed and available
129
+ * - `pending_confirmations` — still waiting for source-chain finality
130
+ */
131
+ export type IrisMessageStatus = "complete" | "pending_confirmations" | (string & {});
132
+ export declare const IrisMessageSchema: z.ZodPipe<z.ZodObject<{
133
+ attestation: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
134
+ message: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
135
+ eventNonce: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
136
+ cctpVersion: z.ZodNumber;
137
+ status: z.ZodType<IrisMessageStatus, unknown, z.core.$ZodTypeInternals<IrisMessageStatus, unknown>>;
138
+ decodedMessage: z.ZodNullable<z.ZodPipe<z.ZodObject<{
139
+ sourceDomain: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
140
+ destinationDomain: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
141
+ nonce: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
142
+ sender: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
143
+ recipient: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
144
+ destinationCaller: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
145
+ minFinalityThreshold: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
146
+ finalityThresholdExecuted: z.ZodPipe<z.ZodString, z.ZodTransform<number, string>>;
147
+ messageBody: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
148
+ decodedMessageBody: z.ZodNullable<z.ZodPipe<z.ZodObject<{
149
+ burnToken: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
150
+ mintRecipient: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
151
+ amount: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
152
+ messageSender: z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>;
153
+ maxFee: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
154
+ feeExecuted: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
155
+ expirationBlock: z.ZodPipe<z.ZodString, z.ZodTransform<bigint, string>>;
156
+ hookData: z.ZodNullable<z.ZodPipe<z.ZodString, z.ZodTransform<`0x${string}`, string>>>;
157
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
158
+ burnToken: `0x${string}`;
159
+ mintRecipient: `0x${string}`;
160
+ amount: bigint;
161
+ messageSender: `0x${string}`;
162
+ maxFee: bigint;
163
+ feeExecuted: bigint;
164
+ expirationBlock: bigint;
165
+ hookData: `0x${string}` | null;
166
+ }>, {
167
+ burnToken: `0x${string}`;
168
+ mintRecipient: `0x${string}`;
169
+ amount: bigint;
170
+ messageSender: `0x${string}`;
171
+ maxFee: bigint;
172
+ feeExecuted: bigint;
173
+ expirationBlock: bigint;
174
+ hookData: `0x${string}` | null;
175
+ }>>>;
176
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
177
+ sourceDomain: number;
178
+ destinationDomain: number;
179
+ nonce: `0x${string}`;
180
+ sender: `0x${string}`;
181
+ recipient: `0x${string}`;
182
+ destinationCaller: `0x${string}`;
183
+ minFinalityThreshold: number;
184
+ finalityThresholdExecuted: number;
185
+ messageBody: `0x${string}`;
186
+ decodedMessageBody: Readonly<{
187
+ burnToken: `0x${string}`;
188
+ mintRecipient: `0x${string}`;
189
+ amount: bigint;
190
+ messageSender: `0x${string}`;
191
+ maxFee: bigint;
192
+ feeExecuted: bigint;
193
+ expirationBlock: bigint;
194
+ hookData: `0x${string}` | null;
195
+ }> | null;
196
+ }>, {
197
+ sourceDomain: number;
198
+ destinationDomain: number;
199
+ nonce: `0x${string}`;
200
+ sender: `0x${string}`;
201
+ recipient: `0x${string}`;
202
+ destinationCaller: `0x${string}`;
203
+ minFinalityThreshold: number;
204
+ finalityThresholdExecuted: number;
205
+ messageBody: `0x${string}`;
206
+ decodedMessageBody: Readonly<{
207
+ burnToken: `0x${string}`;
208
+ mintRecipient: `0x${string}`;
209
+ amount: bigint;
210
+ messageSender: `0x${string}`;
211
+ maxFee: bigint;
212
+ feeExecuted: bigint;
213
+ expirationBlock: bigint;
214
+ hookData: `0x${string}` | null;
215
+ }> | null;
216
+ }>>>;
217
+ delayReason: z.ZodNullable<z.ZodString>;
218
+ }, z.core.$strip>, z.ZodTransform<Readonly<{
219
+ attestation: `0x${string}` | null;
220
+ message: `0x${string}` | null;
221
+ eventNonce: `0x${string}`;
222
+ cctpVersion: number;
223
+ status: IrisMessageStatus;
224
+ decodedMessage: Readonly<{
225
+ sourceDomain: number;
226
+ destinationDomain: number;
227
+ nonce: `0x${string}`;
228
+ sender: `0x${string}`;
229
+ recipient: `0x${string}`;
230
+ destinationCaller: `0x${string}`;
231
+ minFinalityThreshold: number;
232
+ finalityThresholdExecuted: number;
233
+ messageBody: `0x${string}`;
234
+ decodedMessageBody: Readonly<{
235
+ burnToken: `0x${string}`;
236
+ mintRecipient: `0x${string}`;
237
+ amount: bigint;
238
+ messageSender: `0x${string}`;
239
+ maxFee: bigint;
240
+ feeExecuted: bigint;
241
+ expirationBlock: bigint;
242
+ hookData: `0x${string}` | null;
243
+ }> | null;
244
+ }> | null;
245
+ delayReason: string | null;
246
+ }>, {
247
+ attestation: `0x${string}` | null;
248
+ message: `0x${string}` | null;
249
+ eventNonce: `0x${string}`;
250
+ cctpVersion: number;
251
+ status: IrisMessageStatus;
252
+ decodedMessage: Readonly<{
253
+ sourceDomain: number;
254
+ destinationDomain: number;
255
+ nonce: `0x${string}`;
256
+ sender: `0x${string}`;
257
+ recipient: `0x${string}`;
258
+ destinationCaller: `0x${string}`;
259
+ minFinalityThreshold: number;
260
+ finalityThresholdExecuted: number;
261
+ messageBody: `0x${string}`;
262
+ decodedMessageBody: Readonly<{
263
+ burnToken: `0x${string}`;
264
+ mintRecipient: `0x${string}`;
265
+ amount: bigint;
266
+ messageSender: `0x${string}`;
267
+ maxFee: bigint;
268
+ feeExecuted: bigint;
269
+ expirationBlock: bigint;
270
+ hookData: `0x${string}` | null;
271
+ }> | null;
272
+ }> | null;
273
+ delayReason: string | null;
274
+ }>>;
275
+ export type IrisMessage = z.infer<typeof IrisMessageSchema>;
276
+ export type FetchCctpMessagesArgs = Readonly<{
277
+ sourceChainId: number;
278
+ txHash: HexString;
279
+ }>;
280
+ /**
281
+ * Fetch all CCTP V2 messages produced by a source-chain transaction.
282
+ *
283
+ * A single `depositForBurn` emits one message, but the API returns an
284
+ * array to accommodate batched / compound transactions. Returns `[]` when
285
+ * iris has not yet indexed the tx.
286
+ *
287
+ * @throws if `sourceChainId` is not in the SDK's CCTP V2 domain registry
288
+ * @throws if iris responds with a non-2xx status
289
+ * @throws if the response body fails schema validation (e.g., new iris shape)
290
+ */
291
+ export declare const fetchCctpMessages: ({ sourceChainId, txHash, }: FetchCctpMessagesArgs) => Promise<readonly IrisMessage[]>;
292
+ /**
293
+ * Convenience: fetch the first message for a source-chain tx, or `null`
294
+ * if iris has no messages indexed for that tx.
295
+ *
296
+ * Use {@link fetchCctpMessages} directly when a single tx may have burned
297
+ * for multiple destinations (rare — CCTP V2 `depositForBurn` is 1:1).
298
+ */
299
+ export declare const fetchCctpMessage: (args: FetchCctpMessagesArgs) => Promise<IrisMessage | null>;
300
+ //# sourceMappingURL=cctp.iris.api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cctp.iris.api.d.ts","sourceRoot":"","sources":["../../../src/integrations/cctp/cctp.iris.api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAG7C,eAAO,MAAM,kBAAkB,+BAAyC,CAAC;AAkCzE,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;GAWC,CAAC;AAExC,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAI5E,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAaE,CAAC;AAExC,MAAM,MAAM,kBAAkB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,wBAAwB,CAAC,CAAC;AAI1E;;;;;;GAMG;AACH,MAAM,MAAM,iBAAiB,GAAG,UAAU,GAAG,uBAAuB,GAAG,CAAC,MAAM,GAAG,EAAE,CAAC,CAAC;AAErF,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAUS,CAAC;AAExC,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAQ5D,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IACzC,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,SAAS,CAAC;CACrB,CAAC,CAAC;AAEH;;;;;;;;;;GAUG;AACH,eAAO,MAAM,iBAAiB,+BAG3B,qBAAqB,KAAG,QAAQ,SAAS,WAAW,EAAE,CAexD,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,SAAgB,qBAAqB,KAAG,QAAQ,WAAW,GAAG,IAAI,CAG9F,CAAC"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Pure byte-level decoder for CCTP V2 cross-chain messages.
3
+ *
4
+ * Use when you need to verify or inspect the contents of a CCTP V2 message
5
+ * without trusting the pre-decoded fields from iris-api (defense in depth),
6
+ * or when working with a raw message blob produced by a source-chain tx.
7
+ *
8
+ * Message format reference:
9
+ * https://developers.circle.com/stablecoins/message-format
10
+ *
11
+ * The outer header (148 bytes) wraps a variable-length body. For USDC burn
12
+ * transfers the body is a `BurnMessage` V2 — the only body shape this
13
+ * module supports (other body shapes are non-USDC payloads that cli-damm
14
+ * does not currently finalize).
15
+ */
16
+ import type { Address } from "viem";
17
+ import type { HexString } from "../../types";
18
+ /** Version byte at the start of a CCTP V2 header. */
19
+ export declare const CCTP_V2_HEADER_VERSION: 1;
20
+ /** Version byte at the start of a CCTP V2 BurnMessage body. */
21
+ export declare const CCTP_V2_BURN_BODY_VERSION: 1;
22
+ /**
23
+ * Decoded CCTP V2 header. All bytes32 fields are preserved as 0x-prefixed
24
+ * hex strings (not narrowed to addresses) except `sender` and `recipient`,
25
+ * which Circle guarantees are address-padded bytes32 values.
26
+ */
27
+ export type CctpV2Header = Readonly<{
28
+ version: number;
29
+ sourceDomain: number;
30
+ destinationDomain: number;
31
+ nonce: HexString;
32
+ sender: Address;
33
+ recipient: Address;
34
+ destinationCaller: HexString;
35
+ minFinalityThreshold: number;
36
+ finalityThresholdExecuted: number;
37
+ messageBody: HexString;
38
+ }>;
39
+ /**
40
+ * Decoded CCTP V2 BurnMessage body — the message-body shape used for USDC
41
+ * bridging. `hookData` is empty (`"0x"`) for standard transfers.
42
+ */
43
+ export type CctpV2BurnMessageBody = Readonly<{
44
+ version: number;
45
+ burnToken: Address;
46
+ mintRecipient: Address;
47
+ amount: bigint;
48
+ messageSender: Address;
49
+ maxFee: bigint;
50
+ feeExecuted: bigint;
51
+ expirationBlock: bigint;
52
+ hookData: HexString;
53
+ }>;
54
+ /** Header + burn-body decoded together in one pass. */
55
+ export type CctpV2Message = Readonly<CctpV2Header & {
56
+ decodedBody: CctpV2BurnMessageBody;
57
+ }>;
58
+ /**
59
+ * Decode the outer CCTP V2 header and return it alongside the embedded
60
+ * `messageBody`. Does not inspect the body contents — use {@link decodeCctpV2BurnBody}
61
+ * for that.
62
+ *
63
+ * @throws if `message` is shorter than the 148-byte header
64
+ * @throws if the header version byte is not {@link CCTP_V2_HEADER_VERSION}
65
+ */
66
+ export declare const decodeCctpV2Header: (message: HexString) => CctpV2Header;
67
+ /**
68
+ * Decode a CCTP V2 BurnMessage body.
69
+ *
70
+ * @throws if `body` is shorter than the 228-byte fixed portion
71
+ * @throws if the body version byte is not {@link CCTP_V2_BURN_BODY_VERSION}
72
+ */
73
+ export declare const decodeCctpV2BurnBody: (body: HexString) => CctpV2BurnMessageBody;
74
+ /**
75
+ * Convenience: decode the outer header and the embedded burn body in a
76
+ * single call. Throws the same errors as {@link decodeCctpV2Header} and
77
+ * {@link decodeCctpV2BurnBody}.
78
+ */
79
+ export declare const decodeCctpV2Message: (message: HexString) => CctpV2Message;
80
+ //# sourceMappingURL=cctp.message.decoder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cctp.message.decoder.d.ts","sourceRoot":"","sources":["../../../src/integrations/cctp/cctp.message.decoder.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,qDAAqD;AACrD,eAAO,MAAM,sBAAsB,GAAa,CAAC;AAEjD,+DAA+D;AAC/D,eAAO,MAAM,yBAAyB,GAAa,CAAC;AA+BpD;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,iBAAiB,EAAE,SAAS,CAAC;IAC7B,oBAAoB,EAAE,MAAM,CAAC;IAC7B,yBAAyB,EAAE,MAAM,CAAC;IAClC,WAAW,EAAE,SAAS,CAAC;CAC1B,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,MAAM,qBAAqB,GAAG,QAAQ,CAAC;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,OAAO,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,SAAS,CAAC;CACvB,CAAC,CAAC;AAEH,uDAAuD;AACvD,MAAM,MAAM,aAAa,GAAG,QAAQ,CAChC,YAAY,GAAG;IACX,WAAW,EAAE,qBAAqB,CAAC;CACtC,CACJ,CAAC;AASF;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,YAAa,SAAS,KAAG,YA+BvD,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,SAAU,SAAS,KAAG,qBA2BtD,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,YAAa,SAAS,KAAG,aAIxD,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export { default as CctpTokenMessengerV2Abi } from "./token.messenger.v2.abi";
2
2
  export { default as CctpMessageTransmitterV2Abi } from "./message.transmitter.v2.abi";
3
3
  export * from "./cctp.v2";
4
+ export * from "./cctp.message.decoder";
5
+ export * from "./cctp.iris.api";
4
6
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/cctp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AACtF,cAAc,WAAW,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/integrations/cctp/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,EAAE,OAAO,IAAI,2BAA2B,EAAE,MAAM,8BAA8B,CAAC;AACtF,cAAc,WAAW,CAAC;AAC1B,cAAc,wBAAwB,CAAC;AACvC,cAAc,iBAAiB,CAAC"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "./dist/index.cjs",
5
5
  "types": "./dist/index.d.ts",
6
6
  "type": "module",
7
- "version": "1.4.30",
7
+ "version": "1.4.32",
8
8
  "files": [
9
9
  "dist",
10
10
  "src",
@@ -37,6 +37,7 @@
37
37
  "@uniswap/v4-sdk": "1.21.2",
38
38
  "axios": "1.7.2",
39
39
  "ethers": "5.7",
40
- "viem": "2.33.2"
40
+ "viem": "2.33.2",
41
+ "zod": "4.3.6"
41
42
  }
42
43
  }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * Client for Circle's iris-api V2 — the attestation service that watches
3
+ * CCTP V2 `depositForBurn` events and publishes signed attestations that
4
+ * the destination chain needs to mint USDC via `receiveMessage`.
5
+ *
6
+ * Only the production endpoint (https://iris-api.circle.com) is supported.
7
+ * Sandbox is intentionally omitted — we do not bridge via sandbox, and
8
+ * keeping a single base URL removes a class of foot-guns.
9
+ *
10
+ * API reference: https://developers.circle.com/stablecoins/iris-api-reference
11
+ */
12
+
13
+ import { getAddress } from "viem";
14
+ import { z } from "zod";
15
+ import type { Address } from "viem";
16
+ import type { HexString } from "../../types";
17
+ import { getCctpV2Domain } from "./cctp.v2";
18
+
19
+ export const CCTP_IRIS_API_BASE = "https://iris-api.circle.com" as const;
20
+
21
+ // ─── Shared refinements ──────────────────────────────────────────────────
22
+
23
+ const HexSchema = z
24
+ .string()
25
+ .regex(/^0x[0-9a-fA-F]*$/, "Expected a 0x-prefixed hex string")
26
+ .transform((v) => v as HexString);
27
+
28
+ const AddressSchema = z
29
+ .string()
30
+ .regex(/^0x[0-9a-fA-F]{40}$/, "Expected a 20-byte hex address")
31
+ .transform((v): Address => getAddress(v));
32
+
33
+ /** iris-api serialises large integers as JSON strings. Coerce them to bigint. */
34
+ const BigIntStringSchema = z
35
+ .string()
36
+ .regex(/^-?\d+$/, "Expected a decimal integer as string")
37
+ .transform((v) => BigInt(v));
38
+
39
+ /** iris-api serialises small integers as strings too. Coerce them to number. */
40
+ const NumberStringSchema = z
41
+ .string()
42
+ .regex(/^-?\d+$/, "Expected a decimal integer as string")
43
+ .transform((v) => {
44
+ const n = Number(v);
45
+ if (!Number.isSafeInteger(n)) {
46
+ throw new Error(`iris-api returned non-safe integer: ${v}`);
47
+ }
48
+ return n;
49
+ });
50
+
51
+ // ─── Decoded body ────────────────────────────────────────────────────────
52
+
53
+ export const IrisDecodedBurnBodySchema = z
54
+ .object({
55
+ burnToken: AddressSchema,
56
+ mintRecipient: AddressSchema,
57
+ amount: BigIntStringSchema,
58
+ messageSender: AddressSchema,
59
+ maxFee: BigIntStringSchema,
60
+ feeExecuted: BigIntStringSchema,
61
+ expirationBlock: BigIntStringSchema,
62
+ hookData: HexSchema.nullable(),
63
+ })
64
+ .transform((v) => Object.freeze(v));
65
+
66
+ export type IrisDecodedBurnBody = z.infer<typeof IrisDecodedBurnBodySchema>;
67
+
68
+ // ─── Decoded envelope ────────────────────────────────────────────────────
69
+
70
+ export const IrisDecodedMessageSchema = z
71
+ .object({
72
+ sourceDomain: NumberStringSchema,
73
+ destinationDomain: NumberStringSchema,
74
+ nonce: HexSchema,
75
+ sender: AddressSchema,
76
+ recipient: AddressSchema,
77
+ destinationCaller: HexSchema,
78
+ minFinalityThreshold: NumberStringSchema,
79
+ finalityThresholdExecuted: NumberStringSchema,
80
+ messageBody: HexSchema,
81
+ decodedMessageBody: IrisDecodedBurnBodySchema.nullable(),
82
+ })
83
+ .transform((v) => Object.freeze(v));
84
+
85
+ export type IrisDecodedMessage = z.infer<typeof IrisDecodedMessageSchema>;
86
+
87
+ // ─── Top-level message ───────────────────────────────────────────────────
88
+
89
+ /**
90
+ * Possible values of the `status` field. Circle does not publish the full
91
+ * enum, so we accept any string and surface the observed common values as
92
+ * a type hint. Known values at time of writing:
93
+ * - `complete` — attestation signed and available
94
+ * - `pending_confirmations` — still waiting for source-chain finality
95
+ */
96
+ export type IrisMessageStatus = "complete" | "pending_confirmations" | (string & {});
97
+
98
+ export const IrisMessageSchema = z
99
+ .object({
100
+ attestation: HexSchema.nullable(),
101
+ message: HexSchema.nullable(),
102
+ eventNonce: HexSchema,
103
+ cctpVersion: z.number().int(),
104
+ status: z.string() as z.ZodType<IrisMessageStatus>,
105
+ decodedMessage: IrisDecodedMessageSchema.nullable(),
106
+ delayReason: z.string().nullable(),
107
+ })
108
+ .transform((v) => Object.freeze(v));
109
+
110
+ export type IrisMessage = z.infer<typeof IrisMessageSchema>;
111
+
112
+ const IrisMessagesEnvelopeSchema = z.object({
113
+ messages: z.array(IrisMessageSchema).default([]),
114
+ });
115
+
116
+ // ─── Public API ──────────────────────────────────────────────────────────
117
+
118
+ export type FetchCctpMessagesArgs = Readonly<{
119
+ sourceChainId: number;
120
+ txHash: HexString;
121
+ }>;
122
+
123
+ /**
124
+ * Fetch all CCTP V2 messages produced by a source-chain transaction.
125
+ *
126
+ * A single `depositForBurn` emits one message, but the API returns an
127
+ * array to accommodate batched / compound transactions. Returns `[]` when
128
+ * iris has not yet indexed the tx.
129
+ *
130
+ * @throws if `sourceChainId` is not in the SDK's CCTP V2 domain registry
131
+ * @throws if iris responds with a non-2xx status
132
+ * @throws if the response body fails schema validation (e.g., new iris shape)
133
+ */
134
+ export const fetchCctpMessages = async ({
135
+ sourceChainId,
136
+ txHash,
137
+ }: FetchCctpMessagesArgs): Promise<readonly IrisMessage[]> => {
138
+ // Throws if the chain is not CCTP V2 — preserves the existing SDK error.
139
+ const sourceDomain = getCctpV2Domain(sourceChainId);
140
+
141
+ const url = `${CCTP_IRIS_API_BASE}/v2/messages/${sourceDomain}?transactionHash=${txHash}`;
142
+ const response = await fetch(url);
143
+
144
+ if (!response.ok) {
145
+ const body = await response.text().catch(() => "");
146
+ throw new Error(`iris-api returned ${response.status} ${response.statusText}: ${body}`);
147
+ }
148
+
149
+ const json: unknown = await response.json();
150
+ const parsed = IrisMessagesEnvelopeSchema.parse(json);
151
+ return Object.freeze(parsed.messages);
152
+ };
153
+
154
+ /**
155
+ * Convenience: fetch the first message for a source-chain tx, or `null`
156
+ * if iris has no messages indexed for that tx.
157
+ *
158
+ * Use {@link fetchCctpMessages} directly when a single tx may have burned
159
+ * for multiple destinations (rare — CCTP V2 `depositForBurn` is 1:1).
160
+ */
161
+ export const fetchCctpMessage = async (args: FetchCctpMessagesArgs): Promise<IrisMessage | null> => {
162
+ const messages = await fetchCctpMessages(args);
163
+ return messages[0] ?? null;
164
+ };