@wopr-network/platform-core 1.21.0 → 1.22.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.
Files changed (29) hide show
  1. package/dist/billing/crypto/btc/settler.js +1 -1
  2. package/dist/billing/crypto/btc/watcher.d.ts +5 -3
  3. package/dist/billing/crypto/btc/watcher.js +9 -8
  4. package/dist/billing/crypto/evm/__tests__/eth-checkout.test.d.ts +1 -0
  5. package/dist/billing/crypto/evm/__tests__/eth-checkout.test.js +49 -0
  6. package/dist/billing/crypto/evm/__tests__/eth-settler.test.d.ts +1 -0
  7. package/dist/billing/crypto/evm/__tests__/eth-settler.test.js +80 -0
  8. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.d.ts +1 -0
  9. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.js +134 -0
  10. package/dist/billing/crypto/evm/eth-checkout.d.ts +34 -0
  11. package/dist/billing/crypto/evm/eth-checkout.js +53 -0
  12. package/dist/billing/crypto/evm/eth-settler.d.ts +23 -0
  13. package/dist/billing/crypto/evm/eth-settler.js +52 -0
  14. package/dist/billing/crypto/evm/eth-watcher.d.ts +53 -0
  15. package/dist/billing/crypto/evm/eth-watcher.js +83 -0
  16. package/dist/billing/crypto/evm/index.d.ts +6 -0
  17. package/dist/billing/crypto/evm/index.js +3 -0
  18. package/dist/billing/crypto/evm/settler.js +1 -1
  19. package/package.json +1 -1
  20. package/src/billing/crypto/btc/settler.ts +1 -1
  21. package/src/billing/crypto/btc/watcher.ts +12 -11
  22. package/src/billing/crypto/evm/__tests__/eth-checkout.test.ts +60 -0
  23. package/src/billing/crypto/evm/__tests__/eth-settler.test.ts +98 -0
  24. package/src/billing/crypto/evm/__tests__/eth-watcher.test.ts +157 -0
  25. package/src/billing/crypto/evm/eth-checkout.ts +84 -0
  26. package/src/billing/crypto/evm/eth-settler.ts +71 -0
  27. package/src/billing/crypto/evm/eth-watcher.ts +129 -0
  28. package/src/billing/crypto/evm/index.ts +6 -0
  29. package/src/billing/crypto/evm/settler.ts +1 -1
@@ -0,0 +1,129 @@
1
+ import { nativeToCents } from "../oracle/convert.js";
2
+ import type { IPriceOracle } from "../oracle/types.js";
3
+ import { getChainConfig } from "./config.js";
4
+ import type { EvmChain } from "./types.js";
5
+
6
+ type RpcCall = (method: string, params: unknown[]) => Promise<unknown>;
7
+
8
+ /** Event emitted when a native ETH deposit is detected and confirmed. */
9
+ export interface EthPaymentEvent {
10
+ readonly chain: EvmChain;
11
+ readonly from: string;
12
+ readonly to: string;
13
+ /** Raw value in wei (BigInt as string for serialization). */
14
+ readonly valueWei: string;
15
+ /** USD cents equivalent at detection time (integer). */
16
+ readonly amountUsdCents: number;
17
+ readonly txHash: string;
18
+ readonly blockNumber: number;
19
+ }
20
+
21
+ export interface EthWatcherOpts {
22
+ chain: EvmChain;
23
+ rpcCall: RpcCall;
24
+ oracle: IPriceOracle;
25
+ fromBlock: number;
26
+ onPayment: (event: EthPaymentEvent) => void | Promise<void>;
27
+ watchedAddresses?: string[];
28
+ }
29
+
30
+ interface RpcTransaction {
31
+ hash: string;
32
+ from: string;
33
+ to: string | null;
34
+ value: string;
35
+ blockNumber: string;
36
+ }
37
+
38
+ /**
39
+ * Native ETH transfer watcher.
40
+ *
41
+ * Unlike the ERC-20 EvmWatcher which uses eth_getLogs for Transfer events,
42
+ * this scans blocks for transactions where `to` matches a watched deposit
43
+ * address and `value > 0`.
44
+ *
45
+ * Uses the price oracle to convert wei → USD cents at detection time.
46
+ */
47
+ export class EthWatcher {
48
+ private _cursor: number;
49
+ private readonly chain: EvmChain;
50
+ private readonly rpc: RpcCall;
51
+ private readonly oracle: IPriceOracle;
52
+ private readonly onPayment: EthWatcherOpts["onPayment"];
53
+ private readonly confirmations: number;
54
+ private _watchedAddresses: Set<string>;
55
+ private readonly processedTxids = new Set<string>();
56
+
57
+ constructor(opts: EthWatcherOpts) {
58
+ this.chain = opts.chain;
59
+ this.rpc = opts.rpcCall;
60
+ this.oracle = opts.oracle;
61
+ this._cursor = opts.fromBlock;
62
+ this.onPayment = opts.onPayment;
63
+ this.confirmations = getChainConfig(opts.chain).confirmations;
64
+ this._watchedAddresses = new Set((opts.watchedAddresses ?? []).map((a) => a.toLowerCase()));
65
+ }
66
+
67
+ setWatchedAddresses(addresses: string[]): void {
68
+ this._watchedAddresses = new Set(addresses.map((a) => a.toLowerCase()));
69
+ }
70
+
71
+ get cursor(): number {
72
+ return this._cursor;
73
+ }
74
+
75
+ /**
76
+ * Poll for new native ETH transfers to watched addresses.
77
+ *
78
+ * Scans each confirmed block's transactions. Only processes txs
79
+ * where `to` is in the watched set and `value > 0`.
80
+ */
81
+ async poll(): Promise<void> {
82
+ if (this._watchedAddresses.size === 0) return;
83
+
84
+ const latestHex = (await this.rpc("eth_blockNumber", [])) as string;
85
+ const latest = Number.parseInt(latestHex, 16);
86
+ const confirmed = latest - this.confirmations;
87
+
88
+ if (confirmed < this._cursor) return;
89
+
90
+ const { priceCents } = await this.oracle.getPrice("ETH");
91
+
92
+ for (let blockNum = this._cursor; blockNum <= confirmed; blockNum++) {
93
+ const block = (await this.rpc("eth_getBlockByNumber", [`0x${blockNum.toString(16)}`, true])) as {
94
+ transactions: RpcTransaction[];
95
+ } | null;
96
+
97
+ if (!block) continue;
98
+
99
+ for (const tx of block.transactions) {
100
+ if (!tx.to) continue;
101
+ const to = tx.to.toLowerCase();
102
+ if (!this._watchedAddresses.has(to)) continue;
103
+
104
+ const valueWei = BigInt(tx.value);
105
+ if (valueWei === 0n) continue;
106
+
107
+ if (this.processedTxids.has(tx.hash)) continue;
108
+
109
+ const amountUsdCents = nativeToCents(valueWei, priceCents, 18);
110
+
111
+ const event: EthPaymentEvent = {
112
+ chain: this.chain,
113
+ from: tx.from.toLowerCase(),
114
+ to,
115
+ valueWei: valueWei.toString(),
116
+ amountUsdCents,
117
+ txHash: tx.hash,
118
+ blockNumber: blockNum,
119
+ };
120
+
121
+ await this.onPayment(event);
122
+ // Add to processed AFTER successful onPayment to avoid skipping on failure
123
+ this.processedTxids.add(tx.hash);
124
+ }
125
+ }
126
+
127
+ this._cursor = confirmed + 1;
128
+ }
129
+ }
@@ -2,6 +2,12 @@ export { deriveDepositAddress, isValidXpub } from "./address-gen.js";
2
2
  export type { StablecoinCheckoutDeps, StablecoinCheckoutResult } from "./checkout.js";
3
3
  export { createStablecoinCheckout, MIN_STABLECOIN_USD } from "./checkout.js";
4
4
  export { centsFromTokenAmount, getChainConfig, getTokenConfig, tokenAmountFromCents } from "./config.js";
5
+ export type { EthCheckoutDeps, EthCheckoutOpts, EthCheckoutResult } from "./eth-checkout.js";
6
+ export { createEthCheckout, MIN_ETH_USD } from "./eth-checkout.js";
7
+ export type { EthSettlerDeps } from "./eth-settler.js";
8
+ export { settleEthPayment } from "./eth-settler.js";
9
+ export type { EthPaymentEvent, EthWatcherOpts } from "./eth-watcher.js";
10
+ export { EthWatcher } from "./eth-watcher.js";
5
11
  export type { EvmSettlerDeps } from "./settler.js";
6
12
  export { settleEvmPayment } from "./settler.js";
7
13
  export type {
@@ -28,7 +28,7 @@ export async function settleEvmPayment(deps: EvmSettlerDeps, event: EvmPaymentEv
28
28
 
29
29
  const charge = await chargeStore.getByDepositAddress(event.to.toLowerCase());
30
30
  if (!charge) {
31
- return { handled: false, status: "Settled" };
31
+ return { handled: false, status: "Invalid" };
32
32
  }
33
33
 
34
34
  // Update charge status to Settled.