@yodlpay/payment-decoder 1.3.6 → 1.3.8
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 +142 -70
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -30,8 +30,9 @@ import { tokenlist } from "@yodlpay/tokenlists";
|
|
|
30
30
|
import {
|
|
31
31
|
erc20Abi as erc20Abi2,
|
|
32
32
|
getAddress,
|
|
33
|
-
isAddressEqual,
|
|
34
|
-
parseEventLogs
|
|
33
|
+
isAddressEqual as isAddressEqual2,
|
|
34
|
+
parseEventLogs,
|
|
35
|
+
zeroAddress as zeroAddress2
|
|
35
36
|
} from "viem";
|
|
36
37
|
|
|
37
38
|
// src/abi.ts
|
|
@@ -41,6 +42,9 @@ var yodlAbi = getRouterAbi("0.8");
|
|
|
41
42
|
var relaySwapAbi = parseAbi([
|
|
42
43
|
"event Swap(address indexed sender, address indexed recipient, address inputToken, address outputToken, uint256 inputAmount, uint256 outputAmount)"
|
|
43
44
|
]);
|
|
45
|
+
var fundsMovementAbi = parseAbi([
|
|
46
|
+
"event FundsMovement(address from, address to, address currency, uint256 amount, bytes metadata)"
|
|
47
|
+
]);
|
|
44
48
|
|
|
45
49
|
// src/utils.ts
|
|
46
50
|
import { getTokenByAddress } from "@yodlpay/tokenlists";
|
|
@@ -48,9 +52,17 @@ import {
|
|
|
48
52
|
erc20Abi,
|
|
49
53
|
getContract,
|
|
50
54
|
hexToString,
|
|
51
|
-
|
|
55
|
+
isAddressEqual,
|
|
56
|
+
stringify,
|
|
57
|
+
zeroAddress
|
|
52
58
|
} from "viem";
|
|
53
59
|
import { z } from "zod";
|
|
60
|
+
|
|
61
|
+
// src/chains.ts
|
|
62
|
+
import { chains as allChains } from "@yodlpay/tokenlists";
|
|
63
|
+
var chains = allChains.filter((c) => !c.testnet);
|
|
64
|
+
|
|
65
|
+
// src/utils.ts
|
|
54
66
|
function decodeMemo(memo) {
|
|
55
67
|
if (!memo || memo === "0x") return "";
|
|
56
68
|
try {
|
|
@@ -62,6 +74,19 @@ function decodeMemo(memo) {
|
|
|
62
74
|
var erc20String = z.string().max(64).transform((s) => s.replace(/\p{Cc}/gu, ""));
|
|
63
75
|
var erc20Decimals = z.number().int().nonnegative().max(36);
|
|
64
76
|
async function resolveToken(address, chainId2, client) {
|
|
77
|
+
if (isAddressEqual(address, zeroAddress)) {
|
|
78
|
+
const chain = chains.find((c) => c.id === chainId2);
|
|
79
|
+
if (!chain) {
|
|
80
|
+
throw new Error(`Native token requested for unknown chainId ${chainId2}`);
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
chainId: chainId2,
|
|
84
|
+
address,
|
|
85
|
+
name: chain.nativeCurrency.name,
|
|
86
|
+
symbol: chain.nativeCurrency.symbol,
|
|
87
|
+
decimals: chain.nativeCurrency.decimals
|
|
88
|
+
};
|
|
89
|
+
}
|
|
65
90
|
try {
|
|
66
91
|
return getTokenByAddress(address, chainId2);
|
|
67
92
|
} catch {
|
|
@@ -85,7 +110,7 @@ async function resolveToken(address, chainId2, client) {
|
|
|
85
110
|
function decodeYodlFromLogs(logs, routerAddress) {
|
|
86
111
|
const parsed = parseEventLogs({
|
|
87
112
|
abi: yodlAbi,
|
|
88
|
-
logs: logs.filter((l) =>
|
|
113
|
+
logs: logs.filter((l) => isAddressEqual2(l.address, routerAddress)),
|
|
89
114
|
eventName: "Yodl",
|
|
90
115
|
strict: true
|
|
91
116
|
});
|
|
@@ -132,8 +157,22 @@ function extractTokenTransfers(logs) {
|
|
|
132
157
|
return [{ token: log.address, from, to, amount: value }];
|
|
133
158
|
});
|
|
134
159
|
}
|
|
160
|
+
function extractFundsMovements(logs) {
|
|
161
|
+
const parsed = parseEventLogs({
|
|
162
|
+
abi: fundsMovementAbi,
|
|
163
|
+
logs: [...logs],
|
|
164
|
+
eventName: "FundsMovement",
|
|
165
|
+
strict: false
|
|
166
|
+
});
|
|
167
|
+
return parsed.flatMap((log) => {
|
|
168
|
+
if (!log.args) return [];
|
|
169
|
+
const { from, to, currency, amount } = log.args;
|
|
170
|
+
if (!from || !to || !currency || amount === void 0) return [];
|
|
171
|
+
return [{ emitter: log.address, from, to, currency, amount }];
|
|
172
|
+
});
|
|
173
|
+
}
|
|
135
174
|
function isKnownToken(address) {
|
|
136
|
-
return tokenlist.some((t) =>
|
|
175
|
+
return tokenlist.some((t) => isAddressEqual2(t.address, address));
|
|
137
176
|
}
|
|
138
177
|
function transferSignature(t) {
|
|
139
178
|
return `${getAddress(t.from)}:${getAddress(t.to)}:${t.amount}`;
|
|
@@ -180,7 +219,7 @@ function detectMirrorTokens(allTransfers) {
|
|
|
180
219
|
return mirrors;
|
|
181
220
|
}
|
|
182
221
|
function findInputTransfer(transfers, sender) {
|
|
183
|
-
const fromSender = transfers.filter((t) =>
|
|
222
|
+
const fromSender = transfers.filter((t) => isAddressEqual2(t.from, sender));
|
|
184
223
|
if (fromSender.length === 0) return void 0;
|
|
185
224
|
const maxAmount = fromSender.reduce(
|
|
186
225
|
(max, t) => t.amount > max ? t.amount : max,
|
|
@@ -200,52 +239,68 @@ function findInputTransfer(transfers, sender) {
|
|
|
200
239
|
}
|
|
201
240
|
function inferSwapFromTransfers(logs, sender, yodlToken) {
|
|
202
241
|
const transfers = extractTokenTransfers(logs);
|
|
242
|
+
const tokenOutAmount = transfers.filter(
|
|
243
|
+
(t) => isAddressEqual2(t.to, sender) && isAddressEqual2(t.token, yodlToken)
|
|
244
|
+
).reduce((sum, t) => sum + t.amount, 0n);
|
|
245
|
+
if (tokenOutAmount === 0n) return void 0;
|
|
203
246
|
const outgoingDifferentToken = transfers.filter(
|
|
204
|
-
(t) =>
|
|
247
|
+
(t) => isAddressEqual2(t.from, sender) && !isAddressEqual2(t.token, yodlToken)
|
|
205
248
|
);
|
|
206
|
-
if (outgoingDifferentToken.length
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
249
|
+
if (outgoingDifferentToken.length > 0) {
|
|
250
|
+
const byToken = /* @__PURE__ */ new Map();
|
|
251
|
+
for (const t of outgoingDifferentToken) {
|
|
252
|
+
const key = getAddress(t.token);
|
|
253
|
+
const existing = byToken.get(key);
|
|
254
|
+
if (existing) {
|
|
255
|
+
existing.gross += t.amount;
|
|
256
|
+
} else {
|
|
257
|
+
byToken.set(key, { token: t.token, gross: t.amount, refund: 0n });
|
|
258
|
+
}
|
|
215
259
|
}
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
260
|
+
const incomingNonYodl = transfers.filter(
|
|
261
|
+
(t) => isAddressEqual2(t.to, sender) && !isAddressEqual2(t.token, yodlToken)
|
|
262
|
+
);
|
|
263
|
+
for (const t of incomingNonYodl) {
|
|
264
|
+
const key = getAddress(t.token);
|
|
265
|
+
const existing = byToken.get(key);
|
|
266
|
+
if (existing) {
|
|
267
|
+
existing.refund += t.amount;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
let tokenIn;
|
|
271
|
+
let tokenInAmount = 0n;
|
|
272
|
+
for (const { token, gross, refund } of byToken.values()) {
|
|
273
|
+
const net = gross - refund;
|
|
274
|
+
if (net > tokenInAmount) {
|
|
275
|
+
tokenIn = token;
|
|
276
|
+
tokenInAmount = net;
|
|
277
|
+
}
|
|
225
278
|
}
|
|
279
|
+
if (tokenIn && tokenInAmount > 0n) {
|
|
280
|
+
return {
|
|
281
|
+
tokenIn,
|
|
282
|
+
tokenOut: yodlToken,
|
|
283
|
+
tokenInAmount,
|
|
284
|
+
tokenOutAmount
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
return void 0;
|
|
226
288
|
}
|
|
227
|
-
let
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
289
|
+
let nativeInAmount = 0n;
|
|
290
|
+
for (const movement of extractFundsMovements(logs)) {
|
|
291
|
+
if (!isAddressEqual2(movement.currency, zeroAddress2)) continue;
|
|
292
|
+
if (isAddressEqual2(movement.from, sender)) {
|
|
293
|
+
nativeInAmount += movement.amount;
|
|
294
|
+
}
|
|
295
|
+
if (isAddressEqual2(movement.to, sender)) {
|
|
296
|
+
nativeInAmount -= movement.amount;
|
|
234
297
|
}
|
|
235
298
|
}
|
|
236
|
-
if (
|
|
237
|
-
const incomingYodlToken = transfers.filter(
|
|
238
|
-
(t) => isAddressEqual(t.to, sender) && isAddressEqual(t.token, yodlToken)
|
|
239
|
-
);
|
|
240
|
-
const tokenOutAmount = incomingYodlToken.reduce(
|
|
241
|
-
(sum, t) => sum + t.amount,
|
|
242
|
-
0n
|
|
243
|
-
);
|
|
244
|
-
if (tokenOutAmount === 0n) return void 0;
|
|
299
|
+
if (nativeInAmount <= 0n) return void 0;
|
|
245
300
|
return {
|
|
246
|
-
tokenIn,
|
|
301
|
+
tokenIn: zeroAddress2,
|
|
247
302
|
tokenOut: yodlToken,
|
|
248
|
-
tokenInAmount,
|
|
303
|
+
tokenInAmount: nativeInAmount,
|
|
249
304
|
tokenOutAmount
|
|
250
305
|
};
|
|
251
306
|
}
|
|
@@ -267,12 +322,6 @@ import { decodeFunctionData, toFunctionSelector } from "viem";
|
|
|
267
322
|
// src/validation.ts
|
|
268
323
|
import { isAddress, isHash, isHex } from "viem";
|
|
269
324
|
import { z as z2 } from "zod";
|
|
270
|
-
|
|
271
|
-
// src/chains.ts
|
|
272
|
-
import { chains as allChains } from "@yodlpay/tokenlists";
|
|
273
|
-
var chains = allChains.filter((c) => !c.testnet);
|
|
274
|
-
|
|
275
|
-
// src/validation.ts
|
|
276
325
|
var validChainIds = chains.map((c) => c.id);
|
|
277
326
|
var txHashSchema = z2.string().refine((val) => isHash(val), "Invalid transaction hash");
|
|
278
327
|
var chainIdSchema = z2.coerce.number().int().refine((id) => validChainIds.includes(id), {
|
|
@@ -408,8 +457,10 @@ async function fetchAcrossDepositByTx(hash) {
|
|
|
408
457
|
// src/bridges/relay-bridge.ts
|
|
409
458
|
import { getRouter } from "@yodlpay/tokenlists";
|
|
410
459
|
import {
|
|
411
|
-
|
|
412
|
-
|
|
460
|
+
isAddress as isAddress2,
|
|
461
|
+
isAddressEqual as isAddressEqual3,
|
|
462
|
+
parseEventLogs as parseEventLogs2,
|
|
463
|
+
zeroAddress as zeroAddress3
|
|
413
464
|
} from "viem";
|
|
414
465
|
import { entryPoint08Abi, entryPoint08Address } from "viem/account-abstraction";
|
|
415
466
|
|
|
@@ -502,7 +553,7 @@ function extractSenderFromSource(sourceReceipt) {
|
|
|
502
553
|
const userOps = parseEventLogs2({
|
|
503
554
|
abi: entryPoint08Abi,
|
|
504
555
|
logs: sourceReceipt.logs.filter(
|
|
505
|
-
(l) =>
|
|
556
|
+
(l) => isAddressEqual3(l.address, entryPoint08Address)
|
|
506
557
|
),
|
|
507
558
|
eventName: "UserOperationEvent"
|
|
508
559
|
});
|
|
@@ -552,12 +603,15 @@ function parseRelayResponse(request) {
|
|
|
552
603
|
if (!inTx?.hash || !inTx?.chainId || !outTx?.hash || !outTx?.chainId) {
|
|
553
604
|
throw new NoBridgeFoundError();
|
|
554
605
|
}
|
|
606
|
+
const rawInputToken = data?.metadata?.currencyIn?.currency?.address;
|
|
607
|
+
const inputToken = rawInputToken && isAddress2(rawInputToken) ? rawInputToken : void 0;
|
|
555
608
|
return {
|
|
556
609
|
sourceChainId: inTx.chainId,
|
|
557
610
|
sourceTxHash: inTx.hash,
|
|
558
611
|
destinationChainId: outTx.chainId,
|
|
559
612
|
destinationTxHash: outTx.hash,
|
|
560
|
-
inputAmountGross: BigInt(data?.metadata?.currencyIn?.amount ?? 0)
|
|
613
|
+
inputAmountGross: BigInt(data?.metadata?.currencyIn?.amount ?? 0),
|
|
614
|
+
inputToken
|
|
561
615
|
};
|
|
562
616
|
}
|
|
563
617
|
async function decodeRelayBridgePayment(hash, clients, options = {}) {
|
|
@@ -567,7 +621,8 @@ async function decodeRelayBridgePayment(hash, clients, options = {}) {
|
|
|
567
621
|
sourceTxHash,
|
|
568
622
|
destinationChainId,
|
|
569
623
|
destinationTxHash,
|
|
570
|
-
inputAmountGross
|
|
624
|
+
inputAmountGross,
|
|
625
|
+
inputToken
|
|
571
626
|
} = parseRelayResponse(request);
|
|
572
627
|
const destProvider = clients[destinationChainId];
|
|
573
628
|
const sourceProvider = clients[sourceChainId];
|
|
@@ -593,16 +648,33 @@ async function decodeRelayBridgePayment(hash, clients, options = {}) {
|
|
|
593
648
|
destProvider.getBlock({ blockNumber: destReceipt.blockNumber }),
|
|
594
649
|
destProvider.getTransaction({ hash: destinationTxHash })
|
|
595
650
|
]);
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
651
|
+
let tokens;
|
|
652
|
+
try {
|
|
653
|
+
tokens = await resolveBridgeTokens(
|
|
654
|
+
sourceReceipt,
|
|
655
|
+
destReceipt,
|
|
656
|
+
yodlEvent,
|
|
657
|
+
sender,
|
|
658
|
+
inputAmountGross,
|
|
659
|
+
sourceChainId,
|
|
660
|
+
destinationChainId,
|
|
661
|
+
clients
|
|
662
|
+
);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
if (error instanceof ExpectedDecodeError && inputToken && isAddressEqual3(inputToken, zeroAddress3) && inputAmountGross > 0n) {
|
|
665
|
+
const swapEvent = decodeSwapFromLogs(destReceipt.logs);
|
|
666
|
+
const tokenOutAmountGross = swapEvent ? swapEvent.tokenOutAmount : yodlEvent.amount;
|
|
667
|
+
tokens = {
|
|
668
|
+
tokenIn: inputToken,
|
|
669
|
+
tokenInAmount: inputAmountGross,
|
|
670
|
+
tokenOut: yodlEvent.token,
|
|
671
|
+
tokenOutAmount: yodlEvent.amount,
|
|
672
|
+
tokenOutAmountGross
|
|
673
|
+
};
|
|
674
|
+
} else {
|
|
675
|
+
throw error;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
606
678
|
const bridge = {
|
|
607
679
|
sourceChainId,
|
|
608
680
|
sourceTxHash,
|
|
@@ -777,7 +849,7 @@ async function decodeBridgePayment(hash, clients, options = {}) {
|
|
|
777
849
|
|
|
778
850
|
// src/core/payment-decoder.ts
|
|
779
851
|
import { getRouter as getRouter3 } from "@yodlpay/tokenlists";
|
|
780
|
-
import { isAddressEqual as
|
|
852
|
+
import { isAddressEqual as isAddressEqual4 } from "viem";
|
|
781
853
|
async function tryDecodeBridge(hash, clients, options = {}) {
|
|
782
854
|
try {
|
|
783
855
|
return await decodeBridgePayment(hash, clients, options);
|
|
@@ -809,7 +881,7 @@ async function decodePayment(hash, chainId2, clients, cachedReceipt) {
|
|
|
809
881
|
return { type: "swap", ...paymentEvent2, ...swapEvent };
|
|
810
882
|
}
|
|
811
883
|
const effectiveSender = extractSenderFromSource(receipt);
|
|
812
|
-
const isSameChainPayment =
|
|
884
|
+
const isSameChainPayment = isAddressEqual4(
|
|
813
885
|
effectiveSender,
|
|
814
886
|
yodlEvent.sender
|
|
815
887
|
);
|
|
@@ -848,11 +920,11 @@ async function decodePayment(hash, chainId2, clients, cachedReceipt) {
|
|
|
848
920
|
// src/core/yodl-payment.ts
|
|
849
921
|
import {
|
|
850
922
|
formatUnits,
|
|
851
|
-
isAddressEqual as
|
|
852
|
-
zeroAddress
|
|
923
|
+
isAddressEqual as isAddressEqual5,
|
|
924
|
+
zeroAddress as zeroAddress4
|
|
853
925
|
} from "viem";
|
|
854
926
|
async function buildTokenInfo(params, clients) {
|
|
855
|
-
const sameToken =
|
|
927
|
+
const sameToken = isAddressEqual5(params.tokenIn, params.tokenOut) && params.inChainId === params.outChainId;
|
|
856
928
|
const [inToken, outToken] = sameToken ? await resolveToken(
|
|
857
929
|
params.tokenIn,
|
|
858
930
|
params.inChainId,
|
|
@@ -965,7 +1037,7 @@ async function decodeYodlPayment(txHash2, chainId2, clients, cachedReceipt) {
|
|
|
965
1037
|
cachedReceipt
|
|
966
1038
|
);
|
|
967
1039
|
const firstWebhook = paymentInfo.webhooks[0];
|
|
968
|
-
const processorAddress = firstWebhook?.webhookAddress ??
|
|
1040
|
+
const processorAddress = firstWebhook?.webhookAddress ?? zeroAddress4;
|
|
969
1041
|
const processorMemo = firstWebhook?.memo ?? "";
|
|
970
1042
|
const tokenInfo = await extractTokenInfo(
|
|
971
1043
|
paymentInfo,
|