@wopr-network/platform-core 1.67.0 → 1.68.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 (140) hide show
  1. package/dist/auth/better-auth.js +7 -0
  2. package/dist/billing/crypto/btc/checkout.d.ts +4 -0
  3. package/dist/billing/crypto/btc/checkout.js +1 -2
  4. package/dist/billing/crypto/btc/index.d.ts +0 -4
  5. package/dist/billing/crypto/btc/index.js +0 -2
  6. package/dist/billing/crypto/evm/__tests__/checkout.test.js +8 -11
  7. package/dist/billing/crypto/evm/__tests__/eth-checkout.test.js +15 -1
  8. package/dist/billing/crypto/evm/checkout.d.ts +2 -0
  9. package/dist/billing/crypto/evm/checkout.js +1 -2
  10. package/dist/billing/crypto/evm/eth-checkout.d.ts +13 -2
  11. package/dist/billing/crypto/evm/eth-checkout.js +2 -4
  12. package/dist/billing/crypto/evm/eth-settler.d.ts +1 -1
  13. package/dist/billing/crypto/evm/index.d.ts +2 -8
  14. package/dist/billing/crypto/evm/index.js +0 -3
  15. package/dist/billing/crypto/evm/types.d.ts +16 -0
  16. package/dist/billing/crypto/index.d.ts +1 -6
  17. package/dist/billing/crypto/index.js +2 -3
  18. package/dist/billing/crypto/types.d.ts +0 -43
  19. package/dist/billing/crypto/types.js +1 -24
  20. package/dist/email/client.js +16 -0
  21. package/package.json +4 -7
  22. package/src/auth/better-auth.ts +8 -0
  23. package/src/billing/crypto/btc/checkout.ts +3 -2
  24. package/src/billing/crypto/btc/index.ts +0 -4
  25. package/src/billing/crypto/evm/__tests__/checkout.test.ts +10 -12
  26. package/src/billing/crypto/evm/__tests__/eth-checkout.test.ts +17 -1
  27. package/src/billing/crypto/evm/__tests__/eth-settler.test.ts +1 -1
  28. package/src/billing/crypto/evm/checkout.ts +3 -2
  29. package/src/billing/crypto/evm/eth-checkout.ts +15 -6
  30. package/src/billing/crypto/evm/eth-settler.ts +1 -1
  31. package/src/billing/crypto/evm/index.ts +8 -7
  32. package/src/billing/crypto/evm/types.ts +17 -0
  33. package/src/billing/crypto/index.ts +14 -12
  34. package/src/billing/crypto/types.ts +0 -63
  35. package/src/email/client.ts +18 -0
  36. package/dist/billing/crypto/__tests__/address-gen.test.d.ts +0 -1
  37. package/dist/billing/crypto/__tests__/address-gen.test.js +0 -219
  38. package/dist/billing/crypto/__tests__/key-server.test.d.ts +0 -1
  39. package/dist/billing/crypto/__tests__/key-server.test.js +0 -742
  40. package/dist/billing/crypto/__tests__/watcher-service.test.d.ts +0 -1
  41. package/dist/billing/crypto/__tests__/watcher-service.test.js +0 -174
  42. package/dist/billing/crypto/address-gen.d.ts +0 -24
  43. package/dist/billing/crypto/address-gen.js +0 -176
  44. package/dist/billing/crypto/btc/__tests__/watcher.test.d.ts +0 -1
  45. package/dist/billing/crypto/btc/__tests__/watcher.test.js +0 -170
  46. package/dist/billing/crypto/btc/watcher.d.ts +0 -44
  47. package/dist/billing/crypto/btc/watcher.js +0 -118
  48. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.d.ts +0 -1
  49. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.js +0 -167
  50. package/dist/billing/crypto/evm/__tests__/watcher-confirmations.test.d.ts +0 -1
  51. package/dist/billing/crypto/evm/__tests__/watcher-confirmations.test.js +0 -159
  52. package/dist/billing/crypto/evm/__tests__/watcher.test.d.ts +0 -1
  53. package/dist/billing/crypto/evm/__tests__/watcher.test.js +0 -145
  54. package/dist/billing/crypto/evm/eth-watcher.d.ts +0 -66
  55. package/dist/billing/crypto/evm/eth-watcher.js +0 -121
  56. package/dist/billing/crypto/evm/watcher.d.ts +0 -51
  57. package/dist/billing/crypto/evm/watcher.js +0 -156
  58. package/dist/billing/crypto/key-server-entry.d.ts +0 -1
  59. package/dist/billing/crypto/key-server-entry.js +0 -122
  60. package/dist/billing/crypto/key-server.d.ts +0 -32
  61. package/dist/billing/crypto/key-server.js +0 -472
  62. package/dist/billing/crypto/oracle/__tests__/chainlink.test.d.ts +0 -1
  63. package/dist/billing/crypto/oracle/__tests__/chainlink.test.js +0 -83
  64. package/dist/billing/crypto/oracle/__tests__/coingecko.test.d.ts +0 -1
  65. package/dist/billing/crypto/oracle/__tests__/coingecko.test.js +0 -65
  66. package/dist/billing/crypto/oracle/__tests__/composite.test.d.ts +0 -1
  67. package/dist/billing/crypto/oracle/__tests__/composite.test.js +0 -48
  68. package/dist/billing/crypto/oracle/__tests__/convert.test.d.ts +0 -1
  69. package/dist/billing/crypto/oracle/__tests__/convert.test.js +0 -61
  70. package/dist/billing/crypto/oracle/__tests__/fixed.test.d.ts +0 -1
  71. package/dist/billing/crypto/oracle/__tests__/fixed.test.js +0 -20
  72. package/dist/billing/crypto/oracle/chainlink.d.ts +0 -26
  73. package/dist/billing/crypto/oracle/chainlink.js +0 -62
  74. package/dist/billing/crypto/oracle/coingecko.d.ts +0 -22
  75. package/dist/billing/crypto/oracle/coingecko.js +0 -71
  76. package/dist/billing/crypto/oracle/composite.d.ts +0 -14
  77. package/dist/billing/crypto/oracle/composite.js +0 -34
  78. package/dist/billing/crypto/oracle/convert.d.ts +0 -30
  79. package/dist/billing/crypto/oracle/convert.js +0 -51
  80. package/dist/billing/crypto/oracle/fixed.d.ts +0 -10
  81. package/dist/billing/crypto/oracle/fixed.js +0 -22
  82. package/dist/billing/crypto/oracle/index.d.ts +0 -9
  83. package/dist/billing/crypto/oracle/index.js +0 -6
  84. package/dist/billing/crypto/oracle/types.d.ts +0 -22
  85. package/dist/billing/crypto/oracle/types.js +0 -7
  86. package/dist/billing/crypto/plugin/__tests__/integration.test.d.ts +0 -1
  87. package/dist/billing/crypto/plugin/__tests__/integration.test.js +0 -58
  88. package/dist/billing/crypto/plugin/__tests__/interfaces.test.d.ts +0 -1
  89. package/dist/billing/crypto/plugin/__tests__/interfaces.test.js +0 -46
  90. package/dist/billing/crypto/plugin/__tests__/registry.test.d.ts +0 -1
  91. package/dist/billing/crypto/plugin/__tests__/registry.test.js +0 -49
  92. package/dist/billing/crypto/plugin/index.d.ts +0 -2
  93. package/dist/billing/crypto/plugin/index.js +0 -1
  94. package/dist/billing/crypto/plugin/interfaces.d.ts +0 -97
  95. package/dist/billing/crypto/plugin/interfaces.js +0 -2
  96. package/dist/billing/crypto/plugin/registry.d.ts +0 -8
  97. package/dist/billing/crypto/plugin/registry.js +0 -21
  98. package/dist/billing/crypto/plugin-watcher-service.d.ts +0 -32
  99. package/dist/billing/crypto/plugin-watcher-service.js +0 -113
  100. package/dist/billing/crypto/tron/__tests__/address-convert.test.d.ts +0 -1
  101. package/dist/billing/crypto/tron/__tests__/address-convert.test.js +0 -55
  102. package/dist/billing/crypto/tron/address-convert.d.ts +0 -14
  103. package/dist/billing/crypto/tron/address-convert.js +0 -93
  104. package/dist/billing/crypto/watcher-service.d.ts +0 -55
  105. package/dist/billing/crypto/watcher-service.js +0 -438
  106. package/src/billing/crypto/__tests__/address-gen.test.ts +0 -264
  107. package/src/billing/crypto/__tests__/key-server.test.ts +0 -823
  108. package/src/billing/crypto/__tests__/watcher-service.test.ts +0 -242
  109. package/src/billing/crypto/address-gen.ts +0 -185
  110. package/src/billing/crypto/btc/__tests__/watcher.test.ts +0 -201
  111. package/src/billing/crypto/btc/watcher.ts +0 -161
  112. package/src/billing/crypto/evm/__tests__/eth-watcher.test.ts +0 -190
  113. package/src/billing/crypto/evm/__tests__/watcher-confirmations.test.ts +0 -191
  114. package/src/billing/crypto/evm/__tests__/watcher.test.ts +0 -167
  115. package/src/billing/crypto/evm/eth-watcher.ts +0 -182
  116. package/src/billing/crypto/evm/watcher.ts +0 -204
  117. package/src/billing/crypto/key-server-entry.ts +0 -144
  118. package/src/billing/crypto/key-server.ts +0 -617
  119. package/src/billing/crypto/oracle/__tests__/chainlink.test.ts +0 -107
  120. package/src/billing/crypto/oracle/__tests__/coingecko.test.ts +0 -75
  121. package/src/billing/crypto/oracle/__tests__/composite.test.ts +0 -61
  122. package/src/billing/crypto/oracle/__tests__/convert.test.ts +0 -74
  123. package/src/billing/crypto/oracle/__tests__/fixed.test.ts +0 -23
  124. package/src/billing/crypto/oracle/chainlink.ts +0 -86
  125. package/src/billing/crypto/oracle/coingecko.ts +0 -96
  126. package/src/billing/crypto/oracle/composite.ts +0 -35
  127. package/src/billing/crypto/oracle/convert.ts +0 -53
  128. package/src/billing/crypto/oracle/fixed.ts +0 -25
  129. package/src/billing/crypto/oracle/index.ts +0 -9
  130. package/src/billing/crypto/oracle/types.ts +0 -28
  131. package/src/billing/crypto/plugin/__tests__/integration.test.ts +0 -64
  132. package/src/billing/crypto/plugin/__tests__/interfaces.test.ts +0 -51
  133. package/src/billing/crypto/plugin/__tests__/registry.test.ts +0 -58
  134. package/src/billing/crypto/plugin/index.ts +0 -17
  135. package/src/billing/crypto/plugin/interfaces.ts +0 -106
  136. package/src/billing/crypto/plugin/registry.ts +0 -26
  137. package/src/billing/crypto/plugin-watcher-service.ts +0 -148
  138. package/src/billing/crypto/tron/__tests__/address-convert.test.ts +0 -67
  139. package/src/billing/crypto/tron/address-convert.ts +0 -89
  140. package/src/billing/crypto/watcher-service.ts +0 -549
@@ -1,49 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { PluginRegistry } from "../registry.js";
3
- function mockPlugin(id, curve = "secp256k1") {
4
- return {
5
- pluginId: id,
6
- supportedCurve: curve,
7
- encoders: {},
8
- createWatcher: () => ({
9
- init: async () => { },
10
- poll: async () => [],
11
- setWatchedAddresses: () => { },
12
- getCursor: () => 0,
13
- stop: () => { },
14
- }),
15
- createSweeper: () => ({ scan: async () => [], sweep: async () => [] }),
16
- version: 1,
17
- };
18
- }
19
- describe("PluginRegistry", () => {
20
- it("registers and retrieves a plugin", () => {
21
- const reg = new PluginRegistry();
22
- reg.register(mockPlugin("evm"));
23
- expect(reg.get("evm")).toBeDefined();
24
- expect(reg.get("evm")?.pluginId).toBe("evm");
25
- });
26
- it("throws on duplicate registration", () => {
27
- const reg = new PluginRegistry();
28
- reg.register(mockPlugin("evm"));
29
- expect(() => reg.register(mockPlugin("evm"))).toThrow("already registered");
30
- });
31
- it("returns undefined for unknown plugin", () => {
32
- const reg = new PluginRegistry();
33
- expect(reg.get("unknown")).toBeUndefined();
34
- });
35
- it("lists all registered plugins", () => {
36
- const reg = new PluginRegistry();
37
- reg.register(mockPlugin("evm"));
38
- reg.register(mockPlugin("solana", "ed25519"));
39
- expect(reg.list()).toHaveLength(2);
40
- expect(reg
41
- .list()
42
- .map((p) => p.pluginId)
43
- .sort()).toEqual(["evm", "solana"]);
44
- });
45
- it("getOrThrow throws for unknown plugin", () => {
46
- const reg = new PluginRegistry();
47
- expect(() => reg.getOrThrow("nope")).toThrow("not registered");
48
- });
49
- });
@@ -1,2 +0,0 @@
1
- export type { DepositInfo, EncodingParams, IAddressEncoder, IChainPlugin, IChainWatcher, ICurveDeriver, IPriceOracle, ISweepStrategy, IWatcherCursorStore, KeyPair, PaymentEvent, SweeperOpts, SweepResult, WatcherOpts, } from "./interfaces.js";
2
- export { PluginRegistry } from "./registry.js";
@@ -1 +0,0 @@
1
- export { PluginRegistry } from "./registry.js";
@@ -1,97 +0,0 @@
1
- export interface PaymentEvent {
2
- chain: string;
3
- token: string;
4
- from: string;
5
- to: string;
6
- rawAmount: string;
7
- amountUsdCents: number;
8
- txHash: string;
9
- blockNumber: number;
10
- confirmations: number;
11
- confirmationsRequired: number;
12
- }
13
- export interface ICurveDeriver {
14
- derivePublicKey(chainIndex: number, addressIndex: number): Uint8Array;
15
- getCurve(): "secp256k1" | "ed25519";
16
- }
17
- export interface EncodingParams {
18
- hrp?: string;
19
- version?: string;
20
- [key: string]: string | undefined;
21
- }
22
- export interface IAddressEncoder {
23
- encode(publicKey: Uint8Array, params: EncodingParams): string;
24
- encodingType(): string;
25
- }
26
- export interface KeyPair {
27
- privateKey: Uint8Array;
28
- publicKey: Uint8Array;
29
- address: string;
30
- index: number;
31
- }
32
- export interface DepositInfo {
33
- index: number;
34
- address: string;
35
- nativeBalance: bigint;
36
- tokenBalances: Array<{
37
- token: string;
38
- balance: bigint;
39
- decimals: number;
40
- }>;
41
- }
42
- export interface SweepResult {
43
- index: number;
44
- address: string;
45
- token: string;
46
- amount: string;
47
- txHash: string;
48
- }
49
- export interface ISweepStrategy {
50
- scan(keys: KeyPair[], treasury: string): Promise<DepositInfo[]>;
51
- sweep(keys: KeyPair[], treasury: string, dryRun: boolean): Promise<SweepResult[]>;
52
- }
53
- export interface IPriceOracle {
54
- getPrice(token: string, feedAddress?: string): Promise<{
55
- priceMicros: number;
56
- }>;
57
- }
58
- export interface IWatcherCursorStore {
59
- get(watcherId: string): Promise<number | null>;
60
- save(watcherId: string, cursor: number): Promise<void>;
61
- getConfirmationCount(watcherId: string, txKey: string): Promise<number | null>;
62
- saveConfirmationCount(watcherId: string, txKey: string, count: number): Promise<void>;
63
- }
64
- export interface WatcherOpts {
65
- rpcUrl: string;
66
- rpcHeaders: Record<string, string>;
67
- oracle: IPriceOracle;
68
- cursorStore: IWatcherCursorStore;
69
- token: string;
70
- chain: string;
71
- contractAddress?: string;
72
- decimals: number;
73
- confirmations: number;
74
- }
75
- export interface SweeperOpts {
76
- rpcUrl: string;
77
- rpcHeaders: Record<string, string>;
78
- token: string;
79
- chain: string;
80
- contractAddress?: string;
81
- decimals: number;
82
- }
83
- export interface IChainWatcher {
84
- init(): Promise<void>;
85
- poll(): Promise<PaymentEvent[]>;
86
- setWatchedAddresses(addresses: string[]): void;
87
- getCursor(): number;
88
- stop(): void;
89
- }
90
- export interface IChainPlugin {
91
- pluginId: string;
92
- supportedCurve: "secp256k1" | "ed25519";
93
- encoders: Record<string, IAddressEncoder>;
94
- createWatcher(opts: WatcherOpts): IChainWatcher;
95
- createSweeper(opts: SweeperOpts): ISweepStrategy;
96
- version: number;
97
- }
@@ -1,2 +0,0 @@
1
- // src/billing/crypto/plugin/interfaces.ts
2
- export {};
@@ -1,8 +0,0 @@
1
- import type { IChainPlugin } from "./interfaces.js";
2
- export declare class PluginRegistry {
3
- private plugins;
4
- register(plugin: IChainPlugin): void;
5
- get(pluginId: string): IChainPlugin | undefined;
6
- getOrThrow(pluginId: string): IChainPlugin;
7
- list(): IChainPlugin[];
8
- }
@@ -1,21 +0,0 @@
1
- export class PluginRegistry {
2
- plugins = new Map();
3
- register(plugin) {
4
- if (this.plugins.has(plugin.pluginId)) {
5
- throw new Error(`Plugin "${plugin.pluginId}" is already registered`);
6
- }
7
- this.plugins.set(plugin.pluginId, plugin);
8
- }
9
- get(pluginId) {
10
- return this.plugins.get(pluginId);
11
- }
12
- getOrThrow(pluginId) {
13
- const plugin = this.plugins.get(pluginId);
14
- if (!plugin)
15
- throw new Error(`Plugin "${pluginId}" is not registered`);
16
- return plugin;
17
- }
18
- list() {
19
- return [...this.plugins.values()];
20
- }
21
- }
@@ -1,32 +0,0 @@
1
- /**
2
- * Plugin-driven watcher service — replaces the hardcoded watcher-service.ts.
3
- *
4
- * Instead of importing BtcWatcher/EvmWatcher/EthWatcher directly,
5
- * this delegates to IChainPlugin.createWatcher() from the plugin registry.
6
- * Adding a new chain = register a plugin + INSERT a payment_methods row.
7
- *
8
- * Payment flow is unchanged:
9
- * plugin.poll() -> PaymentEvent[] -> handlePayment() -> credit + webhook
10
- */
11
- import type { DrizzleDb } from "../../db/index.js";
12
- import type { ICryptoChargeRepository } from "./charge-store.js";
13
- import type { IWatcherCursorStore } from "./cursor-store.js";
14
- import type { IPriceOracle } from "./oracle/types.js";
15
- import type { IPaymentMethodStore } from "./payment-method-store.js";
16
- import type { PluginRegistry } from "./plugin/registry.js";
17
- export interface PluginWatcherServiceOpts {
18
- db: DrizzleDb;
19
- chargeStore: ICryptoChargeRepository;
20
- methodStore: IPaymentMethodStore;
21
- cursorStore: IWatcherCursorStore;
22
- oracle: IPriceOracle;
23
- registry: PluginRegistry;
24
- pollIntervalMs?: number;
25
- log?: (msg: string, meta?: Record<string, unknown>) => void;
26
- }
27
- /**
28
- * Boot plugin-driven watchers for all enabled payment methods.
29
- *
30
- * Returns a cleanup function that stops all poll timers and watchers.
31
- */
32
- export declare function startPluginWatchers(opts: PluginWatcherServiceOpts): Promise<() => void>;
@@ -1,113 +0,0 @@
1
- /**
2
- * Plugin-driven watcher service — replaces the hardcoded watcher-service.ts.
3
- *
4
- * Instead of importing BtcWatcher/EvmWatcher/EthWatcher directly,
5
- * this delegates to IChainPlugin.createWatcher() from the plugin registry.
6
- * Adding a new chain = register a plugin + INSERT a payment_methods row.
7
- *
8
- * Payment flow is unchanged:
9
- * plugin.poll() -> PaymentEvent[] -> handlePayment() -> credit + webhook
10
- */
11
- import { handlePayment } from "./watcher-service.js";
12
- /** Map legacy watcher_type values to plugin IDs for backward compatibility. */
13
- const WATCHER_TYPE_TO_PLUGIN = {
14
- utxo: "bitcoin",
15
- evm: "evm",
16
- };
17
- function resolvePlugin(registry, method) {
18
- // Prefer explicit plugin_id, fall back to watcher_type mapping
19
- const id = method.pluginId ?? WATCHER_TYPE_TO_PLUGIN[method.watcherType];
20
- return id ? registry.get(id) : undefined;
21
- }
22
- /**
23
- * Boot plugin-driven watchers for all enabled payment methods.
24
- *
25
- * Returns a cleanup function that stops all poll timers and watchers.
26
- */
27
- export async function startPluginWatchers(opts) {
28
- const { db, chargeStore, methodStore, cursorStore, oracle, registry } = opts;
29
- const pollMs = opts.pollIntervalMs ?? 15_000;
30
- const log = opts.log ?? (() => { });
31
- const methods = await methodStore.listEnabled();
32
- const timers = [];
33
- const watchers = [];
34
- for (const method of methods) {
35
- if (!method.rpcUrl)
36
- continue;
37
- const plugin = resolvePlugin(registry, method);
38
- if (!plugin) {
39
- log("No plugin found, skipping method", { id: method.id, chain: method.chain, watcherType: method.watcherType });
40
- continue;
41
- }
42
- const watcher = plugin.createWatcher({
43
- rpcUrl: method.rpcUrl,
44
- rpcHeaders: JSON.parse(method.rpcHeaders ?? "{}"),
45
- oracle,
46
- cursorStore,
47
- token: method.token,
48
- chain: method.chain,
49
- contractAddress: method.contractAddress ?? undefined,
50
- decimals: method.decimals,
51
- confirmations: method.confirmations,
52
- });
53
- try {
54
- await watcher.init();
55
- }
56
- catch (err) {
57
- log("Watcher init failed, skipping", { chain: method.chain, token: method.token, error: String(err) });
58
- continue;
59
- }
60
- // Seed watched addresses from active charges
61
- const active = await chargeStore.listActiveDepositAddresses();
62
- const addrs = active.filter((a) => a.chain === method.chain && a.token === method.token).map((a) => a.address);
63
- watcher.setWatchedAddresses(addrs);
64
- watchers.push(watcher);
65
- log(`Plugin watcher started (${method.chain}:${method.token})`, {
66
- plugin: plugin.pluginId,
67
- addresses: addrs.length,
68
- });
69
- let polling = false;
70
- timers.push(setInterval(async () => {
71
- if (polling)
72
- return;
73
- polling = true;
74
- try {
75
- // Refresh watched addresses each cycle
76
- const fresh = await chargeStore.listActiveDepositAddresses();
77
- const freshAddrs = fresh
78
- .filter((a) => a.chain === method.chain && a.token === method.token)
79
- .map((a) => a.address);
80
- watcher.setWatchedAddresses(freshAddrs);
81
- const events = await watcher.poll();
82
- for (const ev of events) {
83
- log("Plugin payment", {
84
- chain: ev.chain,
85
- token: ev.token,
86
- to: ev.to,
87
- txHash: ev.txHash,
88
- confirmations: ev.confirmations,
89
- });
90
- await handlePayment(db, chargeStore, ev.to, ev.rawAmount, {
91
- txHash: ev.txHash,
92
- confirmations: ev.confirmations,
93
- confirmationsRequired: ev.confirmationsRequired,
94
- amountReceivedCents: ev.amountUsdCents,
95
- }, log);
96
- }
97
- }
98
- catch (err) {
99
- log("Plugin poll error", { chain: method.chain, token: method.token, error: String(err) });
100
- }
101
- finally {
102
- polling = false;
103
- }
104
- }, pollMs));
105
- }
106
- log("All plugin watchers started", { count: watchers.length, pollMs });
107
- return () => {
108
- for (const t of timers)
109
- clearInterval(t);
110
- for (const w of watchers)
111
- w.stop();
112
- };
113
- }
@@ -1,55 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { hexToTron, isTronAddress, tronToHex } from "../address-convert.js";
3
- // Known Tron address / hex pair (Tron foundation address)
4
- const TRON_ADDR = "TJCnKsPa7y5okkXvQAidZBzqx3QyQ6sxMW";
5
- const HEX_ADDR = "0x5a523b449890854c8fc460ab602df9f31fe4293f";
6
- describe("tronToHex", () => {
7
- it("converts T... to 0x hex", () => {
8
- const hex = tronToHex(TRON_ADDR);
9
- expect(hex).toBe(HEX_ADDR);
10
- });
11
- it("rejects non-Tron address", () => {
12
- expect(() => tronToHex("0x1234")).toThrow("Not a Tron address");
13
- });
14
- it("rejects invalid checksum", () => {
15
- // Flip last character
16
- const bad = `${TRON_ADDR.slice(0, -1)}X`;
17
- expect(() => tronToHex(bad)).toThrow();
18
- });
19
- });
20
- describe("hexToTron", () => {
21
- it("converts 0x hex to T...", () => {
22
- const tron = hexToTron(HEX_ADDR);
23
- expect(tron).toBe(TRON_ADDR);
24
- });
25
- it("handles hex without 0x prefix", () => {
26
- const tron = hexToTron(HEX_ADDR.slice(2));
27
- expect(tron).toBe(TRON_ADDR);
28
- });
29
- it("rejects wrong length", () => {
30
- expect(() => hexToTron("0x1234")).toThrow("Invalid hex address length");
31
- });
32
- });
33
- describe("roundtrip", () => {
34
- it("tronToHex → hexToTron is identity", () => {
35
- const hex = tronToHex(TRON_ADDR);
36
- const back = hexToTron(hex);
37
- expect(back).toBe(TRON_ADDR);
38
- });
39
- it("hexToTron → tronToHex is identity", () => {
40
- const tron = hexToTron(HEX_ADDR);
41
- const back = tronToHex(tron);
42
- expect(back).toBe(HEX_ADDR);
43
- });
44
- });
45
- describe("isTronAddress", () => {
46
- it("returns true for T... address", () => {
47
- expect(isTronAddress(TRON_ADDR)).toBe(true);
48
- });
49
- it("returns false for 0x address", () => {
50
- expect(isTronAddress(HEX_ADDR)).toBe(false);
51
- });
52
- it("returns false for BTC address", () => {
53
- expect(isTronAddress("bc1qtest")).toBe(false);
54
- });
55
- });
@@ -1,14 +0,0 @@
1
- /**
2
- * Convert a Tron T... address to 0x hex (20 bytes, no 0x41 prefix).
3
- * For feeding addresses to the EVM watcher JSON-RPC filters.
4
- */
5
- export declare function tronToHex(tronAddr: string): string;
6
- /**
7
- * Convert a 0x hex address (20 bytes) back to Tron T... Base58Check.
8
- * For converting watcher event addresses back to DB format.
9
- */
10
- export declare function hexToTron(hexAddr: string): string;
11
- /**
12
- * Check if an address is a Tron T... address.
13
- */
14
- export declare function isTronAddress(addr: string): boolean;
@@ -1,93 +0,0 @@
1
- /**
2
- * Tron address conversion — T... Base58Check ↔ 0x hex.
3
- *
4
- * Tron addresses are 21 bytes: 0x41 prefix + 20-byte address.
5
- * The JSON-RPC layer strips the 0x41 and returns standard 0x-prefixed hex.
6
- * We need to convert between the two at the watcher boundary.
7
- */
8
- import { sha256 } from "@noble/hashes/sha2.js";
9
- const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
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
- }
18
- let num = 0n;
19
- for (const ch of s) {
20
- const idx = BASE58_ALPHABET.indexOf(ch);
21
- if (idx < 0)
22
- throw new Error(`Invalid base58 character: ${ch}`);
23
- num = num * 58n + BigInt(idx);
24
- }
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;
33
- }
34
- /**
35
- * Convert a Tron T... address to 0x hex (20 bytes, no 0x41 prefix).
36
- * For feeding addresses to the EVM watcher JSON-RPC filters.
37
- */
38
- export function tronToHex(tronAddr) {
39
- if (!tronAddr.startsWith("T"))
40
- throw new Error(`Not a Tron address: ${tronAddr}`);
41
- const decoded = base58decode(tronAddr);
42
- // decoded: [0x41, ...20 bytes address..., ...4 bytes checksum]
43
- // Verify checksum
44
- const payload = decoded.slice(0, 21);
45
- const checksum = sha256(sha256(payload)).slice(0, 4);
46
- for (let i = 0; i < 4; i++) {
47
- if (decoded[21 + i] !== checksum[i])
48
- throw new Error(`Invalid checksum for Tron address: ${tronAddr}`);
49
- }
50
- // Strip 0x41 prefix, return 20-byte hex with 0x prefix
51
- const addrBytes = payload.slice(1);
52
- return `0x${Array.from(addrBytes, (b) => b.toString(16).padStart(2, "0")).join("")}`;
53
- }
54
- /**
55
- * Convert a 0x hex address (20 bytes) back to Tron T... Base58Check.
56
- * For converting watcher event addresses back to DB format.
57
- */
58
- export function hexToTron(hexAddr) {
59
- const hex = hexAddr.startsWith("0x") ? hexAddr.slice(2) : hexAddr;
60
- if (hex.length !== 40)
61
- throw new Error(`Invalid hex address length: ${hex.length}`);
62
- // Build payload: 0x41 + 20 bytes
63
- const payload = new Uint8Array(21);
64
- payload[0] = 0x41;
65
- for (let i = 0; i < 20; i++)
66
- payload[i + 1] = Number.parseInt(hex.slice(i * 2, i * 2 + 2), 16);
67
- // Compute checksum
68
- const checksum = sha256(sha256(payload)).slice(0, 4);
69
- const full = new Uint8Array(25);
70
- full.set(payload);
71
- full.set(checksum, 21);
72
- // Base58 encode
73
- let num = 0n;
74
- for (const byte of full)
75
- num = num * 256n + BigInt(byte);
76
- let encoded = "";
77
- while (num > 0n) {
78
- encoded = BASE58_ALPHABET[Number(num % 58n)] + encoded;
79
- num = num / 58n;
80
- }
81
- for (const byte of full) {
82
- if (byte !== 0)
83
- break;
84
- encoded = `1${encoded}`;
85
- }
86
- return encoded;
87
- }
88
- /**
89
- * Check if an address is a Tron T... address.
90
- */
91
- export function isTronAddress(addr) {
92
- return addr.startsWith("T") && addr.length >= 33 && addr.length <= 35;
93
- }
@@ -1,55 +0,0 @@
1
- /**
2
- * Watcher Service — boots chain watchers and sends webhook callbacks.
3
- *
4
- * Payment flow:
5
- * 1. Watcher detects payment → handlePayment()
6
- * 2. Accumulate native amount (supports partial payments)
7
- * 3. When totalReceived >= expectedAmount AND confirmations >= required → confirmed + credit
8
- * 4. Every payment/confirmation change enqueues a webhook delivery
9
- * 5. Outbox processor retries failed deliveries with exponential backoff
10
- *
11
- * Amount comparison is ALWAYS in native crypto units (sats, wei, token base units).
12
- * The exchange rate is locked at charge creation — no live price comparison.
13
- */
14
- import type { DrizzleDb } from "../../db/index.js";
15
- import type { ICryptoChargeRepository } from "./charge-store.js";
16
- import type { IWatcherCursorStore } from "./cursor-store.js";
17
- import type { IPriceOracle } from "./oracle/types.js";
18
- import type { IPaymentMethodStore } from "./payment-method-store.js";
19
- export interface WatcherServiceOpts {
20
- db: DrizzleDb;
21
- chargeStore: ICryptoChargeRepository;
22
- methodStore: IPaymentMethodStore;
23
- cursorStore: IWatcherCursorStore;
24
- oracle: IPriceOracle;
25
- bitcoindUser?: string;
26
- bitcoindPassword?: string;
27
- pollIntervalMs?: number;
28
- deliveryIntervalMs?: number;
29
- log?: (msg: string, meta?: Record<string, unknown>) => void;
30
- /** Allowed callback URL prefixes. Default: ["https://"] — enforces HTTPS. */
31
- allowedCallbackPrefixes?: string[];
32
- /** Service key sent as Bearer token in webhook deliveries. */
33
- serviceKey?: string;
34
- }
35
- export interface PaymentPayload {
36
- txHash: string;
37
- confirmations: number;
38
- confirmationsRequired: number;
39
- amountReceivedCents: number;
40
- [key: string]: unknown;
41
- }
42
- /**
43
- * Handle a payment event. Accumulates partial payments in native units.
44
- * Fires webhook on every payment/confirmation change with canonical statuses.
45
- *
46
- * 3-phase webhook lifecycle:
47
- * 1. Tx first seen -> status: "partial", confirmations: 0
48
- * 2. Each new block -> status: "partial", confirmations: current
49
- * 3. Threshold reached + full payment -> status: "confirmed"
50
- *
51
- * @param nativeAmount — received amount in native base units (sats for BTC/DOGE, raw token units for ERC20).
52
- * Pass "0" for confirmation-only updates (no new payment, just more confirmations).
53
- */
54
- export declare function handlePayment(db: DrizzleDb, chargeStore: ICryptoChargeRepository, address: string, nativeAmount: string, payload: PaymentPayload, log: (msg: string, meta?: Record<string, unknown>) => void): Promise<void>;
55
- export declare function startWatchers(opts: WatcherServiceOpts): Promise<() => void>;