@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.
- package/dist/index.js +125 -64
- 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
|
-
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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) =>
|
|
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
|
-
|
|
409
|
-
|
|
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
|
-
|
|
518
|
+
function extractSenderFromSource(sourceReceipt) {
|
|
499
519
|
const userOps = parseEventLogs2({
|
|
500
520
|
abi: entryPoint08Abi,
|
|
501
521
|
logs: sourceReceipt.logs.filter(
|
|
502
|
-
(l) =>
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
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
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
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
|
-
|
|
889
|
+
isAddressEqual as isAddressEqual5,
|
|
890
|
+
zeroAddress as zeroAddress3
|
|
835
891
|
} from "viem";
|
|
836
892
|
async function buildTokenInfo(params, clients) {
|
|
837
|
-
const
|
|
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 ??
|
|
1006
|
+
const processorAddress = firstWebhook?.webhookAddress ?? zeroAddress3;
|
|
946
1007
|
const processorMemo = firstWebhook?.memo ?? "";
|
|
947
1008
|
const tokenInfo = await extractTokenInfo(
|
|
948
1009
|
paymentInfo,
|