@wopr-network/platform-core 1.67.0 → 1.67.1

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 (136) hide show
  1. package/dist/billing/crypto/btc/checkout.d.ts +4 -0
  2. package/dist/billing/crypto/btc/checkout.js +1 -2
  3. package/dist/billing/crypto/btc/index.d.ts +0 -4
  4. package/dist/billing/crypto/btc/index.js +0 -2
  5. package/dist/billing/crypto/evm/__tests__/checkout.test.js +8 -11
  6. package/dist/billing/crypto/evm/__tests__/eth-checkout.test.js +15 -1
  7. package/dist/billing/crypto/evm/checkout.d.ts +2 -0
  8. package/dist/billing/crypto/evm/checkout.js +1 -2
  9. package/dist/billing/crypto/evm/eth-checkout.d.ts +13 -2
  10. package/dist/billing/crypto/evm/eth-checkout.js +2 -4
  11. package/dist/billing/crypto/evm/eth-settler.d.ts +1 -1
  12. package/dist/billing/crypto/evm/index.d.ts +2 -8
  13. package/dist/billing/crypto/evm/index.js +0 -3
  14. package/dist/billing/crypto/evm/types.d.ts +16 -0
  15. package/dist/billing/crypto/index.d.ts +1 -6
  16. package/dist/billing/crypto/index.js +2 -3
  17. package/dist/billing/crypto/types.d.ts +0 -43
  18. package/dist/billing/crypto/types.js +1 -24
  19. package/package.json +1 -5
  20. package/src/billing/crypto/btc/checkout.ts +3 -2
  21. package/src/billing/crypto/btc/index.ts +0 -4
  22. package/src/billing/crypto/evm/__tests__/checkout.test.ts +10 -12
  23. package/src/billing/crypto/evm/__tests__/eth-checkout.test.ts +17 -1
  24. package/src/billing/crypto/evm/__tests__/eth-settler.test.ts +1 -1
  25. package/src/billing/crypto/evm/checkout.ts +3 -2
  26. package/src/billing/crypto/evm/eth-checkout.ts +15 -6
  27. package/src/billing/crypto/evm/eth-settler.ts +1 -1
  28. package/src/billing/crypto/evm/index.ts +8 -7
  29. package/src/billing/crypto/evm/types.ts +17 -0
  30. package/src/billing/crypto/index.ts +14 -12
  31. package/src/billing/crypto/types.ts +0 -63
  32. package/dist/billing/crypto/__tests__/address-gen.test.d.ts +0 -1
  33. package/dist/billing/crypto/__tests__/address-gen.test.js +0 -219
  34. package/dist/billing/crypto/__tests__/key-server.test.d.ts +0 -1
  35. package/dist/billing/crypto/__tests__/key-server.test.js +0 -742
  36. package/dist/billing/crypto/__tests__/watcher-service.test.d.ts +0 -1
  37. package/dist/billing/crypto/__tests__/watcher-service.test.js +0 -174
  38. package/dist/billing/crypto/address-gen.d.ts +0 -24
  39. package/dist/billing/crypto/address-gen.js +0 -176
  40. package/dist/billing/crypto/btc/__tests__/watcher.test.d.ts +0 -1
  41. package/dist/billing/crypto/btc/__tests__/watcher.test.js +0 -170
  42. package/dist/billing/crypto/btc/watcher.d.ts +0 -44
  43. package/dist/billing/crypto/btc/watcher.js +0 -118
  44. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.d.ts +0 -1
  45. package/dist/billing/crypto/evm/__tests__/eth-watcher.test.js +0 -167
  46. package/dist/billing/crypto/evm/__tests__/watcher-confirmations.test.d.ts +0 -1
  47. package/dist/billing/crypto/evm/__tests__/watcher-confirmations.test.js +0 -159
  48. package/dist/billing/crypto/evm/__tests__/watcher.test.d.ts +0 -1
  49. package/dist/billing/crypto/evm/__tests__/watcher.test.js +0 -145
  50. package/dist/billing/crypto/evm/eth-watcher.d.ts +0 -66
  51. package/dist/billing/crypto/evm/eth-watcher.js +0 -121
  52. package/dist/billing/crypto/evm/watcher.d.ts +0 -51
  53. package/dist/billing/crypto/evm/watcher.js +0 -156
  54. package/dist/billing/crypto/key-server-entry.d.ts +0 -1
  55. package/dist/billing/crypto/key-server-entry.js +0 -122
  56. package/dist/billing/crypto/key-server.d.ts +0 -32
  57. package/dist/billing/crypto/key-server.js +0 -472
  58. package/dist/billing/crypto/oracle/__tests__/chainlink.test.d.ts +0 -1
  59. package/dist/billing/crypto/oracle/__tests__/chainlink.test.js +0 -83
  60. package/dist/billing/crypto/oracle/__tests__/coingecko.test.d.ts +0 -1
  61. package/dist/billing/crypto/oracle/__tests__/coingecko.test.js +0 -65
  62. package/dist/billing/crypto/oracle/__tests__/composite.test.d.ts +0 -1
  63. package/dist/billing/crypto/oracle/__tests__/composite.test.js +0 -48
  64. package/dist/billing/crypto/oracle/__tests__/convert.test.d.ts +0 -1
  65. package/dist/billing/crypto/oracle/__tests__/convert.test.js +0 -61
  66. package/dist/billing/crypto/oracle/__tests__/fixed.test.d.ts +0 -1
  67. package/dist/billing/crypto/oracle/__tests__/fixed.test.js +0 -20
  68. package/dist/billing/crypto/oracle/chainlink.d.ts +0 -26
  69. package/dist/billing/crypto/oracle/chainlink.js +0 -62
  70. package/dist/billing/crypto/oracle/coingecko.d.ts +0 -22
  71. package/dist/billing/crypto/oracle/coingecko.js +0 -71
  72. package/dist/billing/crypto/oracle/composite.d.ts +0 -14
  73. package/dist/billing/crypto/oracle/composite.js +0 -34
  74. package/dist/billing/crypto/oracle/convert.d.ts +0 -30
  75. package/dist/billing/crypto/oracle/convert.js +0 -51
  76. package/dist/billing/crypto/oracle/fixed.d.ts +0 -10
  77. package/dist/billing/crypto/oracle/fixed.js +0 -22
  78. package/dist/billing/crypto/oracle/index.d.ts +0 -9
  79. package/dist/billing/crypto/oracle/index.js +0 -6
  80. package/dist/billing/crypto/oracle/types.d.ts +0 -22
  81. package/dist/billing/crypto/oracle/types.js +0 -7
  82. package/dist/billing/crypto/plugin/__tests__/integration.test.d.ts +0 -1
  83. package/dist/billing/crypto/plugin/__tests__/integration.test.js +0 -58
  84. package/dist/billing/crypto/plugin/__tests__/interfaces.test.d.ts +0 -1
  85. package/dist/billing/crypto/plugin/__tests__/interfaces.test.js +0 -46
  86. package/dist/billing/crypto/plugin/__tests__/registry.test.d.ts +0 -1
  87. package/dist/billing/crypto/plugin/__tests__/registry.test.js +0 -49
  88. package/dist/billing/crypto/plugin/index.d.ts +0 -2
  89. package/dist/billing/crypto/plugin/index.js +0 -1
  90. package/dist/billing/crypto/plugin/interfaces.d.ts +0 -97
  91. package/dist/billing/crypto/plugin/interfaces.js +0 -2
  92. package/dist/billing/crypto/plugin/registry.d.ts +0 -8
  93. package/dist/billing/crypto/plugin/registry.js +0 -21
  94. package/dist/billing/crypto/plugin-watcher-service.d.ts +0 -32
  95. package/dist/billing/crypto/plugin-watcher-service.js +0 -113
  96. package/dist/billing/crypto/tron/__tests__/address-convert.test.d.ts +0 -1
  97. package/dist/billing/crypto/tron/__tests__/address-convert.test.js +0 -55
  98. package/dist/billing/crypto/tron/address-convert.d.ts +0 -14
  99. package/dist/billing/crypto/tron/address-convert.js +0 -93
  100. package/dist/billing/crypto/watcher-service.d.ts +0 -55
  101. package/dist/billing/crypto/watcher-service.js +0 -438
  102. package/src/billing/crypto/__tests__/address-gen.test.ts +0 -264
  103. package/src/billing/crypto/__tests__/key-server.test.ts +0 -823
  104. package/src/billing/crypto/__tests__/watcher-service.test.ts +0 -242
  105. package/src/billing/crypto/address-gen.ts +0 -185
  106. package/src/billing/crypto/btc/__tests__/watcher.test.ts +0 -201
  107. package/src/billing/crypto/btc/watcher.ts +0 -161
  108. package/src/billing/crypto/evm/__tests__/eth-watcher.test.ts +0 -190
  109. package/src/billing/crypto/evm/__tests__/watcher-confirmations.test.ts +0 -191
  110. package/src/billing/crypto/evm/__tests__/watcher.test.ts +0 -167
  111. package/src/billing/crypto/evm/eth-watcher.ts +0 -182
  112. package/src/billing/crypto/evm/watcher.ts +0 -204
  113. package/src/billing/crypto/key-server-entry.ts +0 -144
  114. package/src/billing/crypto/key-server.ts +0 -617
  115. package/src/billing/crypto/oracle/__tests__/chainlink.test.ts +0 -107
  116. package/src/billing/crypto/oracle/__tests__/coingecko.test.ts +0 -75
  117. package/src/billing/crypto/oracle/__tests__/composite.test.ts +0 -61
  118. package/src/billing/crypto/oracle/__tests__/convert.test.ts +0 -74
  119. package/src/billing/crypto/oracle/__tests__/fixed.test.ts +0 -23
  120. package/src/billing/crypto/oracle/chainlink.ts +0 -86
  121. package/src/billing/crypto/oracle/coingecko.ts +0 -96
  122. package/src/billing/crypto/oracle/composite.ts +0 -35
  123. package/src/billing/crypto/oracle/convert.ts +0 -53
  124. package/src/billing/crypto/oracle/fixed.ts +0 -25
  125. package/src/billing/crypto/oracle/index.ts +0 -9
  126. package/src/billing/crypto/oracle/types.ts +0 -28
  127. package/src/billing/crypto/plugin/__tests__/integration.test.ts +0 -64
  128. package/src/billing/crypto/plugin/__tests__/interfaces.test.ts +0 -51
  129. package/src/billing/crypto/plugin/__tests__/registry.test.ts +0 -58
  130. package/src/billing/crypto/plugin/index.ts +0 -17
  131. package/src/billing/crypto/plugin/interfaces.ts +0 -106
  132. package/src/billing/crypto/plugin/registry.ts +0 -26
  133. package/src/billing/crypto/plugin-watcher-service.ts +0 -148
  134. package/src/billing/crypto/tron/__tests__/address-convert.test.ts +0 -67
  135. package/src/billing/crypto/tron/address-convert.ts +0 -89
  136. package/src/billing/crypto/watcher-service.ts +0 -549
@@ -1,121 +0,0 @@
1
- import { nativeToCents } from "../oracle/convert.js";
2
- /**
3
- * Native ETH transfer watcher.
4
- *
5
- * Unlike the ERC-20 EvmWatcher which uses eth_getLogs for Transfer events,
6
- * this scans blocks for transactions where `to` matches a watched deposit
7
- * address and `value > 0`.
8
- *
9
- * Scans up to latest block (not just confirmed) to detect pending txs.
10
- * Emits events on each confirmation increment. Only advances cursor
11
- * past fully-confirmed blocks.
12
- */
13
- export class EthWatcher {
14
- _cursor;
15
- chain;
16
- rpc;
17
- oracle;
18
- onPayment;
19
- confirmations;
20
- cursorStore;
21
- watcherId;
22
- _watchedAddresses;
23
- constructor(opts) {
24
- this.chain = opts.chain;
25
- this.rpc = opts.rpcCall;
26
- this.oracle = opts.oracle;
27
- this._cursor = opts.fromBlock;
28
- this.onPayment = opts.onPayment;
29
- this.confirmations = opts.confirmations;
30
- this.cursorStore = opts.cursorStore;
31
- this.watcherId = `eth:${opts.chain}`;
32
- this._watchedAddresses = new Set((opts.watchedAddresses ?? []).map((a) => a.toLowerCase()));
33
- }
34
- /** Load cursor from DB. Call once at startup before first poll. */
35
- async init() {
36
- if (!this.cursorStore)
37
- return;
38
- const saved = await this.cursorStore.get(this.watcherId);
39
- if (saved !== null)
40
- this._cursor = saved;
41
- }
42
- setWatchedAddresses(addresses) {
43
- this._watchedAddresses = new Set(addresses.map((a) => a.toLowerCase()));
44
- }
45
- get cursor() {
46
- return this._cursor;
47
- }
48
- /**
49
- * Poll for native ETH transfers to watched addresses, including unconfirmed blocks.
50
- *
51
- * Scans from cursor to latest block. Emits events with current confirmation count.
52
- * Re-emits on each confirmation increment. Only advances cursor past fully-confirmed blocks.
53
- */
54
- async poll() {
55
- if (this._watchedAddresses.size === 0)
56
- return;
57
- const latestHex = (await this.rpc("eth_blockNumber", []));
58
- const latest = Number.parseInt(latestHex, 16);
59
- const confirmed = latest - this.confirmations;
60
- if (latest < this._cursor)
61
- return;
62
- const { priceMicros } = await this.oracle.getPrice("ETH");
63
- // Scan up to latest (not just confirmed) to detect pending txs.
64
- // Fetch blocks in batches to avoid bursting RPC rate limits on fast chains (e.g. Tron 3s blocks).
65
- const BATCH_SIZE = 5;
66
- for (let batchStart = this._cursor; batchStart <= latest; batchStart += BATCH_SIZE) {
67
- const batchEnd = Math.min(batchStart + BATCH_SIZE - 1, latest);
68
- const blockNums = Array.from({ length: batchEnd - batchStart + 1 }, (_, i) => batchStart + i);
69
- const blocks = await Promise.all(blockNums.map((bn) => this.rpc("eth_getBlockByNumber", [`0x${bn.toString(16)}`, true]).then((b) => ({ blockNum: bn, block: b, error: null }), (err) => ({ blockNum: bn, block: null, error: err }))));
70
- // Stop processing at the first failed block so the cursor doesn't advance past it.
71
- const firstFailIdx = blocks.findIndex((b) => b.error !== null || !b.block);
72
- const safeBlocks = firstFailIdx === -1 ? blocks : blocks.slice(0, firstFailIdx);
73
- for (const { blockNum, block } of safeBlocks) {
74
- if (!block)
75
- break;
76
- const confs = latest - blockNum;
77
- for (const tx of block.transactions) {
78
- if (!tx.to)
79
- continue;
80
- const to = tx.to.toLowerCase();
81
- if (!this._watchedAddresses.has(to))
82
- continue;
83
- const valueWei = BigInt(tx.value);
84
- if (valueWei === 0n)
85
- continue;
86
- // Skip if we already emitted at this confirmation count
87
- if (this.cursorStore) {
88
- const lastConf = await this.cursorStore.getConfirmationCount(this.watcherId, tx.hash);
89
- if (lastConf !== null && confs <= lastConf)
90
- continue;
91
- }
92
- const amountUsdCents = nativeToCents(valueWei, priceMicros, 18);
93
- const event = {
94
- chain: this.chain,
95
- from: tx.from.toLowerCase(),
96
- to,
97
- valueWei: valueWei.toString(),
98
- amountUsdCents,
99
- txHash: tx.hash,
100
- blockNumber: blockNum,
101
- confirmations: confs,
102
- confirmationsRequired: this.confirmations,
103
- };
104
- await this.onPayment(event);
105
- if (this.cursorStore) {
106
- await this.cursorStore.saveConfirmationCount(this.watcherId, tx.hash, confs);
107
- }
108
- }
109
- // Only advance cursor past fully-confirmed blocks
110
- if (blockNum <= confirmed) {
111
- this._cursor = blockNum + 1;
112
- if (this.cursorStore) {
113
- await this.cursorStore.save(this.watcherId, this._cursor);
114
- }
115
- }
116
- }
117
- if (firstFailIdx !== -1)
118
- break;
119
- }
120
- }
121
- }
@@ -1,51 +0,0 @@
1
- import type { IWatcherCursorStore } from "../cursor-store.js";
2
- import type { EvmChain, EvmPaymentEvent, StablecoinToken } from "./types.js";
3
- type RpcCall = (method: string, params: unknown[]) => Promise<unknown>;
4
- export interface EvmWatcherOpts {
5
- chain: EvmChain;
6
- token: StablecoinToken;
7
- rpcCall: RpcCall;
8
- fromBlock: number;
9
- onPayment: (event: EvmPaymentEvent) => void | Promise<void>;
10
- /** Active deposit addresses to watch. Filters eth_getLogs by topic[2] (to address). */
11
- watchedAddresses?: string[];
12
- cursorStore?: IWatcherCursorStore;
13
- /** Contract address for the ERC20 token (from DB). */
14
- contractAddress: string;
15
- /** Token decimals (from DB). */
16
- decimals: number;
17
- /** Required confirmations (from DB). */
18
- confirmations: number;
19
- }
20
- export declare class EvmWatcher {
21
- private _cursor;
22
- private readonly chain;
23
- private readonly token;
24
- private readonly rpc;
25
- private readonly onPayment;
26
- private readonly confirmations;
27
- private readonly contractAddress;
28
- private readonly decimals;
29
- private readonly cursorStore?;
30
- private readonly watcherId;
31
- private _watchedAddresses;
32
- constructor(opts: EvmWatcherOpts);
33
- /** Load cursor from DB. Call once at startup before first poll. */
34
- init(): Promise<void>;
35
- /** Update the set of watched deposit addresses (e.g. after a new checkout). */
36
- setWatchedAddresses(addresses: string[]): void;
37
- get cursor(): number;
38
- /**
39
- * Poll for Transfer events, including pending (unconfirmed) blocks.
40
- *
41
- * Two-phase scan:
42
- * 1. Scan cursor..latest for new/updated txs, emit with current confirmation count
43
- * 2. Re-check pending txs automatically since cursor doesn't advance past unconfirmed blocks
44
- *
45
- * Cursor only advances past fully-confirmed blocks.
46
- */
47
- poll(): Promise<void>;
48
- }
49
- /** Create an RPC caller for a given URL (plain JSON-RPC over fetch). */
50
- export declare function createRpcCaller(rpcUrl: string, extraHeaders?: Record<string, string>): RpcCall;
51
- export {};
@@ -1,156 +0,0 @@
1
- import { centsFromTokenAmount } from "./config.js";
2
- const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
3
- export class EvmWatcher {
4
- _cursor;
5
- chain;
6
- token;
7
- rpc;
8
- onPayment;
9
- confirmations;
10
- contractAddress;
11
- decimals;
12
- cursorStore;
13
- watcherId;
14
- _watchedAddresses;
15
- constructor(opts) {
16
- this.chain = opts.chain;
17
- this.token = opts.token;
18
- this.rpc = opts.rpcCall;
19
- this._cursor = opts.fromBlock;
20
- this.onPayment = opts.onPayment;
21
- this.cursorStore = opts.cursorStore;
22
- this.watcherId = `evm:${opts.chain}:${opts.token}`;
23
- this._watchedAddresses = (opts.watchedAddresses ?? []).map((a) => a.toLowerCase());
24
- this.confirmations = opts.confirmations;
25
- this.contractAddress = opts.contractAddress.toLowerCase();
26
- this.decimals = opts.decimals;
27
- }
28
- /** Load cursor from DB. Call once at startup before first poll. */
29
- async init() {
30
- if (!this.cursorStore)
31
- return;
32
- const saved = await this.cursorStore.get(this.watcherId);
33
- if (saved !== null)
34
- this._cursor = saved;
35
- }
36
- /** Update the set of watched deposit addresses (e.g. after a new checkout). */
37
- setWatchedAddresses(addresses) {
38
- this._watchedAddresses = addresses.map((a) => a.toLowerCase());
39
- }
40
- get cursor() {
41
- return this._cursor;
42
- }
43
- /**
44
- * Poll for Transfer events, including pending (unconfirmed) blocks.
45
- *
46
- * Two-phase scan:
47
- * 1. Scan cursor..latest for new/updated txs, emit with current confirmation count
48
- * 2. Re-check pending txs automatically since cursor doesn't advance past unconfirmed blocks
49
- *
50
- * Cursor only advances past fully-confirmed blocks.
51
- */
52
- async poll() {
53
- if (this._watchedAddresses.length === 0)
54
- return; // nothing to watch
55
- const latestHex = (await this.rpc("eth_blockNumber", []));
56
- const latest = Number.parseInt(latestHex, 16);
57
- const confirmed = latest - this.confirmations;
58
- if (latest < this._cursor)
59
- return;
60
- // Filter by topic[2] (to address) when watched addresses are set.
61
- const toFilter = this._watchedAddresses.length > 0
62
- ? this._watchedAddresses.map((a) => `0x000000000000000000000000${a.slice(2)}`)
63
- : null;
64
- // Scan from cursor to latest (not just confirmed) to detect pending txs
65
- const logs = (await this.rpc("eth_getLogs", [
66
- {
67
- address: this.contractAddress,
68
- topics: [TRANSFER_TOPIC, null, toFilter],
69
- fromBlock: `0x${this._cursor.toString(16)}`,
70
- toBlock: `0x${latest.toString(16)}`,
71
- },
72
- ]));
73
- // Group logs by block
74
- const logsByBlock = new Map();
75
- for (const log of logs) {
76
- const bn = Number.parseInt(log.blockNumber, 16);
77
- const arr = logsByBlock.get(bn);
78
- if (arr)
79
- arr.push(log);
80
- else
81
- logsByBlock.set(bn, [log]);
82
- }
83
- // Process all blocks (including unconfirmed), emit with confirmation count
84
- const blockNums = [...logsByBlock.keys()].sort((a, b) => a - b);
85
- for (const blockNum of blockNums) {
86
- const confs = latest - blockNum;
87
- for (const log of logsByBlock.get(blockNum) ?? []) {
88
- const txKey = `${log.transactionHash}:${log.logIndex}`;
89
- // Skip if we already emitted at this confirmation count
90
- if (this.cursorStore) {
91
- const lastConf = await this.cursorStore.getConfirmationCount(this.watcherId, txKey);
92
- if (lastConf !== null && confs <= lastConf)
93
- continue;
94
- }
95
- const to = `0x${log.topics[2].slice(26)}`.toLowerCase();
96
- const from = `0x${log.topics[1].slice(26)}`.toLowerCase();
97
- const rawAmount = BigInt(log.data);
98
- const amountUsdCents = centsFromTokenAmount(rawAmount, this.decimals);
99
- const event = {
100
- chain: this.chain,
101
- token: this.token,
102
- from,
103
- to,
104
- rawAmount: rawAmount.toString(),
105
- amountUsdCents,
106
- txHash: log.transactionHash,
107
- blockNumber: blockNum,
108
- logIndex: Number.parseInt(log.logIndex, 16),
109
- confirmations: confs,
110
- confirmationsRequired: this.confirmations,
111
- };
112
- await this.onPayment(event);
113
- // Track confirmation count
114
- if (this.cursorStore) {
115
- await this.cursorStore.saveConfirmationCount(this.watcherId, txKey, confs);
116
- }
117
- }
118
- // Only advance cursor past fully-confirmed blocks
119
- if (blockNum <= confirmed) {
120
- this._cursor = blockNum + 1;
121
- if (this.cursorStore) {
122
- await this.cursorStore.save(this.watcherId, this._cursor);
123
- }
124
- }
125
- }
126
- // Advance cursor if no logs found but confirmed blocks exist
127
- if (blockNums.length === 0 && confirmed >= this._cursor) {
128
- this._cursor = confirmed + 1;
129
- if (this.cursorStore) {
130
- await this.cursorStore.save(this.watcherId, this._cursor);
131
- }
132
- }
133
- }
134
- }
135
- /** Create an RPC caller for a given URL (plain JSON-RPC over fetch). */
136
- export function createRpcCaller(rpcUrl, extraHeaders) {
137
- let id = 0;
138
- const headers = { "Content-Type": "application/json", ...extraHeaders };
139
- return async (method, params) => {
140
- const res = await fetch(rpcUrl, {
141
- method: "POST",
142
- headers,
143
- body: JSON.stringify({ jsonrpc: "2.0", id: ++id, method, params }),
144
- });
145
- if (!res.ok) {
146
- const body = await res.text().catch(() => "");
147
- const hasApiKey = "TRON-PRO-API-KEY" in headers;
148
- console.error(`[rpc] ${method} ${res.status} auth=${hasApiKey} url=${rpcUrl.replace(/apikey=[^&]+/, "apikey=***")} body=${body.slice(0, 200)}`);
149
- throw new Error(`RPC ${method} failed: ${res.status}`);
150
- }
151
- const data = (await res.json());
152
- if (data.error)
153
- throw new Error(`RPC ${method} error: ${data.error.message}`);
154
- return data.result;
155
- };
156
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,122 +0,0 @@
1
- /**
2
- * Standalone entry point for the crypto key server.
3
- *
4
- * Deploys on the chain server (pay.wopr.bot:3100).
5
- * Boots: postgres → migrations → key server routes → watchers → serve.
6
- *
7
- * Usage: node dist/billing/crypto/key-server-entry.js
8
- */
9
- /* biome-ignore-all lint/suspicious/noConsole: standalone entry point */
10
- import { serve } from "@hono/node-server";
11
- import { bitcoinPlugin, dogecoinPlugin, evmPlugin, litecoinPlugin, solanaPlugin, tronPlugin, } from "@wopr-network/crypto-plugins";
12
- import { drizzle } from "drizzle-orm/node-postgres";
13
- import { migrate } from "drizzle-orm/node-postgres/migrator";
14
- import pg from "pg";
15
- import * as schema from "../../db/schema/index.js";
16
- import { DrizzleCryptoChargeRepository } from "./charge-store.js";
17
- import { DrizzleWatcherCursorStore } from "./cursor-store.js";
18
- import { createRpcCaller } from "./evm/watcher.js";
19
- import { createKeyServerApp } from "./key-server.js";
20
- import { ChainlinkOracle } from "./oracle/chainlink.js";
21
- import { CoinGeckoOracle } from "./oracle/coingecko.js";
22
- import { CompositeOracle } from "./oracle/composite.js";
23
- import { FixedPriceOracle } from "./oracle/fixed.js";
24
- import { DrizzlePaymentMethodStore } from "./payment-method-store.js";
25
- import { PluginRegistry } from "./plugin/registry.js";
26
- import { startPluginWatchers } from "./plugin-watcher-service.js";
27
- import { startWatchers } from "./watcher-service.js";
28
- const PORT = Number(process.env.PORT ?? "3100");
29
- const DATABASE_URL = process.env.DATABASE_URL;
30
- const SERVICE_KEY = process.env.SERVICE_KEY;
31
- const ADMIN_TOKEN = process.env.ADMIN_TOKEN;
32
- const BITCOIND_USER = process.env.BITCOIND_USER ?? "btcpay";
33
- const BITCOIND_PASSWORD = process.env.BITCOIND_PASSWORD ?? "";
34
- const BASE_RPC_URL = process.env.BASE_RPC_URL ?? "https://mainnet.base.org";
35
- if (!DATABASE_URL) {
36
- console.error("DATABASE_URL is required");
37
- process.exit(1);
38
- }
39
- async function main() {
40
- const pool = new pg.Pool({ connectionString: DATABASE_URL });
41
- // Run migrations FIRST, before creating schema-typed db
42
- console.log("[crypto-key-server] Running migrations...");
43
- await migrate(drizzle(pool), { migrationsFolder: "./drizzle/migrations" });
44
- // Now create the schema-typed db (columns guaranteed to exist)
45
- console.log("[crypto-key-server] Connecting...");
46
- const db = drizzle(pool, { schema });
47
- const chargeStore = new DrizzleCryptoChargeRepository(db);
48
- const methodStore = new DrizzlePaymentMethodStore(db);
49
- // Composite oracle: Chainlink on-chain (BTC, ETH on Base) + CoinGecko fallback (DOGE, LTC, etc.)
50
- // Every volatile asset needs reliable USD pricing — the ledger credits nanodollars.
51
- const chainlink = BASE_RPC_URL
52
- ? new ChainlinkOracle({ rpcCall: createRpcCaller(BASE_RPC_URL) })
53
- : new FixedPriceOracle();
54
- // Build token→CoinGecko ID map from DB (zero-deploy chain additions)
55
- const allMethods = await methodStore.listAll();
56
- const dbTokenIds = {};
57
- for (const m of allMethods) {
58
- if (m.oracleAssetId)
59
- dbTokenIds[m.token] = m.oracleAssetId;
60
- }
61
- const coingecko = new CoinGeckoOracle({ tokenIds: dbTokenIds });
62
- const oracle = new CompositeOracle(chainlink, coingecko);
63
- // Build plugin registry — one plugin per chain family
64
- const registry = new PluginRegistry();
65
- registry.register(bitcoinPlugin);
66
- registry.register(litecoinPlugin);
67
- registry.register(dogecoinPlugin);
68
- registry.register(evmPlugin);
69
- registry.register(tronPlugin);
70
- registry.register(solanaPlugin);
71
- console.log(`[crypto-key-server] Registered ${registry.list().length} chain plugins:`, registry.list().map((p) => p.pluginId));
72
- const app = createKeyServerApp({
73
- db,
74
- chargeStore,
75
- methodStore,
76
- oracle,
77
- serviceKey: SERVICE_KEY,
78
- adminToken: ADMIN_TOKEN,
79
- registry,
80
- });
81
- // Boot plugin-driven watchers — polls for payments, sends webhooks.
82
- // Falls back to legacy startWatchers() if USE_LEGACY_WATCHERS=1 is set.
83
- const cursorStore = new DrizzleWatcherCursorStore(db);
84
- const useLegacy = process.env.USE_LEGACY_WATCHERS === "1";
85
- const stopWatchers = useLegacy
86
- ? await startWatchers({
87
- db,
88
- chargeStore,
89
- methodStore,
90
- cursorStore,
91
- oracle,
92
- bitcoindUser: BITCOIND_USER,
93
- bitcoindPassword: BITCOIND_PASSWORD,
94
- serviceKey: SERVICE_KEY,
95
- log: (msg, meta) => console.log(`[watcher] ${msg}`, meta ?? ""),
96
- })
97
- : await startPluginWatchers({
98
- db,
99
- chargeStore,
100
- methodStore,
101
- cursorStore,
102
- oracle,
103
- registry,
104
- log: (msg, meta) => console.log(`[watcher] ${msg}`, meta ?? ""),
105
- });
106
- const server = serve({ fetch: app.fetch, port: PORT });
107
- console.log(`[crypto-key-server] Listening on :${PORT}`);
108
- // Graceful shutdown — stop accepting requests, drain watchers, close pool
109
- const shutdown = async () => {
110
- console.log("[crypto-key-server] Shutting down...");
111
- stopWatchers();
112
- server.close();
113
- await pool.end();
114
- process.exit(0);
115
- };
116
- process.on("SIGTERM", shutdown);
117
- process.on("SIGINT", shutdown);
118
- }
119
- main().catch((err) => {
120
- console.error("[crypto-key-server] Fatal:", err);
121
- process.exit(1);
122
- });
@@ -1,32 +0,0 @@
1
- /**
2
- * Crypto Key Server — shared address derivation + charge management.
3
- *
4
- * Deploys on the chain server (pay.wopr.bot) alongside bitcoind.
5
- * Products don't run watchers or hold xpubs. They request addresses
6
- * and receive webhooks.
7
- *
8
- * ~200 lines of new code wrapping platform-core's existing crypto modules.
9
- */
10
- import { Hono } from "hono";
11
- import type { DrizzleDb } from "../../db/index.js";
12
- import type { ICryptoChargeRepository } from "./charge-store.js";
13
- import type { IPriceOracle } from "./oracle/types.js";
14
- import type { IPaymentMethodStore } from "./payment-method-store.js";
15
- import type { PluginRegistry } from "./plugin/registry.js";
16
- export interface KeyServerDeps {
17
- db: DrizzleDb;
18
- chargeStore: ICryptoChargeRepository;
19
- methodStore: IPaymentMethodStore;
20
- oracle: IPriceOracle;
21
- /** Bearer token for product API routes. If unset, auth is disabled. */
22
- serviceKey?: string;
23
- /** Bearer token for admin routes. If unset, admin routes are disabled. */
24
- adminToken?: string;
25
- /** Plugin registry for address encoding. Falls back to address-gen.ts when absent. */
26
- registry?: PluginRegistry;
27
- }
28
- /**
29
- * Create the Hono app for the crypto key server.
30
- * Mount this on the chain server at the root.
31
- */
32
- export declare function createKeyServerApp(deps: KeyServerDeps): Hono;