@yodlpay/payment-decoder 1.3.5 → 1.3.7

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.
Files changed (2) hide show
  1. package/dist/index.js +125 -64
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,3 +1,6 @@
1
+ // src/bridges/bridge-payment.ts
2
+ import { parseDepositLogs as parseDepositLogs2, parseFillLogs as parseFillLogs2 } from "@across-protocol/app-sdk";
3
+
1
4
  // src/errors.ts
2
5
  var ExpectedDecodeError = class extends Error {
3
6
  constructor(message = "Expected decode error") {
@@ -27,7 +30,7 @@ import { tokenlist } from "@yodlpay/tokenlists";
27
30
  import {
28
31
  erc20Abi as erc20Abi2,
29
32
  getAddress,
30
- isAddressEqual,
33
+ isAddressEqual as isAddressEqual2,
31
34
  parseEventLogs
32
35
  } from "viem";
33
36
 
@@ -45,9 +48,17 @@ import {
45
48
  erc20Abi,
46
49
  getContract,
47
50
  hexToString,
48
- stringify
51
+ isAddressEqual,
52
+ stringify,
53
+ zeroAddress
49
54
  } from "viem";
50
55
  import { z } from "zod";
56
+
57
+ // src/chains.ts
58
+ import { chains as allChains } from "@yodlpay/tokenlists";
59
+ var chains = allChains.filter((c) => !c.testnet);
60
+
61
+ // src/utils.ts
51
62
  function decodeMemo(memo) {
52
63
  if (!memo || memo === "0x") return "";
53
64
  try {
@@ -59,6 +70,19 @@ function decodeMemo(memo) {
59
70
  var erc20String = z.string().max(64).transform((s) => s.replace(/\p{Cc}/gu, ""));
60
71
  var erc20Decimals = z.number().int().nonnegative().max(36);
61
72
  async function resolveToken(address, chainId2, client) {
73
+ if (isAddressEqual(address, zeroAddress)) {
74
+ const chain = chains.find((c) => c.id === chainId2);
75
+ if (!chain) {
76
+ throw new Error(`Native token requested for unknown chainId ${chainId2}`);
77
+ }
78
+ return {
79
+ chainId: chainId2,
80
+ address,
81
+ name: chain.nativeCurrency.name,
82
+ symbol: chain.nativeCurrency.symbol,
83
+ decimals: chain.nativeCurrency.decimals
84
+ };
85
+ }
62
86
  try {
63
87
  return getTokenByAddress(address, chainId2);
64
88
  } catch {
@@ -82,7 +106,7 @@ async function resolveToken(address, chainId2, client) {
82
106
  function decodeYodlFromLogs(logs, routerAddress) {
83
107
  const parsed = parseEventLogs({
84
108
  abi: yodlAbi,
85
- logs: logs.filter((l) => isAddressEqual(l.address, routerAddress)),
109
+ logs: logs.filter((l) => isAddressEqual2(l.address, routerAddress)),
86
110
  eventName: "Yodl",
87
111
  strict: true
88
112
  });
@@ -130,7 +154,7 @@ function extractTokenTransfers(logs) {
130
154
  });
131
155
  }
132
156
  function isKnownToken(address) {
133
- return tokenlist.some((t) => isAddressEqual(t.address, address));
157
+ return tokenlist.some((t) => isAddressEqual2(t.address, address));
134
158
  }
135
159
  function transferSignature(t) {
136
160
  return `${getAddress(t.from)}:${getAddress(t.to)}:${t.amount}`;
@@ -177,7 +201,7 @@ function detectMirrorTokens(allTransfers) {
177
201
  return mirrors;
178
202
  }
179
203
  function findInputTransfer(transfers, sender) {
180
- const fromSender = transfers.filter((t) => isAddressEqual(t.from, sender));
204
+ const fromSender = transfers.filter((t) => isAddressEqual2(t.from, sender));
181
205
  if (fromSender.length === 0) return void 0;
182
206
  const maxAmount = fromSender.reduce(
183
207
  (max, t) => t.amount > max ? t.amount : max,
@@ -198,7 +222,7 @@ function findInputTransfer(transfers, sender) {
198
222
  function inferSwapFromTransfers(logs, sender, yodlToken) {
199
223
  const transfers = extractTokenTransfers(logs);
200
224
  const outgoingDifferentToken = transfers.filter(
201
- (t) => isAddressEqual(t.from, sender) && !isAddressEqual(t.token, yodlToken)
225
+ (t) => isAddressEqual2(t.from, sender) && !isAddressEqual2(t.token, yodlToken)
202
226
  );
203
227
  if (outgoingDifferentToken.length === 0) return void 0;
204
228
  const byToken = /* @__PURE__ */ new Map();
@@ -212,7 +236,7 @@ function inferSwapFromTransfers(logs, sender, yodlToken) {
212
236
  }
213
237
  }
214
238
  const incomingNonYodl = transfers.filter(
215
- (t) => isAddressEqual(t.to, sender) && !isAddressEqual(t.token, yodlToken)
239
+ (t) => isAddressEqual2(t.to, sender) && !isAddressEqual2(t.token, yodlToken)
216
240
  );
217
241
  for (const t of incomingNonYodl) {
218
242
  const key = getAddress(t.token);
@@ -232,7 +256,7 @@ function inferSwapFromTransfers(logs, sender, yodlToken) {
232
256
  }
233
257
  if (!tokenIn || tokenInAmount <= 0n) return void 0;
234
258
  const incomingYodlToken = transfers.filter(
235
- (t) => isAddressEqual(t.to, sender) && isAddressEqual(t.token, yodlToken)
259
+ (t) => isAddressEqual2(t.to, sender) && isAddressEqual2(t.token, yodlToken)
236
260
  );
237
261
  const tokenOutAmount = incomingYodlToken.reduce(
238
262
  (sum, t) => sum + t.amount,
@@ -264,12 +288,6 @@ import { decodeFunctionData, toFunctionSelector } from "viem";
264
288
  // src/validation.ts
265
289
  import { isAddress, isHash, isHex } from "viem";
266
290
  import { z as z2 } from "zod";
267
-
268
- // src/chains.ts
269
- import { chains as allChains } from "@yodlpay/tokenlists";
270
- var chains = allChains.filter((c) => !c.testnet);
271
-
272
- // src/validation.ts
273
291
  var validChainIds = chains.map((c) => c.id);
274
292
  var txHashSchema = z2.string().refine((val) => isHash(val), "Invalid transaction hash");
275
293
  var chainIdSchema = z2.coerce.number().int().refine((id) => validChainIds.includes(id), {
@@ -405,8 +423,10 @@ async function fetchAcrossDepositByTx(hash) {
405
423
  // src/bridges/relay-bridge.ts
406
424
  import { getRouter } from "@yodlpay/tokenlists";
407
425
  import {
408
- isAddressEqual as isAddressEqual2,
409
- parseEventLogs as parseEventLogs2
426
+ isAddress as isAddress2,
427
+ isAddressEqual as isAddressEqual3,
428
+ parseEventLogs as parseEventLogs2,
429
+ zeroAddress as zeroAddress2
410
430
  } from "viem";
411
431
  import { entryPoint08Abi, entryPoint08Address } from "viem/account-abstraction";
412
432
 
@@ -495,19 +515,18 @@ async function calculateOutputAmountGross(inputAmount, inputToken, inputChainId,
495
515
  if (decimalDiff > 0) return inputAmount / 10n ** BigInt(decimalDiff);
496
516
  return inputAmount * 10n ** BigInt(-decimalDiff);
497
517
  }
498
- async function extractSenderFromSource(sourceReceipt, sourceProvider, sourceHash) {
518
+ function extractSenderFromSource(sourceReceipt) {
499
519
  const userOps = parseEventLogs2({
500
520
  abi: entryPoint08Abi,
501
521
  logs: sourceReceipt.logs.filter(
502
- (l) => isAddressEqual2(l.address, entryPoint08Address)
522
+ (l) => isAddressEqual3(l.address, entryPoint08Address)
503
523
  ),
504
524
  eventName: "UserOperationEvent"
505
525
  });
506
526
  if (userOps[0]) {
507
527
  return userOps[0].args.sender;
508
528
  }
509
- const sourceTx = await sourceProvider.getTransaction({ hash: sourceHash });
510
- return sourceTx.from;
529
+ return sourceReceipt.from;
511
530
  }
512
531
  async function resolveBridgeTokens(sourceReceipt, destReceipt, yodlEvent, sender, inputAmountGross, inputChainId, outputChainId, clients) {
513
532
  const sourceTransfers = extractTokenTransfers(sourceReceipt.logs);
@@ -550,12 +569,15 @@ function parseRelayResponse(request) {
550
569
  if (!inTx?.hash || !inTx?.chainId || !outTx?.hash || !outTx?.chainId) {
551
570
  throw new NoBridgeFoundError();
552
571
  }
572
+ const rawInputToken = data?.metadata?.currencyIn?.currency?.address;
573
+ const inputToken = rawInputToken && isAddress2(rawInputToken) ? rawInputToken : void 0;
553
574
  return {
554
575
  sourceChainId: inTx.chainId,
555
576
  sourceTxHash: inTx.hash,
556
577
  destinationChainId: outTx.chainId,
557
578
  destinationTxHash: outTx.hash,
558
- inputAmountGross: BigInt(data?.metadata?.currencyIn?.amount ?? 0)
579
+ inputAmountGross: BigInt(data?.metadata?.currencyIn?.amount ?? 0),
580
+ inputToken
559
581
  };
560
582
  }
561
583
  async function decodeRelayBridgePayment(hash, clients, options = {}) {
@@ -565,7 +587,8 @@ async function decodeRelayBridgePayment(hash, clients, options = {}) {
565
587
  sourceTxHash,
566
588
  destinationChainId,
567
589
  destinationTxHash,
568
- inputAmountGross
590
+ inputAmountGross,
591
+ inputToken
569
592
  } = parseRelayResponse(request);
570
593
  const destProvider = clients[destinationChainId];
571
594
  const sourceProvider = clients[sourceChainId];
@@ -586,21 +609,38 @@ async function decodeRelayBridgePayment(hash, clients, options = {}) {
586
609
  "No Yodl event found in destination transaction"
587
610
  );
588
611
  }
589
- const [destBlock, destTx, sender] = await Promise.all([
612
+ const sender = extractSenderFromSource(sourceReceipt);
613
+ const [destBlock, destTx] = await Promise.all([
590
614
  destProvider.getBlock({ blockNumber: destReceipt.blockNumber }),
591
- destProvider.getTransaction({ hash: destinationTxHash }),
592
- extractSenderFromSource(sourceReceipt, sourceProvider, sourceTxHash)
615
+ destProvider.getTransaction({ hash: destinationTxHash })
593
616
  ]);
594
- const tokens = await resolveBridgeTokens(
595
- sourceReceipt,
596
- destReceipt,
597
- yodlEvent,
598
- sender,
599
- inputAmountGross,
600
- sourceChainId,
601
- destinationChainId,
602
- clients
603
- );
617
+ let tokens;
618
+ try {
619
+ tokens = await resolveBridgeTokens(
620
+ sourceReceipt,
621
+ destReceipt,
622
+ yodlEvent,
623
+ sender,
624
+ inputAmountGross,
625
+ sourceChainId,
626
+ destinationChainId,
627
+ clients
628
+ );
629
+ } catch (error) {
630
+ if (error instanceof ExpectedDecodeError && inputToken && isAddressEqual3(inputToken, zeroAddress2) && inputAmountGross > 0n) {
631
+ const swapEvent = decodeSwapFromLogs(destReceipt.logs);
632
+ const tokenOutAmountGross = swapEvent ? swapEvent.tokenOutAmount : yodlEvent.amount;
633
+ tokens = {
634
+ tokenIn: inputToken,
635
+ tokenInAmount: inputAmountGross,
636
+ tokenOut: yodlEvent.token,
637
+ tokenOutAmount: yodlEvent.amount,
638
+ tokenOutAmountGross
639
+ };
640
+ } else {
641
+ throw error;
642
+ }
643
+ }
604
644
  const bridge = {
605
645
  sourceChainId,
606
646
  sourceTxHash,
@@ -619,29 +659,26 @@ async function decodeRelayBridgePayment(hash, clients, options = {}) {
619
659
  }
620
660
 
621
661
  // src/bridges/across-bridge.ts
622
- async function resolveAcrossStatus(hash, fillReceipt) {
623
- try {
624
- return await fetchAcrossDepositByTx(hash);
625
- } catch (error) {
626
- if (!(error instanceof NoBridgeFoundError) || !fillReceipt) {
627
- throw error;
662
+ async function resolveAcrossStatus(hash, fillReceipt, cachedFillLog) {
663
+ const fillLog = cachedFillLog !== void 0 ? cachedFillLog : fillReceipt ? parseFillLogs(fillReceipt.logs) : null;
664
+ if (fillLog) {
665
+ const status = await fetchAcrossDepositByDepositId(
666
+ Number(fillLog.originChainId),
667
+ String(fillLog.depositId)
668
+ );
669
+ if (status.fillTxnRef?.toLowerCase() === hash.toLowerCase()) {
670
+ return status;
628
671
  }
672
+ return await fetchAcrossDepositByTx(hash);
629
673
  }
630
- const fillLog = parseFillLogs(fillReceipt.logs);
631
- if (!fillLog) {
632
- throw new NoBridgeFoundError();
633
- }
634
- const status = await fetchAcrossDepositByDepositId(
635
- Number(fillLog.originChainId),
636
- String(fillLog.depositId)
637
- );
638
- if (!status.fillTxnRef || status.fillTxnRef.toLowerCase() !== hash.toLowerCase()) {
639
- throw new NoBridgeFoundError();
640
- }
641
- return status;
674
+ return await fetchAcrossDepositByTx(hash);
642
675
  }
643
676
  async function decodeAcrossBridgePayment(hash, clients, options = {}) {
644
- const status = await resolveAcrossStatus(hash, options.fillReceipt);
677
+ const status = await resolveAcrossStatus(
678
+ hash,
679
+ options.fillReceipt,
680
+ options.parsedFillLog
681
+ );
645
682
  if (status.status !== "filled" || !status.fillTxnRef) {
646
683
  throw new NoBridgeFoundError();
647
684
  }
@@ -669,10 +706,10 @@ async function decodeAcrossBridgePayment(hash, clients, options = {}) {
669
706
  if (!yodlEvent) {
670
707
  throw new NoBridgeFoundError();
671
708
  }
672
- const [destBlock, destTx, sender] = await Promise.all([
709
+ const sender = extractSenderFromSource(sourceReceipt);
710
+ const [destBlock, destTx] = await Promise.all([
673
711
  destProvider.getBlock({ blockNumber: destReceipt.blockNumber }),
674
- destProvider.getTransaction({ hash: destinationTxHash }),
675
- extractSenderFromSource(sourceReceipt, sourceProvider, sourceTxHash)
712
+ destProvider.getTransaction({ hash: destinationTxHash })
676
713
  ]);
677
714
  const depositLog = parseDepositLogs(sourceReceipt.logs);
678
715
  const depositData = depositLog && depositLog.depositId === BigInt(status.depositId) ? {
@@ -733,6 +770,16 @@ async function decodeAcrossBridgePayment(hash, clients, options = {}) {
733
770
  // src/bridges/bridge-payment.ts
734
771
  async function decodeBridgePayment(hash, clients, options = {}) {
735
772
  const includeAcross = options.includeAcross ?? true;
773
+ const parsedFillLog = options.fillReceipt ? parseFillLogs2(options.fillReceipt.logs) : null;
774
+ const parsedDepositLog = options.sourceReceipt ? parseDepositLogs2(options.sourceReceipt.logs) : null;
775
+ const hasAcrossEvents = !!(parsedFillLog || parsedDepositLog);
776
+ if (includeAcross && hasAcrossEvents) {
777
+ return await decodeAcrossBridgePayment(hash, clients, {
778
+ fillReceipt: options.fillReceipt,
779
+ sourceReceipt: options.sourceReceipt,
780
+ parsedFillLog
781
+ });
782
+ }
736
783
  let relayError;
737
784
  try {
738
785
  return await decodeRelayBridgePayment(hash, clients, {
@@ -768,6 +815,7 @@ async function decodeBridgePayment(hash, clients, options = {}) {
768
815
 
769
816
  // src/core/payment-decoder.ts
770
817
  import { getRouter as getRouter3 } from "@yodlpay/tokenlists";
818
+ import { isAddressEqual as isAddressEqual4 } from "viem";
771
819
  async function tryDecodeBridge(hash, clients, options = {}) {
772
820
  try {
773
821
  return await decodeBridgePayment(hash, clients, options);
@@ -798,10 +846,17 @@ async function decodePayment(hash, chainId2, clients, cachedReceipt) {
798
846
  );
799
847
  return { type: "swap", ...paymentEvent2, ...swapEvent };
800
848
  }
801
- const bridgeResult2 = await tryDecodeBridge(hash, clients, {
802
- fillReceipt: receipt
803
- });
804
- if (bridgeResult2) return bridgeResult2;
849
+ const effectiveSender = extractSenderFromSource(receipt);
850
+ const isSameChainPayment = isAddressEqual4(
851
+ effectiveSender,
852
+ yodlEvent.sender
853
+ );
854
+ if (!isSameChainPayment) {
855
+ const bridgeResult2 = await tryDecodeBridge(hash, clients, {
856
+ fillReceipt: receipt
857
+ });
858
+ if (bridgeResult2) return bridgeResult2;
859
+ }
805
860
  const [block, tx] = await Promise.all([
806
861
  provider.getBlock({ blockNumber: receipt.blockNumber }),
807
862
  provider.getTransaction({ hash })
@@ -831,10 +886,16 @@ async function decodePayment(hash, chainId2, clients, cachedReceipt) {
831
886
  // src/core/yodl-payment.ts
832
887
  import {
833
888
  formatUnits,
834
- zeroAddress
889
+ isAddressEqual as isAddressEqual5,
890
+ zeroAddress as zeroAddress3
835
891
  } from "viem";
836
892
  async function buildTokenInfo(params, clients) {
837
- const [inToken, outToken] = await Promise.all([
893
+ const sameToken = isAddressEqual5(params.tokenIn, params.tokenOut) && params.inChainId === params.outChainId;
894
+ const [inToken, outToken] = sameToken ? await resolveToken(
895
+ params.tokenIn,
896
+ params.inChainId,
897
+ getClient(clients, params.inChainId)
898
+ ).then((t) => [t, t]) : await Promise.all([
838
899
  resolveToken(
839
900
  params.tokenIn,
840
901
  params.inChainId,
@@ -942,7 +1003,7 @@ async function decodeYodlPayment(txHash2, chainId2, clients, cachedReceipt) {
942
1003
  cachedReceipt
943
1004
  );
944
1005
  const firstWebhook = paymentInfo.webhooks[0];
945
- const processorAddress = firstWebhook?.webhookAddress ?? zeroAddress;
1006
+ const processorAddress = firstWebhook?.webhookAddress ?? zeroAddress3;
946
1007
  const processorMemo = firstWebhook?.memo ?? "";
947
1008
  const tokenInfo = await extractTokenInfo(
948
1009
  paymentInfo,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yodlpay/payment-decoder",
3
- "version": "1.3.5",
3
+ "version": "1.3.7",
4
4
  "description": "Decode Yodl payment hashes into structured payment data",
5
5
  "keywords": [
6
6
  "yodl",