@wopr-network/platform-core 1.61.0 → 1.61.2

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.
@@ -10,6 +10,7 @@
10
10
  * - p2pkh: DOGE (D...), TRON (T...) — params: { version }
11
11
  * - evm: ETH, ERC-20 (0x...) — params: {}
12
12
  */
13
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
13
14
  import { ripemd160 } from "@noble/hashes/legacy.js";
14
15
  import { sha256 } from "@noble/hashes/sha2.js";
15
16
  import { bech32 } from "@scure/base";
@@ -53,7 +54,13 @@ function encodeP2pkh(pubkey, versionByte) {
53
54
  return base58encode(full);
54
55
  }
55
56
  function encodeEvm(pubkey) {
56
- const hexPubKey = `0x${Array.from(pubkey, (b) => b.toString(16).padStart(2, "0")).join("")}`;
57
+ // HDKey.publicKey is SEC1 compressed (33 bytes, 02/03 prefix).
58
+ // Ethereum addresses = keccak256(uncompressed_pubkey[1:]).slice(-20).
59
+ // viem's publicKeyToAddress expects uncompressed (65 bytes, 04 prefix).
60
+ // Decompress via secp256k1 point recovery before hashing.
61
+ const hexKey = Array.from(pubkey, (b) => b.toString(16).padStart(2, "0")).join("");
62
+ const uncompressed = secp256k1.Point.fromHex(hexKey).toBytes(false);
63
+ const hexPubKey = `0x${Array.from(uncompressed, (b) => b.toString(16).padStart(2, "0")).join("")}`;
57
64
  return publicKeyToAddress(hexPubKey);
58
65
  }
59
66
  // ---------- public API ----------
@@ -284,8 +284,15 @@ export async function startWatchers(opts) {
284
284
  if (!method.rpcUrl)
285
285
  continue;
286
286
  const rpcCall = createRpcCaller(method.rpcUrl);
287
- const latestHex = (await rpcCall("eth_blockNumber", []));
288
- const latestBlock = Number.parseInt(latestHex, 16);
287
+ let latestBlock;
288
+ try {
289
+ const latestHex = (await rpcCall("eth_blockNumber", []));
290
+ latestBlock = Number.parseInt(latestHex, 16);
291
+ }
292
+ catch (err) {
293
+ log("Skipping ETH watcher — RPC unreachable", { chain: method.chain, token: method.token, error: String(err) });
294
+ continue;
295
+ }
289
296
  const backfillStart = Math.max(0, latestBlock - BACKFILL_BLOCKS);
290
297
  const activeAddresses = await chargeStore.listActiveDepositAddresses();
291
298
  // Only watch addresses for native charges on this chain (not ERC20 charges)
@@ -346,8 +353,15 @@ export async function startWatchers(opts) {
346
353
  if (!method.rpcUrl || !method.contractAddress)
347
354
  continue;
348
355
  const rpcCall = createRpcCaller(method.rpcUrl);
349
- const latestHex = (await rpcCall("eth_blockNumber", []));
350
- const latestBlock = Number.parseInt(latestHex, 16);
356
+ let latestBlock;
357
+ try {
358
+ const latestHex = (await rpcCall("eth_blockNumber", []));
359
+ latestBlock = Number.parseInt(latestHex, 16);
360
+ }
361
+ catch (err) {
362
+ log("Skipping EVM watcher — RPC unreachable", { chain: method.chain, token: method.token, error: String(err) });
363
+ continue;
364
+ }
351
365
  const activeAddresses = await chargeStore.listActiveDepositAddresses();
352
366
  const chainAddresses = activeAddresses.filter((a) => a.chain === method.chain).map((a) => a.address);
353
367
  const watcher = new EvmWatcher({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wopr-network/platform-core",
3
- "version": "1.61.0",
3
+ "version": "1.61.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -131,6 +131,7 @@
131
131
  "dependencies": {
132
132
  "@aws-sdk/client-ses": "^3.1014.0",
133
133
  "@hono/node-server": "^1.19.11",
134
+ "@noble/curves": "^2.0.1",
134
135
  "@noble/hashes": "^2.0.1",
135
136
  "@scure/base": "^2.0.0",
136
137
  "@scure/bip32": "^2.0.1",
@@ -10,6 +10,7 @@
10
10
  * - p2pkh: DOGE (D...), TRON (T...) — params: { version }
11
11
  * - evm: ETH, ERC-20 (0x...) — params: {}
12
12
  */
13
+ import { secp256k1 } from "@noble/curves/secp256k1.js";
13
14
  import { ripemd160 } from "@noble/hashes/legacy.js";
14
15
  import { sha256 } from "@noble/hashes/sha2.js";
15
16
  import { bech32 } from "@scure/base";
@@ -64,7 +65,14 @@ function encodeP2pkh(pubkey: Uint8Array, versionByte: number): string {
64
65
  }
65
66
 
66
67
  function encodeEvm(pubkey: Uint8Array): string {
67
- const hexPubKey = `0x${Array.from(pubkey, (b) => b.toString(16).padStart(2, "0")).join("")}` as `0x${string}`;
68
+ // HDKey.publicKey is SEC1 compressed (33 bytes, 02/03 prefix).
69
+ // Ethereum addresses = keccak256(uncompressed_pubkey[1:]).slice(-20).
70
+ // viem's publicKeyToAddress expects uncompressed (65 bytes, 04 prefix).
71
+ // Decompress via secp256k1 point recovery before hashing.
72
+ const hexKey = Array.from(pubkey, (b) => b.toString(16).padStart(2, "0")).join("");
73
+ const uncompressed = secp256k1.Point.fromHex(hexKey).toBytes(false);
74
+ const hexPubKey =
75
+ `0x${Array.from(uncompressed, (b: number) => b.toString(16).padStart(2, "0")).join("")}` as `0x${string}`;
68
76
  return publicKeyToAddress(hexPubKey);
69
77
  }
70
78
 
@@ -373,8 +373,14 @@ export async function startWatchers(opts: WatcherServiceOpts): Promise<() => voi
373
373
  if (!method.rpcUrl) continue;
374
374
 
375
375
  const rpcCall = createRpcCaller(method.rpcUrl);
376
- const latestHex = (await rpcCall("eth_blockNumber", [])) as string;
377
- const latestBlock = Number.parseInt(latestHex, 16);
376
+ let latestBlock: number;
377
+ try {
378
+ const latestHex = (await rpcCall("eth_blockNumber", [])) as string;
379
+ latestBlock = Number.parseInt(latestHex, 16);
380
+ } catch (err) {
381
+ log("Skipping ETH watcher — RPC unreachable", { chain: method.chain, token: method.token, error: String(err) });
382
+ continue;
383
+ }
378
384
  const backfillStart = Math.max(0, latestBlock - BACKFILL_BLOCKS);
379
385
 
380
386
  const activeAddresses = await chargeStore.listActiveDepositAddresses();
@@ -446,8 +452,14 @@ export async function startWatchers(opts: WatcherServiceOpts): Promise<() => voi
446
452
  if (!method.rpcUrl || !method.contractAddress) continue;
447
453
 
448
454
  const rpcCall = createRpcCaller(method.rpcUrl);
449
- const latestHex = (await rpcCall("eth_blockNumber", [])) as string;
450
- const latestBlock = Number.parseInt(latestHex, 16);
455
+ let latestBlock: number;
456
+ try {
457
+ const latestHex = (await rpcCall("eth_blockNumber", [])) as string;
458
+ latestBlock = Number.parseInt(latestHex, 16);
459
+ } catch (err) {
460
+ log("Skipping EVM watcher — RPC unreachable", { chain: method.chain, token: method.token, error: String(err) });
461
+ continue;
462
+ }
451
463
 
452
464
  const activeAddresses = await chargeStore.listActiveDepositAddresses();
453
465
  const chainAddresses = activeAddresses.filter((a) => a.chain === method.chain).map((a) => a.address);