@@ -2,9 +2,16 @@ import { Idl } from "@coral-xyz/anchor";
2
2
import { getDeployedAddress , SvmSpokeIdl } from "@across-protocol/contracts" ;
3
3
import { getSolanaChainId } from "@across-protocol/contracts/dist/src/svm/web3-v1" ;
4
4
import web3 , { Address , Commitment , GetSignaturesForAddressApi , GetTransactionApi , Signature } from "@solana/kit" ;
5
- import { bs58 } from "../../utils" ;
5
+ import { bs58 , chainIsSvm , getMessageHash } from "../../utils" ;
6
6
import { EventName , EventWithData , SVMProvider } from "./types" ;
7
7
import { decodeEvent , isDevnet } from "./utils" ;
8
+ import { DepositWithTime , FillWithTime } from "../../interfaces" ;
9
+ import { unwrapEventData } from "./" ;
10
+ import assert from "assert" ;
11
+ import {
12
+ FundsDepositedEventObject ,
13
+ FilledRelayEventObject ,
14
+ } from "@across-protocol/contracts/dist/typechain/contracts/SpokePool" ;
8
15
9
16
// Utility type to extract the return type for the JSON encoding overload. We only care about the overload where the
10
17
// configuration parameter (C) has the optional property 'encoding' set to 'json'.
@@ -19,6 +26,9 @@ type GetSignaturesForAddressConfig = Parameters<GetSignaturesForAddressApi["getS
19
26
type GetSignaturesForAddressTransaction = ReturnType < GetSignaturesForAddressApi [ "getSignaturesForAddress" ] > [ number ] ;
20
27
type GetSignaturesForAddressApiResponse = readonly GetSignaturesForAddressTransaction [ ] ;
21
28
29
+ export type DepositEventFromSignature = Omit < DepositWithTime , "fromLiteChain" | "toLiteChain" > ;
30
+ export type FillEventFromSignature = FillWithTime ;
31
+
22
32
export class SvmCpiEventsClient {
23
33
private rpc : SVMProvider ;
24
34
private programAddress : Address ;
@@ -211,6 +221,109 @@ export class SvmCpiEventsClient {
211
221
return events ;
212
222
}
213
223
224
+ /**
225
+ * Finds all FundsDeposited events for a given transaction signature.
226
+ *
227
+ * @param originChainId - The chain ID where the deposit originated.
228
+ * @param txSignature - The transaction signature to search for events.
229
+ * @param commitment - Optional commitment level for the transaction query.
230
+ * @returns A promise that resolves to an array of deposit events for the transaction, or undefined if none found.
231
+ */
232
+ public async getDepositEventsFromSignature (
233
+ originChainId : number ,
234
+ txSignature : Signature ,
235
+ commitment : Commitment = "confirmed"
236
+ ) : Promise < DepositEventFromSignature [ ] | undefined > {
237
+ assert ( chainIsSvm ( originChainId ) , `Origin chain ${ originChainId } is not an SVM chain` ) ;
238
+
239
+ const [ events , txDetails ] = await Promise . all ( [
240
+ this . readEventsFromSignature ( txSignature , commitment ) ,
241
+ this . rpc
242
+ . getTransaction ( txSignature , {
243
+ commitment,
244
+ maxSupportedTransactionVersion : 0 ,
245
+ } )
246
+ . send ( ) ,
247
+ ] ) ;
248
+
249
+ // Filter for FundsDeposited events only
250
+ const depositEvents = events ?. filter ( ( event ) => event ?. name === "FundsDeposited" ) ;
251
+
252
+ if ( ! txDetails || ! depositEvents ?. length ) {
253
+ return ;
254
+ }
255
+
256
+ return events . map ( ( event ) => {
257
+ const unwrappedEventArgs = unwrapEventData ( event as Record < string , unknown > , [ "depositId" ] ) as Record <
258
+ "data" ,
259
+ FundsDepositedEventObject
260
+ > ;
261
+
262
+ return {
263
+ ...unwrappedEventArgs . data ,
264
+ depositTimestamp : Number ( txDetails . blockTime ) ,
265
+ originChainId,
266
+ messageHash : getMessageHash ( unwrappedEventArgs . data . message ) ,
267
+ blockNumber : Number ( txDetails . slot ) ,
268
+ txnIndex : 0 ,
269
+ txnRef : txSignature ,
270
+ logIndex : 0 ,
271
+ destinationChainId : unwrappedEventArgs . data . destinationChainId . toNumber ( ) ,
272
+ } satisfies DepositEventFromSignature ;
273
+ } ) ;
274
+ }
275
+
276
+ /**
277
+ * Finds all FilledRelay events for a given transaction signature.
278
+ *
279
+ * @param destinationChainId - The destination chain ID (must be an SVM chain).
280
+ * @param txSignature - The transaction signature to search for events.
281
+ * @returns A promise that resolves to an array of fill events for the transaction, or undefined if none found.
282
+ */
283
+ public async getFillEventsFromSignature (
284
+ destinationChainId : number ,
285
+ txSignature : Signature ,
286
+ commitment : Commitment = "confirmed"
287
+ ) : Promise < FillEventFromSignature [ ] | undefined > {
288
+ assert ( chainIsSvm ( destinationChainId ) , `Destination chain ${ destinationChainId } is not an SVM chain` ) ;
289
+
290
+ // Find all events from the transaction signature and get transaction details
291
+ const [ events , txDetails ] = await Promise . all ( [
292
+ this . readEventsFromSignature ( txSignature , commitment ) ,
293
+ this . rpc
294
+ . getTransaction ( txSignature , {
295
+ commitment,
296
+ maxSupportedTransactionVersion : 0 ,
297
+ } )
298
+ . send ( ) ,
299
+ ] ) ;
300
+
301
+ // Filter for FilledRelay events only
302
+ const fillEvents = events ?. filter ( ( event ) => event ?. name === "FilledRelay" ) ;
303
+
304
+ if ( ! txDetails || ! fillEvents ?. length ) {
305
+ return ;
306
+ }
307
+
308
+ return fillEvents . map ( ( event ) => {
309
+ const unwrappedEventData = unwrapEventData ( event as Record < string , unknown > ) as Record <
310
+ "data" ,
311
+ FilledRelayEventObject
312
+ > ;
313
+ return {
314
+ ...unwrappedEventData . data ,
315
+ fillTimestamp : Number ( txDetails . blockTime ) ,
316
+ blockNumber : Number ( txDetails . slot ) ,
317
+ txnRef : txSignature ,
318
+ txnIndex : 0 ,
319
+ logIndex : 0 ,
320
+ destinationChainId,
321
+ repaymentChainId : unwrappedEventData . data . repaymentChainId . toNumber ( ) ,
322
+ originChainId : unwrappedEventData . data . originChainId . toNumber ( ) ,
323
+ } satisfies FillEventFromSignature ;
324
+ } ) ;
325
+ }
326
+
214
327
public getProgramAddress ( ) : Address {
215
328
return this . programAddress ;
216
329
}
0 commit comments