@wopr-network/platform-core 1.63.2 → 1.65.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 (47) hide show
  1. package/dist/billing/crypto/__tests__/address-gen.test.js +191 -90
  2. package/dist/billing/crypto/address-gen.js +32 -0
  3. package/dist/billing/crypto/evm/eth-watcher.js +52 -41
  4. package/dist/billing/crypto/evm/watcher.js +5 -11
  5. package/dist/billing/crypto/index.d.ts +2 -0
  6. package/dist/billing/crypto/index.js +1 -0
  7. package/dist/billing/crypto/key-server.js +4 -0
  8. package/dist/billing/crypto/payment-method-store.d.ts +4 -0
  9. package/dist/billing/crypto/payment-method-store.js +11 -0
  10. package/dist/billing/crypto/plugin/__tests__/integration.test.d.ts +1 -0
  11. package/dist/billing/crypto/plugin/__tests__/integration.test.js +58 -0
  12. package/dist/billing/crypto/plugin/__tests__/interfaces.test.d.ts +1 -0
  13. package/dist/billing/crypto/plugin/__tests__/interfaces.test.js +46 -0
  14. package/dist/billing/crypto/plugin/__tests__/registry.test.d.ts +1 -0
  15. package/dist/billing/crypto/plugin/__tests__/registry.test.js +49 -0
  16. package/dist/billing/crypto/plugin/index.d.ts +2 -0
  17. package/dist/billing/crypto/plugin/index.js +1 -0
  18. package/dist/billing/crypto/plugin/interfaces.d.ts +97 -0
  19. package/dist/billing/crypto/plugin/interfaces.js +2 -0
  20. package/dist/billing/crypto/plugin/registry.d.ts +8 -0
  21. package/dist/billing/crypto/plugin/registry.js +21 -0
  22. package/dist/billing/crypto/watcher-service.js +4 -4
  23. package/dist/db/schema/crypto.d.ts +345 -0
  24. package/dist/db/schema/crypto.js +34 -1
  25. package/dist/db/schema/snapshots.d.ts +1 -1
  26. package/docs/superpowers/plans/2026-03-24-crypto-plugin-phase1.md +697 -0
  27. package/docs/superpowers/specs/2026-03-24-crypto-plugin-architecture-design.md +309 -0
  28. package/drizzle/migrations/0022_rpc_headers_column.sql +1 -0
  29. package/drizzle/migrations/0023_key_rings_table.sql +35 -0
  30. package/drizzle/migrations/0024_backfill_key_rings.sql +75 -0
  31. package/drizzle/migrations/meta/_journal.json +14 -0
  32. package/package.json +5 -1
  33. package/src/billing/crypto/__tests__/address-gen.test.ts +207 -88
  34. package/src/billing/crypto/address-gen.ts +31 -0
  35. package/src/billing/crypto/evm/eth-watcher.ts +64 -47
  36. package/src/billing/crypto/evm/watcher.ts +8 -9
  37. package/src/billing/crypto/index.ts +9 -0
  38. package/src/billing/crypto/key-server.ts +5 -0
  39. package/src/billing/crypto/payment-method-store.ts +15 -0
  40. package/src/billing/crypto/plugin/__tests__/integration.test.ts +64 -0
  41. package/src/billing/crypto/plugin/__tests__/interfaces.test.ts +51 -0
  42. package/src/billing/crypto/plugin/__tests__/registry.test.ts +58 -0
  43. package/src/billing/crypto/plugin/index.ts +17 -0
  44. package/src/billing/crypto/plugin/interfaces.ts +106 -0
  45. package/src/billing/crypto/plugin/registry.ts +26 -0
  46. package/src/billing/crypto/watcher-service.ts +4 -4
  47. package/src/db/schema/crypto.ts +44 -1
@@ -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";
@@ -767,6 +784,57 @@ export declare const paymentMethods: import("drizzle-orm/pg-core").PgTableWithCo
767
784
  identity: undefined;
768
785
  generated: undefined;
769
786
  }, {}, {}>;
787
+ keyRingId: import("drizzle-orm/pg-core").PgColumn<{
788
+ name: "key_ring_id";
789
+ tableName: "payment_methods";
790
+ dataType: "string";
791
+ columnType: "PgText";
792
+ data: string;
793
+ driverParam: string;
794
+ notNull: false;
795
+ hasDefault: false;
796
+ isPrimaryKey: false;
797
+ isAutoincrement: false;
798
+ hasRuntimeDefault: false;
799
+ enumValues: [string, ...string[]];
800
+ baseColumn: never;
801
+ identity: undefined;
802
+ generated: undefined;
803
+ }, {}, {}>;
804
+ encoding: import("drizzle-orm/pg-core").PgColumn<{
805
+ name: "encoding";
806
+ tableName: "payment_methods";
807
+ dataType: "string";
808
+ columnType: "PgText";
809
+ data: string;
810
+ driverParam: string;
811
+ notNull: false;
812
+ hasDefault: false;
813
+ isPrimaryKey: false;
814
+ isAutoincrement: false;
815
+ hasRuntimeDefault: false;
816
+ enumValues: [string, ...string[]];
817
+ baseColumn: never;
818
+ identity: undefined;
819
+ generated: undefined;
820
+ }, {}, {}>;
821
+ pluginId: import("drizzle-orm/pg-core").PgColumn<{
822
+ name: "plugin_id";
823
+ tableName: "payment_methods";
824
+ dataType: "string";
825
+ columnType: "PgText";
826
+ data: string;
827
+ driverParam: string;
828
+ notNull: false;
829
+ hasDefault: false;
830
+ isPrimaryKey: false;
831
+ isAutoincrement: false;
832
+ hasRuntimeDefault: false;
833
+ enumValues: [string, ...string[]];
834
+ baseColumn: never;
835
+ identity: undefined;
836
+ generated: undefined;
837
+ }, {}, {}>;
770
838
  createdAt: import("drizzle-orm/pg-core").PgColumn<{
771
839
  name: "created_at";
772
840
  tableName: "payment_methods";
@@ -1220,3 +1288,280 @@ export declare const watcherProcessed: import("drizzle-orm/pg-core").PgTableWith
1220
1288
  };
1221
1289
  dialect: "pg";
1222
1290
  }>;
1291
+ /**
1292
+ * Key rings — decouples key material (xpub/seed) from payment methods.
1293
+ * Each key ring maps to a BIP-44 coin type + account index.
1294
+ */
1295
+ export declare const keyRings: import("drizzle-orm/pg-core").PgTableWithColumns<{
1296
+ name: "key_rings";
1297
+ schema: undefined;
1298
+ columns: {
1299
+ id: import("drizzle-orm/pg-core").PgColumn<{
1300
+ name: "id";
1301
+ tableName: "key_rings";
1302
+ dataType: "string";
1303
+ columnType: "PgText";
1304
+ data: string;
1305
+ driverParam: string;
1306
+ notNull: true;
1307
+ hasDefault: false;
1308
+ isPrimaryKey: true;
1309
+ isAutoincrement: false;
1310
+ hasRuntimeDefault: false;
1311
+ enumValues: [string, ...string[]];
1312
+ baseColumn: never;
1313
+ identity: undefined;
1314
+ generated: undefined;
1315
+ }, {}, {}>;
1316
+ curve: import("drizzle-orm/pg-core").PgColumn<{
1317
+ name: "curve";
1318
+ tableName: "key_rings";
1319
+ dataType: "string";
1320
+ columnType: "PgText";
1321
+ data: string;
1322
+ driverParam: string;
1323
+ notNull: true;
1324
+ hasDefault: false;
1325
+ isPrimaryKey: false;
1326
+ isAutoincrement: false;
1327
+ hasRuntimeDefault: false;
1328
+ enumValues: [string, ...string[]];
1329
+ baseColumn: never;
1330
+ identity: undefined;
1331
+ generated: undefined;
1332
+ }, {}, {}>;
1333
+ derivationScheme: import("drizzle-orm/pg-core").PgColumn<{
1334
+ name: "derivation_scheme";
1335
+ tableName: "key_rings";
1336
+ dataType: "string";
1337
+ columnType: "PgText";
1338
+ data: string;
1339
+ driverParam: string;
1340
+ notNull: true;
1341
+ hasDefault: false;
1342
+ isPrimaryKey: false;
1343
+ isAutoincrement: false;
1344
+ hasRuntimeDefault: false;
1345
+ enumValues: [string, ...string[]];
1346
+ baseColumn: never;
1347
+ identity: undefined;
1348
+ generated: undefined;
1349
+ }, {}, {}>;
1350
+ derivationMode: import("drizzle-orm/pg-core").PgColumn<{
1351
+ name: "derivation_mode";
1352
+ tableName: "key_rings";
1353
+ dataType: "string";
1354
+ columnType: "PgText";
1355
+ data: string;
1356
+ driverParam: string;
1357
+ notNull: true;
1358
+ hasDefault: true;
1359
+ isPrimaryKey: false;
1360
+ isAutoincrement: false;
1361
+ hasRuntimeDefault: false;
1362
+ enumValues: [string, ...string[]];
1363
+ baseColumn: never;
1364
+ identity: undefined;
1365
+ generated: undefined;
1366
+ }, {}, {}>;
1367
+ keyMaterial: import("drizzle-orm/pg-core").PgColumn<{
1368
+ name: "key_material";
1369
+ tableName: "key_rings";
1370
+ dataType: "string";
1371
+ columnType: "PgText";
1372
+ data: string;
1373
+ driverParam: string;
1374
+ notNull: true;
1375
+ hasDefault: true;
1376
+ isPrimaryKey: false;
1377
+ isAutoincrement: false;
1378
+ hasRuntimeDefault: false;
1379
+ enumValues: [string, ...string[]];
1380
+ baseColumn: never;
1381
+ identity: undefined;
1382
+ generated: undefined;
1383
+ }, {}, {}>;
1384
+ coinType: import("drizzle-orm/pg-core").PgColumn<{
1385
+ name: "coin_type";
1386
+ tableName: "key_rings";
1387
+ dataType: "number";
1388
+ columnType: "PgInteger";
1389
+ data: number;
1390
+ driverParam: string | number;
1391
+ notNull: true;
1392
+ hasDefault: false;
1393
+ isPrimaryKey: false;
1394
+ isAutoincrement: false;
1395
+ hasRuntimeDefault: false;
1396
+ enumValues: undefined;
1397
+ baseColumn: never;
1398
+ identity: undefined;
1399
+ generated: undefined;
1400
+ }, {}, {}>;
1401
+ accountIndex: import("drizzle-orm/pg-core").PgColumn<{
1402
+ name: "account_index";
1403
+ tableName: "key_rings";
1404
+ dataType: "number";
1405
+ columnType: "PgInteger";
1406
+ data: number;
1407
+ driverParam: string | number;
1408
+ notNull: true;
1409
+ hasDefault: true;
1410
+ isPrimaryKey: false;
1411
+ isAutoincrement: false;
1412
+ hasRuntimeDefault: false;
1413
+ enumValues: undefined;
1414
+ baseColumn: never;
1415
+ identity: undefined;
1416
+ generated: undefined;
1417
+ }, {}, {}>;
1418
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
1419
+ name: "created_at";
1420
+ tableName: "key_rings";
1421
+ dataType: "string";
1422
+ columnType: "PgText";
1423
+ data: string;
1424
+ driverParam: string;
1425
+ notNull: true;
1426
+ hasDefault: true;
1427
+ isPrimaryKey: false;
1428
+ isAutoincrement: false;
1429
+ hasRuntimeDefault: false;
1430
+ enumValues: [string, ...string[]];
1431
+ baseColumn: never;
1432
+ identity: undefined;
1433
+ generated: undefined;
1434
+ }, {}, {}>;
1435
+ };
1436
+ dialect: "pg";
1437
+ }>;
1438
+ /**
1439
+ * Pre-derived address pool — for Ed25519 chains that need offline derivation.
1440
+ * Addresses are derived in batches and assigned on demand.
1441
+ */
1442
+ export declare const addressPool: import("drizzle-orm/pg-core").PgTableWithColumns<{
1443
+ name: "address_pool";
1444
+ schema: undefined;
1445
+ columns: {
1446
+ id: import("drizzle-orm/pg-core").PgColumn<{
1447
+ name: "id";
1448
+ tableName: "address_pool";
1449
+ dataType: "number";
1450
+ columnType: "PgSerial";
1451
+ data: number;
1452
+ driverParam: number;
1453
+ notNull: true;
1454
+ hasDefault: true;
1455
+ isPrimaryKey: true;
1456
+ isAutoincrement: false;
1457
+ hasRuntimeDefault: false;
1458
+ enumValues: undefined;
1459
+ baseColumn: never;
1460
+ identity: undefined;
1461
+ generated: undefined;
1462
+ }, {}, {}>;
1463
+ keyRingId: import("drizzle-orm/pg-core").PgColumn<{
1464
+ name: "key_ring_id";
1465
+ tableName: "address_pool";
1466
+ dataType: "string";
1467
+ columnType: "PgText";
1468
+ data: string;
1469
+ driverParam: string;
1470
+ notNull: true;
1471
+ hasDefault: false;
1472
+ isPrimaryKey: false;
1473
+ isAutoincrement: false;
1474
+ hasRuntimeDefault: false;
1475
+ enumValues: [string, ...string[]];
1476
+ baseColumn: never;
1477
+ identity: undefined;
1478
+ generated: undefined;
1479
+ }, {}, {}>;
1480
+ derivationIndex: import("drizzle-orm/pg-core").PgColumn<{
1481
+ name: "derivation_index";
1482
+ tableName: "address_pool";
1483
+ dataType: "number";
1484
+ columnType: "PgInteger";
1485
+ data: number;
1486
+ driverParam: string | number;
1487
+ notNull: true;
1488
+ hasDefault: false;
1489
+ isPrimaryKey: false;
1490
+ isAutoincrement: false;
1491
+ hasRuntimeDefault: false;
1492
+ enumValues: undefined;
1493
+ baseColumn: never;
1494
+ identity: undefined;
1495
+ generated: undefined;
1496
+ }, {}, {}>;
1497
+ publicKey: import("drizzle-orm/pg-core").PgColumn<{
1498
+ name: "public_key";
1499
+ tableName: "address_pool";
1500
+ dataType: "string";
1501
+ columnType: "PgText";
1502
+ data: string;
1503
+ driverParam: string;
1504
+ notNull: true;
1505
+ hasDefault: false;
1506
+ isPrimaryKey: false;
1507
+ isAutoincrement: false;
1508
+ hasRuntimeDefault: false;
1509
+ enumValues: [string, ...string[]];
1510
+ baseColumn: never;
1511
+ identity: undefined;
1512
+ generated: undefined;
1513
+ }, {}, {}>;
1514
+ address: import("drizzle-orm/pg-core").PgColumn<{
1515
+ name: "address";
1516
+ tableName: "address_pool";
1517
+ dataType: "string";
1518
+ columnType: "PgText";
1519
+ data: string;
1520
+ driverParam: string;
1521
+ notNull: true;
1522
+ hasDefault: false;
1523
+ isPrimaryKey: false;
1524
+ isAutoincrement: false;
1525
+ hasRuntimeDefault: false;
1526
+ enumValues: [string, ...string[]];
1527
+ baseColumn: never;
1528
+ identity: undefined;
1529
+ generated: undefined;
1530
+ }, {}, {}>;
1531
+ assignedTo: import("drizzle-orm/pg-core").PgColumn<{
1532
+ name: "assigned_to";
1533
+ tableName: "address_pool";
1534
+ dataType: "string";
1535
+ columnType: "PgText";
1536
+ data: string;
1537
+ driverParam: string;
1538
+ notNull: false;
1539
+ hasDefault: false;
1540
+ isPrimaryKey: false;
1541
+ isAutoincrement: false;
1542
+ hasRuntimeDefault: false;
1543
+ enumValues: [string, ...string[]];
1544
+ baseColumn: never;
1545
+ identity: undefined;
1546
+ generated: undefined;
1547
+ }, {}, {}>;
1548
+ createdAt: import("drizzle-orm/pg-core").PgColumn<{
1549
+ name: "created_at";
1550
+ tableName: "address_pool";
1551
+ dataType: "string";
1552
+ columnType: "PgText";
1553
+ data: string;
1554
+ driverParam: string;
1555
+ notNull: true;
1556
+ hasDefault: true;
1557
+ isPrimaryKey: false;
1558
+ isAutoincrement: false;
1559
+ hasRuntimeDefault: false;
1560
+ enumValues: [string, ...string[]];
1561
+ baseColumn: never;
1562
+ identity: undefined;
1563
+ generated: undefined;
1564
+ }, {}, {}>;
1565
+ };
1566
+ dialect: "pg";
1567
+ }>;
@@ -1,5 +1,5 @@
1
1
  import { sql } from "drizzle-orm";
2
- import { boolean, index, integer, pgTable, primaryKey, text } from "drizzle-orm/pg-core";
2
+ import { boolean, index, integer, pgTable, primaryKey, serial, text, uniqueIndex } from "drizzle-orm/pg-core";
3
3
  /**
4
4
  * Crypto payment charges — tracks the lifecycle of each payment.
5
5
  * reference_id is the charge ID (e.g. "btc:bc1q...").
@@ -73,6 +73,7 @@ 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
79
  addressType: text("address_type").notNull().default("evm"), // "bech32" (BTC/LTC), "p2pkh" (DOGE/TRX), "evm" (ETH/ERC20)
@@ -81,6 +82,9 @@ export const paymentMethods = pgTable("payment_methods", {
81
82
  oracleAssetId: text("oracle_asset_id"), // CoinGecko slug (e.g. "bitcoin", "tron"). Null = stablecoin (1:1 USD) or use token symbol fallback.
82
83
  confirmations: integer("confirmations").notNull().default(1),
83
84
  nextIndex: integer("next_index").notNull().default(0), // atomic derivation counter, never reuses
85
+ keyRingId: text("key_ring_id"), // FK to key_rings.id (nullable during migration)
86
+ encoding: text("encoding"), // address encoding override (e.g. "bech32", "p2pkh", "evm")
87
+ pluginId: text("plugin_id"), // plugin identifier (e.g. "evm", "utxo", "solana")
84
88
  createdAt: text("created_at").notNull().default(sql `(now())`),
85
89
  });
86
90
  /**
@@ -133,3 +137,32 @@ export const watcherProcessed = pgTable("watcher_processed", {
133
137
  txId: text("tx_id").notNull(),
134
138
  processedAt: text("processed_at").notNull().default(sql `(now())`),
135
139
  }, (table) => [primaryKey({ columns: [table.watcherId, table.txId] })]);
140
+ /**
141
+ * Key rings — decouples key material (xpub/seed) from payment methods.
142
+ * Each key ring maps to a BIP-44 coin type + account index.
143
+ */
144
+ export const keyRings = pgTable("key_rings", {
145
+ id: text("id").primaryKey(),
146
+ curve: text("curve").notNull(), // "secp256k1" | "ed25519"
147
+ derivationScheme: text("derivation_scheme").notNull(), // "bip32" | "slip10" | "ed25519-hd"
148
+ derivationMode: text("derivation_mode").notNull().default("on-demand"), // "on-demand" | "pre-derived"
149
+ keyMaterial: text("key_material").notNull().default("{}"), // JSON: { xpub: "..." }
150
+ coinType: integer("coin_type").notNull(), // BIP-44 coin type
151
+ accountIndex: integer("account_index").notNull().default(0),
152
+ createdAt: text("created_at").notNull().default(sql `(now())`),
153
+ }, (table) => [uniqueIndex("key_rings_path_unique").on(table.coinType, table.accountIndex)]);
154
+ /**
155
+ * Pre-derived address pool — for Ed25519 chains that need offline derivation.
156
+ * Addresses are derived in batches and assigned on demand.
157
+ */
158
+ export const addressPool = pgTable("address_pool", {
159
+ id: serial("id").primaryKey(),
160
+ keyRingId: text("key_ring_id")
161
+ .notNull()
162
+ .references(() => keyRings.id),
163
+ derivationIndex: integer("derivation_index").notNull(),
164
+ publicKey: text("public_key").notNull(),
165
+ address: text("address").notNull(),
166
+ assignedTo: text("assigned_to"), // charge reference or tenant ID
167
+ createdAt: text("created_at").notNull().default(sql `(now())`),
168
+ }, (table) => [uniqueIndex("address_pool_ring_index").on(table.keyRingId, table.derivationIndex)]);
@@ -92,7 +92,7 @@ export declare const snapshots: import("drizzle-orm/pg-core").PgTableWithColumns
92
92
  tableName: "snapshots";
93
93
  dataType: "string";
94
94
  columnType: "PgText";
95
- data: "nightly" | "on-demand" | "pre-restore";
95
+ data: "on-demand" | "nightly" | "pre-restore";
96
96
  driverParam: string;
97
97
  notNull: true;
98
98
  hasDefault: true;