forked from karagozemin/OverSync
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparseHtlcReceipt.ts
More file actions
125 lines (115 loc) · 3.78 KB
/
Copy pathparseHtlcReceipt.ts
File metadata and controls
125 lines (115 loc) · 3.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import { decodeEventLog, type Hex } from "viem";
/**
* ABI fragments for the two HTLC versions OverSync currently supports.
*
* v1 `MainnetHTLC.OrderCreated` keys the order by a bytes32 hash, while
* v2 `HTLCEscrow.OrderCreated` uses a monotonic uint256 id. We try both
* decodes so a single helper works regardless of which contract the
* relayer happens to deploy ETH into.
*/
const V1_HTLC_ABI = [
{
type: "event",
name: "OrderCreated",
inputs: [
{ name: "orderId", type: "bytes32", indexed: true },
{ name: "sender", type: "address", indexed: true },
{ name: "beneficiary", type: "address", indexed: true },
{ name: "token", type: "address", indexed: false },
{ name: "amount", type: "uint256", indexed: false },
{ name: "hashLock", type: "bytes32", indexed: false },
{ name: "timelock", type: "uint256", indexed: false },
],
},
] as const;
const V2_HTLC_ABI = [
{
type: "event",
name: "OrderCreated",
inputs: [
{ name: "orderId", type: "uint256", indexed: true },
{ name: "sender", type: "address", indexed: true },
{ name: "beneficiary", type: "address", indexed: true },
{ name: "token", type: "address", indexed: false },
{ name: "amount", type: "uint256", indexed: false },
{ name: "safetyDeposit", type: "uint256", indexed: false },
{ name: "hashlock", type: "bytes32", indexed: false },
{ name: "timelock", type: "uint64", indexed: false },
],
},
] as const;
export interface ParsedHtlcOrder {
contractMode: "v1-mainnet-htlc" | "v2-escrow";
contractAddress: string;
/** Decimal string for v2 (uint256) or 0x-prefixed bytes32 hex for v1. */
orderId: string;
amountWei: string;
timelockUnixSeconds: number;
}
interface RawLog {
address: string;
topics: string[];
data: string;
}
function toLogInput(log: RawLog) {
return {
address: log.address as `0x${string}`,
topics: log.topics as [Hex, ...Hex[]],
data: log.data as Hex,
};
}
/**
* Scan an Ethereum tx receipt's logs for an HTLC `OrderCreated` event and
* return the metadata needed for a permissionless refund.
*
* Returns `null` if no recognised HTLC event is present (e.g. the user
* sent a plain transfer, or the relayer used an unrelated contract).
*/
export function parseHtlcReceipt(logs: RawLog[] | undefined | null): ParsedHtlcOrder | null {
if (!logs || logs.length === 0) return null;
for (const raw of logs) {
if (!raw?.topics?.length || !raw.data) continue;
const input = toLogInput(raw);
// v2 first — it's the active deployment for testnet today and the
// forward-looking format for mainnet at v2 launch.
try {
const decoded = decodeEventLog({ abi: V2_HTLC_ABI, ...input });
if (decoded.eventName === "OrderCreated") {
const args = decoded.args as {
orderId: bigint;
amount: bigint;
timelock: bigint;
};
return {
contractMode: "v2-escrow",
contractAddress: raw.address,
orderId: args.orderId.toString(),
amountWei: args.amount.toString(),
timelockUnixSeconds: Number(args.timelock),
};
}
} catch {
// fall through to v1
}
try {
const decoded = decodeEventLog({ abi: V1_HTLC_ABI, ...input });
if (decoded.eventName === "OrderCreated") {
const args = decoded.args as {
orderId: `0x${string}`;
amount: bigint;
timelock: bigint;
};
return {
contractMode: "v1-mainnet-htlc",
contractAddress: raw.address,
orderId: args.orderId,
amountWei: args.amount.toString(),
timelockUnixSeconds: Number(args.timelock),
};
}
} catch {
// not an HTLC OrderCreated log; keep scanning
}
}
return null;
}