@wopr-network/platform-core 1.63.1 → 1.64.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 (32) hide show
  1. package/dist/billing/crypto/__tests__/address-gen.test.js +191 -90
  2. package/dist/billing/crypto/__tests__/key-server.test.js +3 -0
  3. package/dist/billing/crypto/address-gen.js +32 -0
  4. package/dist/billing/crypto/evm/eth-watcher.js +52 -41
  5. package/dist/billing/crypto/evm/watcher.js +5 -11
  6. package/dist/billing/crypto/key-server-entry.js +8 -1
  7. package/dist/billing/crypto/key-server.js +19 -14
  8. package/dist/billing/crypto/oracle/coingecko.js +3 -0
  9. package/dist/billing/crypto/payment-method-store.d.ts +2 -0
  10. package/dist/billing/crypto/payment-method-store.js +5 -0
  11. package/dist/billing/crypto/tron/address-convert.js +15 -5
  12. package/dist/billing/crypto/watcher-service.js +9 -9
  13. package/dist/db/schema/crypto.d.ts +34 -0
  14. package/dist/db/schema/crypto.js +3 -1
  15. package/docs/superpowers/plans/2026-03-24-crypto-plugin-phase1.md +697 -0
  16. package/docs/superpowers/specs/2026-03-24-crypto-plugin-architecture-design.md +309 -0
  17. package/drizzle/migrations/0022_oracle_asset_id_column.sql +23 -0
  18. package/drizzle/migrations/0022_rpc_headers_column.sql +1 -0
  19. package/drizzle/migrations/meta/_journal.json +14 -0
  20. package/package.json +1 -1
  21. package/src/billing/crypto/__tests__/address-gen.test.ts +207 -88
  22. package/src/billing/crypto/__tests__/key-server.test.ts +3 -0
  23. package/src/billing/crypto/address-gen.ts +31 -0
  24. package/src/billing/crypto/evm/eth-watcher.ts +64 -47
  25. package/src/billing/crypto/evm/watcher.ts +8 -9
  26. package/src/billing/crypto/key-server-entry.ts +7 -1
  27. package/src/billing/crypto/key-server.ts +26 -19
  28. package/src/billing/crypto/oracle/coingecko.ts +3 -0
  29. package/src/billing/crypto/payment-method-store.ts +7 -0
  30. package/src/billing/crypto/tron/address-convert.ts +13 -4
  31. package/src/billing/crypto/watcher-service.ts +12 -11
  32. package/src/db/schema/crypto.ts +3 -1
@@ -44,11 +44,13 @@ export class DrizzlePaymentMethodStore {
44
44
  displayOrder: method.displayOrder,
45
45
  iconUrl: method.iconUrl,
46
46
  rpcUrl: method.rpcUrl,
47
+ rpcHeaders: method.rpcHeaders ?? "{}",
47
48
  oracleAddress: method.oracleAddress,
48
49
  xpub: method.xpub,
49
50
  addressType: method.addressType,
50
51
  encodingParams: method.encodingParams,
51
52
  watcherType: method.watcherType,
53
+ oracleAssetId: method.oracleAssetId,
52
54
  confirmations: method.confirmations,
53
55
  })
54
56
  .onConflictDoUpdate({
@@ -69,6 +71,7 @@ export class DrizzlePaymentMethodStore {
69
71
  addressType: method.addressType,
70
72
  encodingParams: method.encodingParams,
71
73
  watcherType: method.watcherType,
74
+ oracleAssetId: method.oracleAssetId,
72
75
  confirmations: method.confirmations,
73
76
  },
74
77
  });
@@ -103,11 +106,13 @@ function toRecord(row) {
103
106
  displayOrder: row.displayOrder,
104
107
  iconUrl: row.iconUrl,
105
108
  rpcUrl: row.rpcUrl,
109
+ rpcHeaders: row.rpcHeaders ?? "{}",
106
110
  oracleAddress: row.oracleAddress,
107
111
  xpub: row.xpub,
108
112
  addressType: row.addressType,
109
113
  encodingParams: row.encodingParams,
110
114
  watcherType: row.watcherType,
115
+ oracleAssetId: row.oracleAssetId,
111
116
  confirmations: row.confirmations,
112
117
  };
113
118
  }
@@ -8,6 +8,13 @@
8
8
  import { sha256 } from "@noble/hashes/sha2.js";
9
9
  const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
10
10
  function base58decode(s) {
11
+ // Count leading '1' characters (each represents a 0x00 byte)
12
+ let leadingZeros = 0;
13
+ for (const ch of s) {
14
+ if (ch !== "1")
15
+ break;
16
+ leadingZeros++;
17
+ }
11
18
  let num = 0n;
12
19
  for (const ch of s) {
13
20
  const idx = BASE58_ALPHABET.indexOf(ch);
@@ -15,11 +22,14 @@ function base58decode(s) {
15
22
  throw new Error(`Invalid base58 character: ${ch}`);
16
23
  num = num * 58n + BigInt(idx);
17
24
  }
18
- const hex = num.toString(16).padStart(50, "0"); // 25 bytes = 50 hex chars
19
- const bytes = new Uint8Array(25);
20
- for (let i = 0; i < 25; i++)
21
- bytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
22
- return bytes;
25
+ const hex = num.toString(16).padStart(2, "0");
26
+ const dataBytes = new Uint8Array(hex.length / 2);
27
+ for (let i = 0; i < dataBytes.length; i++)
28
+ dataBytes[i] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
29
+ // Prepend leading zero bytes
30
+ const result = new Uint8Array(leadingZeros + dataBytes.length);
31
+ result.set(dataBytes, leadingZeros);
32
+ return result;
23
33
  }
24
34
  /**
25
35
  * Convert a Tron T... address to 0x hex (20 bytes, no 0x41 prefix).
@@ -274,16 +274,16 @@ export async function startWatchers(opts) {
274
274
  const nativeEvmMethods = evmMethods.filter((m) => m.type === "native");
275
275
  const erc20Methods = evmMethods.filter((m) => m.type === "erc20" && m.contractAddress);
276
276
  const BACKFILL_BLOCKS = 1000; // Scan ~30min of blocks on first deploy to catch missed deposits
277
- // Address conversion helpers for chains with non-EVM address formats (e.g. Tron T...).
278
- // The EVM watcher uses 0x hex addresses; the DB stores native format (T... for Tron).
279
- // Determined by addressType from the DBnot by inspecting addresses at runtime.
280
- const needsAddrConvert = (method) => method.addressType === "p2pkh";
281
- const toWatcherAddr = (addr, method) => needsAddrConvert(method) && isTronAddress(addr) ? tronToHex(addr) : addr;
282
- const fromWatcherAddr = (addr, method) => needsAddrConvert(method) ? hexToTron(addr) : addr;
277
+ // Address conversion for EVM-watched chains with non-0x address formats (Tron T...).
278
+ // Only applies to chains routed through the EVM watcher but storing non-hex addresses.
279
+ // UTXO chains (DOGE p2pkh) never enter this path they use the UTXO watcher.
280
+ const isTronMethod = (method) => (method.addressType === "p2pkh" || method.addressType === "keccak-b58check") && method.chain === "tron";
281
+ const toWatcherAddr = (addr, method) => isTronMethod(method) && isTronAddress(addr) ? tronToHex(addr) : addr;
282
+ const fromWatcherAddr = (addr, method) => isTronMethod(method) ? hexToTron(addr) : addr;
283
283
  for (const method of nativeEvmMethods) {
284
284
  if (!method.rpcUrl)
285
285
  continue;
286
- const rpcCall = createRpcCaller(method.rpcUrl);
286
+ const rpcCall = createRpcCaller(method.rpcUrl, JSON.parse(method.rpcHeaders ?? "{}"));
287
287
  let latestBlock;
288
288
  try {
289
289
  const latestHex = (await rpcCall("eth_blockNumber", []));
@@ -352,7 +352,7 @@ export async function startWatchers(opts) {
352
352
  for (const method of erc20Methods) {
353
353
  if (!method.rpcUrl || !method.contractAddress)
354
354
  continue;
355
- const rpcCall = createRpcCaller(method.rpcUrl);
355
+ const rpcCall = createRpcCaller(method.rpcUrl, JSON.parse(method.rpcHeaders ?? "{}"));
356
356
  let latestBlock;
357
357
  try {
358
358
  const latestHex = (await rpcCall("eth_blockNumber", []));
@@ -370,7 +370,7 @@ export async function startWatchers(opts) {
370
370
  rpcCall,
371
371
  fromBlock: latestBlock,
372
372
  watchedAddresses: chainAddresses.map((a) => toWatcherAddr(a, method)),
373
- contractAddress: method.contractAddress,
373
+ contractAddress: toWatcherAddr(method.contractAddress, method),
374
374
  decimals: method.decimals,
375
375
  confirmations: method.confirmations,
376
376
  cursorStore,
@@ -631,6 +631,23 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
631
631
  identity: undefined;
632
632
  generated: undefined;
633
633
  }, {}, {}>;
634
+ rpcHeaders: import("drizzle-orm/pg-core").PgColumn<{
635
+ name: "rpc_headers";
636
+ tableName: "payment_methods";
637
+ dataType: "string";
638
+ columnType: "PgText";
639
+ data: string;
640
+ driverParam: string;
641
+ notNull: true;
642
+ hasDefault: true;
643
+ isPrimaryKey: false;
644
+ isAutoincrement: false;
645
+ hasRuntimeDefault: false;
646
+ enumValues: [string, ...string[]];
647
+ baseColumn: never;
648
+ identity: undefined;
649
+ generated: undefined;
650
+ }, {}, {}>;
634
651
  oracleAddress: import("drizzle-orm/pg-core").PgColumn<{
635
652
  name: "oracle_address";
636
653
  tableName: "payment_methods";
@@ -716,6 +733,23 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
716
733
  identity: undefined;
717
734
  generated: undefined;
718
735
  }, {}, {}>;
736
+ oracleAssetId: import("drizzle-orm/pg-core").PgColumn<{
737
+ name: "oracle_asset_id";
738
+ tableName: "payment_methods";
739
+ dataType: "string";
740
+ columnType: "PgText";
741
+ data: string;
742
+ driverParam: string;
743
+ notNull: false;
744
+ hasDefault: false;
745
+ isPrimaryKey: false;
746
+ isAutoincrement: false;
747
+ hasRuntimeDefault: false;
748
+ enumValues: [string, ...string[]];
749
+ baseColumn: never;
750
+ identity: undefined;
751
+ generated: undefined;
752
+ }, {}, {}>;
719
753
  confirmations: import("drizzle-orm/pg-core").PgColumn<{
720
754
  name: "confirmations";
721
755
  tableName: "payment_methods";
@@ -73,11 +73,13 @@ export const paymentMethods = pgTable("payment_methods", {
73
73
  displayOrder: integer("display_order").notNull().default(0),
74
74
  iconUrl: text("icon_url"),
75
75
  rpcUrl: text("rpc_url"), // chain node RPC endpoint
76
+ rpcHeaders: text("rpc_headers").notNull().default("{}"), // JSON: extra headers for RPC calls (e.g. {"TRON-PRO-API-KEY":"xxx"})
76
77
  oracleAddress: text("oracle_address"), // Chainlink feed address for price (null = 1:1 stablecoin)
77
78
  xpub: text("xpub"), // HD wallet extended public key for deposit address derivation
78
- addressType: text("address_type").notNull().default("evm"), // "bech32" (BTC/LTC), "p2pkh" (DOGE), "evm" (ETH/ERC20)
79
+ addressType: text("address_type").notNull().default("evm"), // "bech32" (BTC/LTC), "p2pkh" (DOGE/TRX), "evm" (ETH/ERC20)
79
80
  encodingParams: text("encoding_params").notNull().default("{}"), // JSON: {"hrp":"bc"}, {"version":"0x1e"}, etc.
80
81
  watcherType: text("watcher_type").notNull().default("evm"), // "utxo" (BTC/LTC/DOGE) or "evm" (ETH/ERC20/TRX)
82
+ oracleAssetId: text("oracle_asset_id"), // CoinGecko slug (e.g. "bitcoin", "tron"). Null = stablecoin (1:1 USD) or use token symbol fallback.
81
83
  confirmations: integer("confirmations").notNull().default(1),
82
84
  nextIndex: integer("next_index").notNull().default(0), // atomic derivation counter, never reuses
83
85
  createdAt: text("created_at").notNull().default(sql `(now())`),