@yodlpay/payment-decoder 1.2.1 → 1.3.0
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 +24 -3
- package/dist/index.d.ts +13 -4
- package/dist/index.js +55 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -38,6 +38,23 @@ Decode a Yodl payment transaction into a structured `YodlPayment` object.
|
|
|
38
38
|
- `chainId` - Chain ID where the transaction was executed
|
|
39
39
|
- `clients` - Map of chainId → PublicClient (must include all chains needed for bridges)
|
|
40
40
|
|
|
41
|
+
### `detectChain(hash, clients)`
|
|
42
|
+
|
|
43
|
+
Auto-detect which chain a transaction belongs to by trying all configured chains in parallel. Useful when the chain ID is unknown.
|
|
44
|
+
|
|
45
|
+
- `hash` - Transaction hash to look up
|
|
46
|
+
- `clients` - Map of chainId → PublicClient
|
|
47
|
+
|
|
48
|
+
Returns `{ chainId, receipt }` — the receipt is included so it can be passed to `decodePayment` / `decodeYodlPayment` to avoid a duplicate RPC call.
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
import { detectChain, decodeYodlPayment, createClients } from "@yodlpay/payment-decoder";
|
|
52
|
+
|
|
53
|
+
const clients = createClients();
|
|
54
|
+
const { chainId, receipt } = await detectChain("0x1234...abcd", clients);
|
|
55
|
+
const payment = await decodeYodlPayment("0x1234...abcd", chainId, clients, receipt);
|
|
56
|
+
```
|
|
57
|
+
|
|
41
58
|
### `ChainClients`
|
|
42
59
|
|
|
43
60
|
Type alias for the clients map: `Record<number, PublicClient>`
|
|
@@ -47,18 +64,22 @@ Type alias for the clients map: `Record<number, PublicClient>`
|
|
|
47
64
|
Decode a transaction directly from the command line. Outputs JSON to stdout.
|
|
48
65
|
|
|
49
66
|
```bash
|
|
50
|
-
bun run decode <txHash>
|
|
67
|
+
bun run decode <txHash> [chainId]
|
|
51
68
|
```
|
|
52
69
|
|
|
53
70
|
**Arguments:**
|
|
54
71
|
|
|
55
72
|
- `txHash` - The transaction hash (64 hex characters with `0x` prefix)
|
|
56
|
-
- `chainId` - The chain ID where the transaction occurred (e.g., `8453` for Base, `42161` for Arbitrum)
|
|
73
|
+
- `chainId` *(optional)* - The chain ID where the transaction occurred (e.g., `8453` for Base, `42161` for Arbitrum). If omitted, the chain is auto-detected.
|
|
57
74
|
|
|
58
|
-
**
|
|
75
|
+
**Examples:**
|
|
59
76
|
|
|
60
77
|
```bash
|
|
78
|
+
# With explicit chain ID
|
|
61
79
|
bun run decode 0xe7ecad85dcfb6b4e25c833fa3617a45cf34df505cb698e04ad7d75a39032158f 42161
|
|
80
|
+
|
|
81
|
+
# Auto-detect chain
|
|
82
|
+
bun run decode 0xe7ecad85dcfb6b4e25c833fa3617a45cf34df505cb698e04ad7d75a39032158f
|
|
62
83
|
```
|
|
63
84
|
|
|
64
85
|
The CLI uses public RPC endpoints. For production use, configure your own RPC URLs via the library API.
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
|
-
import { PublicClient,
|
|
1
|
+
import { PublicClient, Hex, TransactionReceipt, Address, Log } from 'viem';
|
|
2
2
|
import { TokenInfo } from '@yodlpay/tokenlists';
|
|
3
3
|
|
|
4
4
|
type ChainClients = Record<number, PublicClient>;
|
|
5
5
|
declare function createClients(): ChainClients;
|
|
6
|
+
/**
|
|
7
|
+
* Auto-detect which chain a transaction belongs to by trying all configured
|
|
8
|
+
* chains in parallel. Returns both the chain ID and the receipt for reuse.
|
|
9
|
+
*/
|
|
10
|
+
declare function detectChain(hash: Hex, clients: ChainClients): Promise<{
|
|
11
|
+
chainId: number;
|
|
12
|
+
receipt: TransactionReceipt;
|
|
13
|
+
}>;
|
|
6
14
|
|
|
7
15
|
type ServiceProvider = "relay";
|
|
8
16
|
interface Webhook {
|
|
@@ -77,6 +85,7 @@ interface DecodedYodlEvent {
|
|
|
77
85
|
token: Address;
|
|
78
86
|
amount: bigint;
|
|
79
87
|
memo: string;
|
|
88
|
+
logIndex: number;
|
|
80
89
|
}
|
|
81
90
|
/**
|
|
82
91
|
* Decode Yodl payment event from transaction logs.
|
|
@@ -91,7 +100,7 @@ declare class NoYodlEventError extends Error {
|
|
|
91
100
|
constructor(message?: string);
|
|
92
101
|
}
|
|
93
102
|
|
|
94
|
-
declare function decodePayment(hash: Hex, chainId: number, clients: ChainClients): Promise<PaymentInfo>;
|
|
103
|
+
declare function decodePayment(hash: Hex, chainId: number, clients: ChainClients, cachedReceipt?: TransactionReceipt): Promise<PaymentInfo>;
|
|
95
104
|
|
|
96
105
|
/**
|
|
97
106
|
* Decode a bridge transaction given any hash (source or destination).
|
|
@@ -105,6 +114,6 @@ declare function decodeBridgePayment(hash: Hex, clients: ChainClients): Promise<
|
|
|
105
114
|
type: "bridge";
|
|
106
115
|
}>>;
|
|
107
116
|
|
|
108
|
-
declare function decodeYodlPayment(txHash: Hex, chainId: number, clients: ChainClients): Promise<YodlPayment>;
|
|
117
|
+
declare function decodeYodlPayment(txHash: Hex, chainId: number, clients: ChainClients, cachedReceipt?: TransactionReceipt): Promise<YodlPayment>;
|
|
109
118
|
|
|
110
|
-
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 };
|
|
119
|
+
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,12 +1,19 @@
|
|
|
1
1
|
// src/clients.ts
|
|
2
2
|
import { chains } from "@yodlpay/tokenlists";
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
createPublicClient,
|
|
5
|
+
http
|
|
6
|
+
} from "viem";
|
|
7
|
+
var rpcOverrides = {
|
|
8
|
+
137: "https://polygon-bor-rpc.publicnode.com"
|
|
9
|
+
// polygon-rpc.com is down
|
|
10
|
+
};
|
|
4
11
|
function createClients() {
|
|
5
12
|
const clients = {};
|
|
6
13
|
for (const chain of chains) {
|
|
7
14
|
clients[chain.id] = createPublicClient({
|
|
8
15
|
chain,
|
|
9
|
-
transport: http()
|
|
16
|
+
transport: http(rpcOverrides[chain.id])
|
|
10
17
|
});
|
|
11
18
|
}
|
|
12
19
|
return clients;
|
|
@@ -16,6 +23,25 @@ function getClient(clients, chainId) {
|
|
|
16
23
|
if (!client) throw new Error(`No client configured for chain ${chainId}`);
|
|
17
24
|
return client;
|
|
18
25
|
}
|
|
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`);
|
|
42
|
+
}
|
|
43
|
+
return found.value;
|
|
44
|
+
}
|
|
19
45
|
|
|
20
46
|
// src/decode-utils.ts
|
|
21
47
|
import {
|
|
@@ -146,14 +172,15 @@ function decodeYodlFromLogs(logs, routerAddress) {
|
|
|
146
172
|
address: routerAddress,
|
|
147
173
|
context: "Yodl event"
|
|
148
174
|
},
|
|
149
|
-
(decoded) => {
|
|
175
|
+
(decoded, log) => {
|
|
150
176
|
const args = decoded.args;
|
|
151
177
|
return {
|
|
152
178
|
sender: args.sender,
|
|
153
179
|
receiver: args.receiver,
|
|
154
180
|
token: args.token,
|
|
155
181
|
amount: args.amount,
|
|
156
|
-
memo: decodeMemo(args.memo)
|
|
182
|
+
memo: decodeMemo(args.memo),
|
|
183
|
+
logIndex: log.logIndex ?? 0
|
|
157
184
|
};
|
|
158
185
|
}
|
|
159
186
|
);
|
|
@@ -220,11 +247,13 @@ import { chains as chains2 } from "@yodlpay/tokenlists";
|
|
|
220
247
|
import { isAddress, isHex } from "viem";
|
|
221
248
|
import { z } from "zod";
|
|
222
249
|
var validChainIds = chains2.map((c) => c.id);
|
|
223
|
-
var
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
250
|
+
var txHashSchema = z.string().regex(/^0x[a-fA-F0-9]{64}$/, "Invalid transaction hash").transform((val) => val);
|
|
251
|
+
var chainIdSchema = z.coerce.number().int().refine((id) => validChainIds.includes(id), {
|
|
252
|
+
message: `Chain ID must be one of: ${validChainIds.join(", ")}`
|
|
253
|
+
});
|
|
254
|
+
var ArgsSchema = z.union([
|
|
255
|
+
z.tuple([txHashSchema, chainIdSchema]),
|
|
256
|
+
z.tuple([txHashSchema])
|
|
228
257
|
]);
|
|
229
258
|
var WebhooksSchema = z.array(
|
|
230
259
|
z.object({
|
|
@@ -447,16 +476,17 @@ async function tryDecodeBridge(hash, clients) {
|
|
|
447
476
|
return void 0;
|
|
448
477
|
}
|
|
449
478
|
}
|
|
450
|
-
async function decodePayment(hash, chainId, clients) {
|
|
479
|
+
async function decodePayment(hash, chainId, clients, cachedReceipt) {
|
|
451
480
|
const provider = getClient(clients, chainId);
|
|
452
|
-
const receipt = await provider.getTransactionReceipt({ hash });
|
|
481
|
+
const receipt = cachedReceipt ?? await provider.getTransactionReceipt({ hash });
|
|
453
482
|
const { address: routerAddress } = getRouter2(chainId);
|
|
454
483
|
const [block, tx] = await Promise.all([
|
|
455
484
|
provider.getBlock({ blockNumber: receipt.blockNumber }),
|
|
456
485
|
provider.getTransaction({ hash })
|
|
457
486
|
]);
|
|
458
487
|
const yodlEvent = decodeYodlFromLogs(receipt.logs, routerAddress);
|
|
459
|
-
const
|
|
488
|
+
const swapLogs = yodlEvent ? receipt.logs.filter((l) => (l.logIndex ?? 0) < yodlEvent.logIndex) : receipt.logs;
|
|
489
|
+
const swapEvent = decodeSwapFromLogs(swapLogs);
|
|
460
490
|
if (yodlEvent) {
|
|
461
491
|
const blockTimestamp = toBlockTimestamp(block);
|
|
462
492
|
const webhooks = extractEmbeddedParams(tx.input);
|
|
@@ -475,7 +505,10 @@ async function decodePayment(hash, chainId, clients) {
|
|
|
475
505
|
|
|
476
506
|
// src/yodl-payment.ts
|
|
477
507
|
import { getTokenByAddress as getTokenByAddress2 } from "@yodlpay/tokenlists";
|
|
478
|
-
import {
|
|
508
|
+
import {
|
|
509
|
+
formatUnits,
|
|
510
|
+
zeroAddress
|
|
511
|
+
} from "viem";
|
|
479
512
|
function buildTokenInfo(params) {
|
|
480
513
|
const inToken = getTokenByAddress2(params.tokenIn, params.inChainId);
|
|
481
514
|
const outToken = getTokenByAddress2(params.tokenOut, params.outChainId);
|
|
@@ -558,8 +591,13 @@ function extractTokenInfo(paymentInfo, chainId, txHash) {
|
|
|
558
591
|
}
|
|
559
592
|
}
|
|
560
593
|
}
|
|
561
|
-
async function decodeYodlPayment(txHash, chainId, clients) {
|
|
562
|
-
const paymentInfo = await decodePayment(
|
|
594
|
+
async function decodeYodlPayment(txHash, chainId, clients, cachedReceipt) {
|
|
595
|
+
const paymentInfo = await decodePayment(
|
|
596
|
+
txHash,
|
|
597
|
+
chainId,
|
|
598
|
+
clients,
|
|
599
|
+
cachedReceipt
|
|
600
|
+
);
|
|
563
601
|
const firstWebhook = paymentInfo.webhooks[0];
|
|
564
602
|
const processorAddress = firstWebhook?.webhookAddress ?? zeroAddress;
|
|
565
603
|
const processorMemo = firstWebhook?.memo ?? "";
|
|
@@ -582,5 +620,6 @@ export {
|
|
|
582
620
|
decodeBridgePayment,
|
|
583
621
|
decodePayment,
|
|
584
622
|
decodeYodlFromLogs,
|
|
585
|
-
decodeYodlPayment
|
|
623
|
+
decodeYodlPayment,
|
|
624
|
+
detectChain
|
|
586
625
|
};
|