@yodlpay/payment-decoder 1.3.2 → 1.3.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/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Decode Yodl payment transaction hashes into structured payment data. Supports direct payments, swaps, and cross-chain bridges.
4
4
 
5
+ Bridge discovery currently supports Relay (source + destination hashes) and Across source-chain deposits via Across status API.
6
+
5
7
  ## Installation
6
8
 
7
9
  ```bash
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ declare function detectChain(hash: Hex, clients: ChainClients): Promise<{
12
12
  receipt: TransactionReceipt;
13
13
  }>;
14
14
 
15
- type ServiceProvider = "relay";
15
+ type ServiceProvider = "relay" | "across";
16
16
  interface Webhook {
17
17
  webhookAddress: Address;
18
18
  payload: readonly Hex[];
@@ -75,6 +75,15 @@ interface YodlPayment {
75
75
  blockTimestamp: Date;
76
76
  }
77
77
 
78
+ interface DecodeBridgeOptions {
79
+ includeAcross?: boolean;
80
+ /** Receipt of the fill (destination) tx, used for Across fill-side lookups. */
81
+ fillReceipt?: TransactionReceipt;
82
+ }
83
+ declare function decodeBridgePayment(hash: Hex, clients: ChainClients, options?: DecodeBridgeOptions): Promise<Extract<PaymentInfo, {
84
+ type: "bridge";
85
+ }>>;
86
+
78
87
  /**
79
88
  * Decoded Yodl event data from onchain logs.
80
89
  * Does not include blockTimestamp or webhooks — callers provide those.
@@ -102,18 +111,6 @@ declare class NoYodlEventError extends Error {
102
111
 
103
112
  declare function decodePayment(hash: Hex, chainId: number, clients: ChainClients, cachedReceipt?: TransactionReceipt): Promise<PaymentInfo>;
104
113
 
105
- /**
106
- * Decode a bridge transaction given any hash (source or destination).
107
- * Fetches bridge info from Relay, then decodes the Yodl event from the destination chain.
108
- * Extracts the original sender from the source chain's UserOperationEvent.
109
- *
110
- * SECURITY: Token data is extracted from onchain logs, not trusted from Relay.
111
- * Relay is only used for bridge discovery (hashes + chain IDs).
112
- */
113
- declare function decodeBridgePayment(hash: Hex, clients: ChainClients): Promise<Extract<PaymentInfo, {
114
- type: "bridge";
115
- }>>;
116
-
117
114
  declare function decodeYodlPayment(txHash: Hex, chainId: number, clients: ChainClients, cachedReceipt?: TransactionReceipt): Promise<YodlPayment>;
118
115
 
119
116
  export { type BridgeInfo, type ChainClients, type DecodedYodlEvent, NoBridgeFoundError, NoYodlEventError, type PaymentEvent, type PaymentInfo, type ServiceProvider, type SwapInfo, type TokenInInfo, type TokenOutInfo, type Webhook, type YodlPayment, createClients, decodeBridgePayment, decodePayment, decodeYodlFromLogs, decodeYodlPayment, detectChain };
package/dist/index.js CHANGED
@@ -1,46 +1,119 @@
1
- // src/clients.ts
2
- import { chains } from "@yodlpay/tokenlists";
1
+ // src/across-bridge.ts
2
+ import { parseDepositLogs, parseFillLogs } from "@across-protocol/app-sdk";
3
+ import { getRouter as getRouter2 } from "@yodlpay/tokenlists";
4
+
5
+ // src/across-client.ts
6
+ import { isHash } from "viem";
7
+ import { z } from "zod";
8
+
9
+ // src/errors.ts
3
10
  import {
4
- createPublicClient,
5
- http
11
+ AbiDecodingDataSizeTooSmallError,
12
+ AbiEventSignatureEmptyTopicsError,
13
+ AbiEventSignatureNotFoundError,
14
+ DecodeLogTopicsMismatch
6
15
  } from "viem";
7
- var rpcOverrides = {
8
- 137: "https://polygon-bor-rpc.publicnode.com"
9
- // polygon-rpc.com is down
16
+ var ExpectedDecodeError = class extends Error {
17
+ constructor(message = "Expected decode error") {
18
+ super(message);
19
+ this.name = "ExpectedDecodeError";
20
+ }
10
21
  };
11
- function createClients() {
12
- const clients = {};
13
- for (const chain of chains) {
14
- clients[chain.id] = createPublicClient({
15
- chain,
16
- transport: http(rpcOverrides[chain.id])
17
- });
22
+ var NoBridgeFoundError = class extends Error {
23
+ constructor(message = "No bridge transaction found") {
24
+ super(message);
25
+ this.name = "NoBridgeFoundError";
18
26
  }
19
- return clients;
20
- }
21
- function getClient(clients, chainId) {
22
- const client = clients[chainId];
23
- if (!client) throw new Error(`No client configured for chain ${chainId}`);
24
- return client;
27
+ };
28
+ var NoYodlEventError = class extends Error {
29
+ constructor(message = "No Yodl event found in logs") {
30
+ super(message);
31
+ this.name = "NoYodlEventError";
32
+ }
33
+ };
34
+ function isExpectedDecodeError(error) {
35
+ return error instanceof AbiEventSignatureNotFoundError || error instanceof AbiEventSignatureEmptyTopicsError || error instanceof AbiDecodingDataSizeTooSmallError || error instanceof DecodeLogTopicsMismatch;
25
36
  }
26
- async function detectChain(hash, clients) {
27
- const entries = Object.entries(clients).map(([id, client]) => ({
28
- chainId: Number(id),
29
- client
30
- }));
31
- const results = await Promise.allSettled(
32
- entries.map(async ({ chainId, client }) => {
33
- const receipt = await client.getTransactionReceipt({ hash });
34
- return { chainId, receipt };
35
- })
36
- );
37
- const found = results.find(
38
- (r) => r.status === "fulfilled"
39
- );
40
- if (!found) {
41
- throw new Error(`Transaction ${hash} not found on any configured chain`);
37
+
38
+ // src/across-client.ts
39
+ var ACROSS_API = "https://app.across.to/api/deposit/status";
40
+ var ACROSS_TIMEOUT_MS = 8e3;
41
+ var chainId = z.union([
42
+ z.number().int().positive(),
43
+ z.string().regex(/^\d+$/).transform(Number)
44
+ ]).pipe(z.number().int().positive());
45
+ var txHash = z.string().refine((v) => isHash(v));
46
+ var nullableTxHash = txHash.nullish().transform((v) => v ?? null);
47
+ var depositId = z.union([
48
+ z.string().regex(/^\d+$/),
49
+ z.number().int().nonnegative().transform((v) => String(v))
50
+ ]);
51
+ var AcrossDepositStatusSchema = z.object({
52
+ status: z.enum(["filled", "pending", "expired", "refunded"]),
53
+ originChainId: chainId,
54
+ destinationChainId: chainId,
55
+ depositId,
56
+ depositTxnRef: txHash.optional(),
57
+ depositTxHash: txHash.optional(),
58
+ fillTxnRef: nullableTxHash,
59
+ fillTx: nullableTxHash,
60
+ actionsSucceeded: z.boolean().nullish().transform((v) => v ?? null)
61
+ }).transform((d) => {
62
+ const depositTxnRef = d.depositTxnRef ?? d.depositTxHash;
63
+ if (!depositTxnRef) {
64
+ throw new Error("Missing depositTxnRef and depositTxHash");
42
65
  }
43
- return found.value;
66
+ return {
67
+ status: d.status,
68
+ originChainId: d.originChainId,
69
+ destinationChainId: d.destinationChainId,
70
+ depositId: d.depositId,
71
+ depositTxnRef,
72
+ fillTxnRef: d.fillTxnRef ?? d.fillTx,
73
+ actionsSucceeded: d.actionsSucceeded
74
+ };
75
+ });
76
+ async function fetchAcrossDepositByDepositId(originChainId, depositId2) {
77
+ const url = new URL(ACROSS_API);
78
+ url.searchParams.set("originChainId", String(originChainId));
79
+ url.searchParams.set("depositId", depositId2);
80
+ return fetchAcrossDeposit(url);
81
+ }
82
+ async function fetchAcrossDeposit(url) {
83
+ const controller = new AbortController();
84
+ const timeout = setTimeout(() => controller.abort(), ACROSS_TIMEOUT_MS);
85
+ let response;
86
+ try {
87
+ response = await fetch(url, { signal: controller.signal });
88
+ } catch (error) {
89
+ const message = error instanceof Error && error.name === "AbortError" ? `Across API request timed out after ${ACROSS_TIMEOUT_MS}ms` : "Across API request failed";
90
+ throw new Error(message, { cause: error });
91
+ } finally {
92
+ clearTimeout(timeout);
93
+ }
94
+ if (response.status === 404) throw new NoBridgeFoundError();
95
+ if (!response.ok) {
96
+ const body = await response.text().catch(() => "");
97
+ const kind = response.status === 429 || response.status >= 500 ? "temporary error" : "error";
98
+ throw new Error(
99
+ `Across API ${kind} (${response.status})${body ? ` - ${body}` : ""}`
100
+ );
101
+ }
102
+ const data = await response.json().catch(() => {
103
+ throw new Error("Across API returned invalid JSON");
104
+ });
105
+ const result = AcrossDepositStatusSchema.safeParse(data);
106
+ if (!result.success) {
107
+ throw new Error(
108
+ `Across API response validation failed: ${result.error.message}`
109
+ );
110
+ }
111
+ return result.data;
112
+ }
113
+ async function fetchAcrossDepositByTx(hash) {
114
+ const url = new URL(ACROSS_API);
115
+ url.searchParams.set("depositTxnRef", hash);
116
+ return fetchAcrossDeposit(url);
44
117
  }
45
118
 
46
119
  // src/decode-utils.ts
@@ -85,35 +158,6 @@ var entryPointAbi = [
85
158
  }
86
159
  ];
87
160
 
88
- // src/errors.ts
89
- import {
90
- AbiDecodingDataSizeTooSmallError,
91
- AbiEventSignatureEmptyTopicsError,
92
- AbiEventSignatureNotFoundError,
93
- DecodeLogTopicsMismatch
94
- } from "viem";
95
- var ExpectedDecodeError = class extends Error {
96
- constructor(message = "Expected decode error") {
97
- super(message);
98
- this.name = "ExpectedDecodeError";
99
- }
100
- };
101
- var NoBridgeFoundError = class extends Error {
102
- constructor(message = "No bridge transaction found") {
103
- super(message);
104
- this.name = "NoBridgeFoundError";
105
- }
106
- };
107
- var NoYodlEventError = class extends Error {
108
- constructor(message = "No Yodl event found in logs") {
109
- super(message);
110
- this.name = "NoYodlEventError";
111
- }
112
- };
113
- function isExpectedDecodeError(error) {
114
- return error instanceof AbiEventSignatureNotFoundError || error instanceof AbiEventSignatureEmptyTopicsError || error instanceof AbiDecodingDataSizeTooSmallError || error instanceof DecodeLogTopicsMismatch;
115
- }
116
-
117
161
  // src/utils.ts
118
162
  import { getTokenByAddress } from "@yodlpay/tokenlists";
119
163
  import {
@@ -121,7 +165,7 @@ import {
121
165
  getContract,
122
166
  hexToString
123
167
  } from "viem";
124
- import { z } from "zod";
168
+ import { z as z2 } from "zod";
125
169
  function decodeMemo(memo) {
126
170
  if (!memo || memo === "0x") return "";
127
171
  try {
@@ -130,11 +174,11 @@ function decodeMemo(memo) {
130
174
  return "";
131
175
  }
132
176
  }
133
- var erc20String = z.string().max(64).transform((s) => s.replace(/\p{Cc}/gu, ""));
134
- var erc20Decimals = z.number().int().nonnegative().max(36);
135
- async function resolveToken(address, chainId, client) {
177
+ var erc20String = z2.string().max(64).transform((s) => s.replace(/\p{Cc}/gu, ""));
178
+ var erc20Decimals = z2.number().int().nonnegative().max(36);
179
+ async function resolveToken(address, chainId2, client) {
136
180
  try {
137
- return getTokenByAddress(address, chainId);
181
+ return getTokenByAddress(address, chainId2);
138
182
  } catch {
139
183
  const token = getContract({ address, abi: erc20Abi, client });
140
184
  const [name, symbol, decimals] = await Promise.all([
@@ -143,7 +187,7 @@ async function resolveToken(address, chainId, client) {
143
187
  token.read.decimals()
144
188
  ]);
145
189
  return {
146
- chainId,
190
+ chainId: chainId2,
147
191
  address,
148
192
  name: erc20String.parse(name),
149
193
  symbol: erc20String.parse(symbol),
@@ -323,29 +367,26 @@ function buildPaymentEvent(yodlEvent, webhooks, blockTimestamp, senderOverride)
323
367
  };
324
368
  }
325
369
 
326
- // src/payment-decoder.ts
327
- import { getRouter as getRouter2 } from "@yodlpay/tokenlists";
328
-
329
370
  // src/embedded-params.ts
330
371
  import { decodeFunctionData, toFunctionSelector } from "viem";
331
372
 
332
373
  // src/validation.ts
333
- import { chains as chains2 } from "@yodlpay/tokenlists";
374
+ import { chains } from "@yodlpay/tokenlists";
334
375
  import { isAddress, isHex } from "viem";
335
- import { z as z2 } from "zod";
336
- var validChainIds = chains2.map((c) => c.id);
337
- var txHashSchema = z2.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid transaction hash").transform((val) => val);
338
- var chainIdSchema = z2.coerce.number().int().refine((id) => validChainIds.includes(id), {
376
+ import { z as z3 } from "zod";
377
+ var validChainIds = chains.map((c) => c.id);
378
+ var txHashSchema = z3.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid transaction hash").transform((val) => val);
379
+ var chainIdSchema = z3.coerce.number().int().refine((id) => validChainIds.includes(id), {
339
380
  message: `Chain ID must be one of: ${validChainIds.join(", ")}`
340
381
  });
341
- var ArgsSchema = z2.union([
342
- z2.tuple([txHashSchema, chainIdSchema]),
343
- z2.tuple([txHashSchema])
382
+ var ArgsSchema = z3.union([
383
+ z3.tuple([txHashSchema, chainIdSchema]),
384
+ z3.tuple([txHashSchema])
344
385
  ]);
345
- var WebhooksSchema = z2.array(
346
- z2.object({
347
- webhookAddress: z2.string().refine((val) => isAddress(val)),
348
- payload: z2.array(z2.string().refine((val) => isHex(val)))
386
+ var WebhooksSchema = z3.array(
387
+ z3.object({
388
+ webhookAddress: z3.string().refine((val) => isAddress(val)),
389
+ payload: z3.array(z3.string().refine((val) => isHex(val)))
349
390
  }).transform((webhook) => ({
350
391
  ...webhook,
351
392
  memo: webhook.payload[0] ? decodeMemo(webhook.payload[0]) : ""
@@ -390,6 +431,53 @@ import {
390
431
  } from "viem";
391
432
  import { entryPoint08Address } from "viem/account-abstraction";
392
433
 
434
+ // src/clients.ts
435
+ import { chains as chains2 } from "@yodlpay/tokenlists";
436
+ import {
437
+ createPublicClient,
438
+ http
439
+ } from "viem";
440
+ var rpcOverrides = {
441
+ 1: "https://ethereum-rpc.publicnode.com",
442
+ // eth.merkle.io is down
443
+ 137: "https://polygon-bor-rpc.publicnode.com"
444
+ // polygon-rpc.com is down
445
+ };
446
+ function createClients() {
447
+ const clients = {};
448
+ for (const chain of chains2) {
449
+ clients[chain.id] = createPublicClient({
450
+ chain,
451
+ transport: http(rpcOverrides[chain.id])
452
+ });
453
+ }
454
+ return clients;
455
+ }
456
+ function getClient(clients, chainId2) {
457
+ const client = clients[chainId2];
458
+ if (!client) throw new Error(`No client configured for chain ${chainId2}`);
459
+ return client;
460
+ }
461
+ async function detectChain(hash, clients) {
462
+ const entries = Object.entries(clients).map(([id, client]) => ({
463
+ chainId: Number(id),
464
+ client
465
+ }));
466
+ const results = await Promise.allSettled(
467
+ entries.map(async ({ chainId: chainId2, client }) => {
468
+ const receipt = await client.getTransactionReceipt({ hash });
469
+ return { chainId: chainId2, receipt };
470
+ })
471
+ );
472
+ const found = results.find(
473
+ (r) => r.status === "fulfilled"
474
+ );
475
+ if (!found) {
476
+ throw new Error(`Transaction ${hash} not found on any configured chain`);
477
+ }
478
+ return found.value;
479
+ }
480
+
393
481
  // src/relay-client.ts
394
482
  var MAINNET_RELAY_API = "https://api.relay.link";
395
483
  var TESTNET_RELAY_API = "https://api.testnets.relay.link";
@@ -498,7 +586,7 @@ function parseRelayResponse(request) {
498
586
  inputAmountGross: BigInt(data?.metadata?.currencyIn?.amount ?? 0)
499
587
  };
500
588
  }
501
- async function decodeBridgePayment(hash, clients) {
589
+ async function decodeRelayBridgePayment(hash, clients) {
502
590
  const request = await fetchRelayRequest(hash);
503
591
  const {
504
592
  sourceChainId,
@@ -507,8 +595,11 @@ async function decodeBridgePayment(hash, clients) {
507
595
  destinationTxHash,
508
596
  inputAmountGross
509
597
  } = parseRelayResponse(request);
510
- const destProvider = getClient(clients, destinationChainId);
511
- const sourceProvider = getClient(clients, sourceChainId);
598
+ const destProvider = clients[destinationChainId];
599
+ const sourceProvider = clients[sourceChainId];
600
+ if (!destProvider || !sourceProvider) {
601
+ throw new NoBridgeFoundError();
602
+ }
512
603
  const { address: routerAddress } = getRouter(destinationChainId);
513
604
  const [destReceipt, sourceReceipt] = await Promise.all([
514
605
  destProvider.getTransactionReceipt({ hash: destinationTxHash }),
@@ -552,23 +643,161 @@ async function decodeBridgePayment(hash, clients) {
552
643
  return { type: "bridge", ...payment, ...bridge };
553
644
  }
554
645
 
646
+ // src/across-bridge.ts
647
+ async function resolveAcrossStatus(hash, fillReceipt) {
648
+ try {
649
+ return await fetchAcrossDepositByTx(hash);
650
+ } catch (error) {
651
+ if (!(error instanceof NoBridgeFoundError) || !fillReceipt) {
652
+ throw error;
653
+ }
654
+ }
655
+ const fillLog = parseFillLogs(fillReceipt.logs);
656
+ if (!fillLog) {
657
+ throw new NoBridgeFoundError();
658
+ }
659
+ const status = await fetchAcrossDepositByDepositId(
660
+ Number(fillLog.originChainId),
661
+ String(fillLog.depositId)
662
+ );
663
+ if (status.fillTxnRef !== hash) {
664
+ throw new NoBridgeFoundError();
665
+ }
666
+ return status;
667
+ }
668
+ async function decodeAcrossBridgePayment(hash, clients, fillReceipt) {
669
+ const status = await resolveAcrossStatus(hash, fillReceipt);
670
+ if (status.status !== "filled" || !status.fillTxnRef) {
671
+ throw new NoBridgeFoundError();
672
+ }
673
+ if (status.actionsSucceeded === false) {
674
+ throw new NoBridgeFoundError();
675
+ }
676
+ const sourceChainId = status.originChainId;
677
+ const sourceTxHash = status.depositTxnRef;
678
+ const destinationChainId = status.destinationChainId;
679
+ const destinationTxHash = status.fillTxnRef;
680
+ const sourceProvider = clients[sourceChainId];
681
+ const destProvider = clients[destinationChainId];
682
+ if (!sourceProvider || !destProvider) {
683
+ throw new NoBridgeFoundError();
684
+ }
685
+ const { address: routerAddress } = getRouter2(destinationChainId);
686
+ const [sourceReceipt, destReceipt] = await Promise.all([
687
+ sourceProvider.getTransactionReceipt({ hash: sourceTxHash }),
688
+ destProvider.getTransactionReceipt({ hash: destinationTxHash })
689
+ ]);
690
+ const yodlEvent = decodeYodlFromLogs(destReceipt.logs, routerAddress);
691
+ if (!yodlEvent) {
692
+ throw new NoBridgeFoundError();
693
+ }
694
+ const [destBlock, destTx, sender] = await Promise.all([
695
+ destProvider.getBlock({ blockNumber: destReceipt.blockNumber }),
696
+ destProvider.getTransaction({ hash: destinationTxHash }),
697
+ extractSenderFromSource(sourceReceipt, sourceProvider, sourceTxHash)
698
+ ]);
699
+ const depositLog = parseDepositLogs(sourceReceipt.logs);
700
+ const depositData = depositLog && depositLog.depositId === BigInt(status.depositId) ? {
701
+ tokenIn: depositLog.inputToken,
702
+ inputAmount: depositLog.inputAmount,
703
+ outputAmount: depositLog.outputAmount
704
+ } : void 0;
705
+ const fillLog = parseFillLogs(destReceipt.logs, {
706
+ depositId: BigInt(status.depositId)
707
+ });
708
+ const fillOutputAmount = fillLog?.relayExecutionInfo?.updatedOutputAmount ?? fillLog?.outputAmount ?? 0n;
709
+ let tokens;
710
+ try {
711
+ tokens = await resolveBridgeTokens(
712
+ sourceReceipt,
713
+ destReceipt,
714
+ yodlEvent,
715
+ sender,
716
+ 0n,
717
+ sourceChainId,
718
+ destinationChainId,
719
+ clients
720
+ );
721
+ } catch (error) {
722
+ if (error instanceof ExpectedDecodeError && depositData?.tokenIn && depositData.inputAmount > 0n) {
723
+ tokens = {
724
+ tokenIn: depositData.tokenIn,
725
+ tokenInAmount: depositData.inputAmount,
726
+ tokenOut: yodlEvent.token,
727
+ tokenOutAmount: yodlEvent.amount,
728
+ tokenOutAmountGross: fillOutputAmount > 0n ? fillOutputAmount : depositData.outputAmount > 0n ? depositData.outputAmount : yodlEvent.amount
729
+ };
730
+ } else {
731
+ throw error;
732
+ }
733
+ }
734
+ const tokenOutAmountGross = fillOutputAmount > 0n ? fillOutputAmount : depositData?.outputAmount && depositData.outputAmount > 0n ? depositData.outputAmount : tokens.tokenOutAmountGross > 0n ? tokens.tokenOutAmountGross : tokens.tokenOutAmount;
735
+ const bridge = {
736
+ sourceChainId,
737
+ sourceTxHash,
738
+ destinationChainId,
739
+ destinationTxHash,
740
+ ...tokens,
741
+ tokenIn: depositData?.tokenIn || tokens.tokenIn,
742
+ tokenInAmount: depositData?.inputAmount ?? tokens.tokenInAmount,
743
+ tokenOutAmountGross,
744
+ service: "across"
745
+ };
746
+ const payment = buildPaymentEvent(
747
+ yodlEvent,
748
+ extractEmbeddedParams(destTx.input),
749
+ toBlockTimestamp(destBlock),
750
+ sender
751
+ );
752
+ return { type: "bridge", ...payment, ...bridge };
753
+ }
754
+
755
+ // src/bridge-payment.ts
756
+ async function decodeBridgePayment(hash, clients, options = {}) {
757
+ const includeAcross = options.includeAcross ?? true;
758
+ let relayError;
759
+ try {
760
+ return await decodeRelayBridgePayment(hash, clients);
761
+ } catch (error) {
762
+ relayError = error;
763
+ if (!includeAcross) {
764
+ throw error;
765
+ }
766
+ }
767
+ try {
768
+ return await decodeAcrossBridgePayment(hash, clients, options.fillReceipt);
769
+ } catch (acrossError) {
770
+ if (!(relayError instanceof NoBridgeFoundError) && !(acrossError instanceof NoBridgeFoundError)) {
771
+ throw new Error("Both Relay and Across bridge decoders failed", {
772
+ cause: { relayError, acrossError }
773
+ });
774
+ }
775
+ if (relayError instanceof NoBridgeFoundError && acrossError instanceof NoBridgeFoundError) {
776
+ throw acrossError;
777
+ }
778
+ if (acrossError instanceof NoBridgeFoundError) {
779
+ throw relayError;
780
+ }
781
+ throw acrossError;
782
+ }
783
+ }
784
+
555
785
  // src/payment-decoder.ts
556
- async function tryDecodeBridge(hash, clients) {
786
+ import { getRouter as getRouter3 } from "@yodlpay/tokenlists";
787
+ async function tryDecodeBridge(hash, clients, options = {}) {
557
788
  try {
558
- return await decodeBridgePayment(hash, clients);
789
+ return await decodeBridgePayment(hash, clients, options);
559
790
  } catch (error) {
560
- if (!(error instanceof NoBridgeFoundError)) {
561
- if (process.env.NODE_ENV === "development") {
562
- console.warn("Unexpected error checking bridge info:", error);
563
- }
791
+ if (error instanceof NoBridgeFoundError) {
792
+ return void 0;
564
793
  }
565
- return void 0;
794
+ throw error;
566
795
  }
567
796
  }
568
- async function decodePayment(hash, chainId, clients, cachedReceipt) {
569
- const provider = getClient(clients, chainId);
797
+ async function decodePayment(hash, chainId2, clients, cachedReceipt) {
798
+ const provider = getClient(clients, chainId2);
570
799
  const receipt = cachedReceipt ?? await provider.getTransactionReceipt({ hash });
571
- const { address: routerAddress } = getRouter2(chainId);
800
+ const { address: routerAddress } = getRouter3(chainId2);
572
801
  const [block, tx] = await Promise.all([
573
802
  provider.getBlock({ blockNumber: receipt.blockNumber }),
574
803
  provider.getTransaction({ hash })
@@ -583,7 +812,9 @@ async function decodePayment(hash, chainId, clients, cachedReceipt) {
583
812
  if (swapEvent) {
584
813
  return { type: "swap", ...paymentEvent, ...swapEvent };
585
814
  }
586
- const bridgeResult2 = await tryDecodeBridge(hash, clients);
815
+ const bridgeResult2 = await tryDecodeBridge(hash, clients, {
816
+ fillReceipt: receipt
817
+ });
587
818
  if (bridgeResult2) return bridgeResult2;
588
819
  return { type: "direct", ...paymentEvent };
589
820
  }
@@ -631,7 +862,7 @@ async function buildTokenInfo(params, clients) {
631
862
  }
632
863
  };
633
864
  }
634
- async function extractTokenInfo(paymentInfo, chainId, txHash, clients) {
865
+ async function extractTokenInfo(paymentInfo, chainId2, txHash2, clients) {
635
866
  switch (paymentInfo.type) {
636
867
  case "direct": {
637
868
  return {
@@ -642,15 +873,15 @@ async function extractTokenInfo(paymentInfo, chainId, txHash, clients) {
642
873
  tokenOut: paymentInfo.token,
643
874
  tokenOutAmountGross: paymentInfo.amount,
644
875
  tokenOutAmountNet: paymentInfo.amount,
645
- inChainId: chainId,
646
- outChainId: chainId
876
+ inChainId: chainId2,
877
+ outChainId: chainId2
647
878
  },
648
879
  clients
649
880
  ),
650
- sourceChainId: chainId,
651
- sourceTxHash: txHash,
652
- destinationChainId: chainId,
653
- destinationTxHash: txHash,
881
+ sourceChainId: chainId2,
882
+ sourceTxHash: txHash2,
883
+ destinationChainId: chainId2,
884
+ destinationTxHash: txHash2,
654
885
  solver: null
655
886
  };
656
887
  }
@@ -663,15 +894,15 @@ async function extractTokenInfo(paymentInfo, chainId, txHash, clients) {
663
894
  tokenOut: paymentInfo.token,
664
895
  tokenOutAmountGross: paymentInfo.tokenOutAmount,
665
896
  tokenOutAmountNet: paymentInfo.amount,
666
- inChainId: chainId,
667
- outChainId: chainId
897
+ inChainId: chainId2,
898
+ outChainId: chainId2
668
899
  },
669
900
  clients
670
901
  ),
671
- sourceChainId: chainId,
672
- sourceTxHash: txHash,
673
- destinationChainId: chainId,
674
- destinationTxHash: txHash,
902
+ sourceChainId: chainId2,
903
+ sourceTxHash: txHash2,
904
+ destinationChainId: chainId2,
905
+ destinationTxHash: txHash2,
675
906
  solver: paymentInfo.service ?? null
676
907
  };
677
908
  }
@@ -698,10 +929,10 @@ async function extractTokenInfo(paymentInfo, chainId, txHash, clients) {
698
929
  }
699
930
  }
700
931
  }
701
- async function decodeYodlPayment(txHash, chainId, clients, cachedReceipt) {
932
+ async function decodeYodlPayment(txHash2, chainId2, clients, cachedReceipt) {
702
933
  const paymentInfo = await decodePayment(
703
- txHash,
704
- chainId,
934
+ txHash2,
935
+ chainId2,
705
936
  clients,
706
937
  cachedReceipt
707
938
  );
@@ -710,8 +941,8 @@ async function decodeYodlPayment(txHash, chainId, clients, cachedReceipt) {
710
941
  const processorMemo = firstWebhook?.memo ?? "";
711
942
  const tokenInfo = await extractTokenInfo(
712
943
  paymentInfo,
713
- chainId,
714
- txHash,
944
+ chainId2,
945
+ txHash2,
715
946
  clients
716
947
  );
717
948
  return {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yodlpay/payment-decoder",
3
- "version": "1.3.2",
3
+ "version": "1.3.3",
4
4
  "description": "Decode Yodl payment hashes into structured payment data",
5
5
  "keywords": [
6
6
  "yodl",
@@ -48,6 +48,7 @@
48
48
  "zod": "^4.3.6"
49
49
  },
50
50
  "dependencies": {
51
+ "@across-protocol/app-sdk": "^0.5.0",
51
52
  "@yodlpay/tokenlists": "^1.1.12"
52
53
  },
53
54
  "peerDependencies": {