@wopr-network/platform-core 1.66.1 → 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 -363
  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 -348
  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 -395
  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 -444
  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,159 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { EvmWatcher } from "../watcher.js";
3
- const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
4
- function mockTransferLog(to, amount, blockNumber) {
5
- return {
6
- address: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
7
- topics: [
8
- TRANSFER_TOPIC,
9
- `0x${"00".repeat(12)}${"ab".repeat(20)}`,
10
- `0x${"00".repeat(12)}${to.slice(2).toLowerCase()}`,
11
- ],
12
- data: `0x${amount.toString(16).padStart(64, "0")}`,
13
- blockNumber: `0x${blockNumber.toString(16)}`,
14
- transactionHash: `0x${"ff".repeat(32)}`,
15
- logIndex: "0x0",
16
- };
17
- }
18
- function makeCursorStore() {
19
- const cursors = new Map();
20
- return {
21
- get: vi.fn().mockImplementation(async (id) => cursors.get(id) ?? null),
22
- save: vi.fn().mockImplementation(async (id, val) => {
23
- cursors.set(id, val);
24
- }),
25
- hasProcessedTx: vi.fn().mockResolvedValue(false),
26
- markProcessedTx: vi.fn().mockResolvedValue(undefined),
27
- getConfirmationCount: vi.fn().mockResolvedValue(null),
28
- saveConfirmationCount: vi.fn().mockResolvedValue(undefined),
29
- };
30
- }
31
- describe("EvmWatcher — intermediate confirmations", () => {
32
- it("emits events with confirmation count", async () => {
33
- const toAddr = `0x${"cc".repeat(20)}`;
34
- const events = [];
35
- // Base has confirmations: 1. Latest block is 105. Log at block 103 -> 2 confirmations.
36
- const mockRpc = vi
37
- .fn()
38
- .mockResolvedValueOnce(`0x${(105).toString(16)}`) // eth_blockNumber
39
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 103)]); // eth_getLogs
40
- const watcher = new EvmWatcher({
41
- chain: "base",
42
- token: "USDC",
43
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
44
- decimals: 6,
45
- confirmations: 1,
46
- rpcCall: mockRpc,
47
- fromBlock: 100,
48
- watchedAddresses: [toAddr],
49
- cursorStore: makeCursorStore(),
50
- onPayment: (evt) => {
51
- events.push(evt);
52
- },
53
- });
54
- await watcher.poll();
55
- expect(events).toHaveLength(1);
56
- expect(events[0].confirmations).toBe(2); // 105 - 103
57
- expect(events[0].confirmationsRequired).toBe(1); // Base chain config
58
- });
59
- it("skips event when confirmation count unchanged", async () => {
60
- const toAddr = `0x${"cc".repeat(20)}`;
61
- const events = [];
62
- const cursorStore = makeCursorStore();
63
- cursorStore.getConfirmationCount.mockResolvedValue(2);
64
- const mockRpc = vi
65
- .fn()
66
- .mockResolvedValueOnce(`0x${(105).toString(16)}`)
67
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 103)]);
68
- const watcher = new EvmWatcher({
69
- chain: "base",
70
- token: "USDC",
71
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
72
- decimals: 6,
73
- confirmations: 1,
74
- rpcCall: mockRpc,
75
- fromBlock: 100,
76
- watchedAddresses: [toAddr],
77
- cursorStore,
78
- onPayment: (evt) => {
79
- events.push(evt);
80
- },
81
- });
82
- await watcher.poll();
83
- expect(events).toHaveLength(0);
84
- });
85
- it("re-emits when confirmations increase", async () => {
86
- const toAddr = `0x${"cc".repeat(20)}`;
87
- const events = [];
88
- const cursorStore = makeCursorStore();
89
- cursorStore.getConfirmationCount.mockResolvedValue(1);
90
- const mockRpc = vi
91
- .fn()
92
- .mockResolvedValueOnce(`0x${(105).toString(16)}`)
93
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 103)]);
94
- const watcher = new EvmWatcher({
95
- chain: "base",
96
- token: "USDC",
97
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
98
- decimals: 6,
99
- confirmations: 1,
100
- rpcCall: mockRpc,
101
- fromBlock: 100,
102
- watchedAddresses: [toAddr],
103
- cursorStore,
104
- onPayment: (evt) => {
105
- events.push(evt);
106
- },
107
- });
108
- await watcher.poll();
109
- expect(events).toHaveLength(1);
110
- expect(events[0].confirmations).toBe(2);
111
- });
112
- it("includes confirmationsRequired from chain config", async () => {
113
- const toAddr = `0x${"cc".repeat(20)}`;
114
- const events = [];
115
- const mockRpc = vi
116
- .fn()
117
- .mockResolvedValueOnce(`0x${(110).toString(16)}`)
118
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 105)]);
119
- const watcher = new EvmWatcher({
120
- chain: "base",
121
- token: "USDC",
122
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
123
- decimals: 6,
124
- confirmations: 1,
125
- rpcCall: mockRpc,
126
- fromBlock: 100,
127
- watchedAddresses: [toAddr],
128
- cursorStore: makeCursorStore(),
129
- onPayment: (evt) => {
130
- events.push(evt);
131
- },
132
- });
133
- await watcher.poll();
134
- expect(events).toHaveLength(1);
135
- expect(events[0].confirmationsRequired).toBe(1); // Base chain config
136
- });
137
- it("saves confirmation count after emitting", async () => {
138
- const toAddr = `0x${"cc".repeat(20)}`;
139
- const cursorStore = makeCursorStore();
140
- const mockRpc = vi
141
- .fn()
142
- .mockResolvedValueOnce(`0x${(105).toString(16)}`)
143
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 103)]);
144
- const watcher = new EvmWatcher({
145
- chain: "base",
146
- token: "USDC",
147
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
148
- decimals: 6,
149
- confirmations: 1,
150
- rpcCall: mockRpc,
151
- fromBlock: 100,
152
- watchedAddresses: [toAddr],
153
- cursorStore,
154
- onPayment: () => { },
155
- });
156
- await watcher.poll();
157
- expect(cursorStore.saveConfirmationCount).toHaveBeenCalledWith(expect.any(String), expect.stringContaining("0x"), 2);
158
- });
159
- });
@@ -1 +0,0 @@
1
- export {};
@@ -1,145 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import { EvmWatcher } from "../watcher.js";
3
- const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
4
- function mockTransferLog(to, amount, blockNumber) {
5
- return {
6
- address: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913",
7
- topics: [
8
- TRANSFER_TOPIC,
9
- `0x${"00".repeat(12)}${"ab".repeat(20)}`, // from (padded)
10
- `0x${"00".repeat(12)}${to.slice(2).toLowerCase()}`, // to (padded)
11
- ],
12
- data: `0x${amount.toString(16).padStart(64, "0")}`,
13
- blockNumber: `0x${blockNumber.toString(16)}`,
14
- transactionHash: `0x${"ff".repeat(32)}`,
15
- logIndex: "0x0",
16
- };
17
- }
18
- describe("EvmWatcher", () => {
19
- it("parses Transfer log into EvmPaymentEvent", async () => {
20
- const toAddr = `0x${"cc".repeat(20)}`;
21
- const events = [];
22
- const mockRpc = vi
23
- .fn()
24
- .mockResolvedValueOnce(`0x${(102).toString(16)}`)
25
- .mockResolvedValueOnce([mockTransferLog(toAddr, 10000000n, 99)]);
26
- const watcher = new EvmWatcher({
27
- chain: "base",
28
- token: "USDC",
29
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
30
- decimals: 6,
31
- confirmations: 1,
32
- rpcCall: mockRpc,
33
- fromBlock: 99,
34
- watchedAddresses: [toAddr],
35
- onPayment: (evt) => {
36
- events.push(evt);
37
- },
38
- });
39
- await watcher.poll();
40
- expect(events).toHaveLength(1);
41
- expect(events[0].amountUsdCents).toBe(1000);
42
- expect(events[0].to).toMatch(/^0x/);
43
- });
44
- it("advances cursor after processing", async () => {
45
- const mockRpc = vi
46
- .fn()
47
- .mockResolvedValueOnce(`0x${(200).toString(16)}`)
48
- .mockResolvedValueOnce([]);
49
- const watcher = new EvmWatcher({
50
- chain: "base",
51
- token: "USDC",
52
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
53
- decimals: 6,
54
- confirmations: 1,
55
- rpcCall: mockRpc,
56
- fromBlock: 100,
57
- watchedAddresses: ["0xdeadbeef"],
58
- onPayment: vi.fn(),
59
- });
60
- await watcher.poll();
61
- expect(watcher.cursor).toBeGreaterThan(100);
62
- });
63
- it("skips blocks not yet confirmed", async () => {
64
- const events = [];
65
- // latest = 50, cursor = 50 → latest < cursor is false, but range is empty (50..50)
66
- // With intermediate confirmations, we still scan the range but find no logs
67
- const mockRpc = vi
68
- .fn()
69
- .mockResolvedValueOnce(`0x${(50).toString(16)}`) // eth_blockNumber
70
- .mockResolvedValueOnce([]); // eth_getLogs (empty)
71
- const watcher = new EvmWatcher({
72
- chain: "base",
73
- token: "USDC",
74
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
75
- decimals: 6,
76
- confirmations: 1,
77
- rpcCall: mockRpc,
78
- fromBlock: 50,
79
- watchedAddresses: ["0xdeadbeef"],
80
- onPayment: (evt) => {
81
- events.push(evt);
82
- },
83
- });
84
- await watcher.poll();
85
- expect(events).toHaveLength(0);
86
- });
87
- it("processes multiple logs in one poll", async () => {
88
- const addr1 = `0x${"aa".repeat(20)}`;
89
- const addr2 = `0x${"bb".repeat(20)}`;
90
- const events = [];
91
- const mockRpc = vi
92
- .fn()
93
- .mockResolvedValueOnce(`0x${(110).toString(16)}`)
94
- .mockResolvedValueOnce([mockTransferLog(addr1, 5000000n, 105), mockTransferLog(addr2, 20000000n, 107)]);
95
- const watcher = new EvmWatcher({
96
- chain: "base",
97
- token: "USDC",
98
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
99
- decimals: 6,
100
- confirmations: 1,
101
- rpcCall: mockRpc,
102
- fromBlock: 100,
103
- watchedAddresses: [addr1, addr2],
104
- onPayment: (evt) => {
105
- events.push(evt);
106
- },
107
- });
108
- await watcher.poll();
109
- expect(events).toHaveLength(2);
110
- expect(events[0].amountUsdCents).toBe(500);
111
- expect(events[1].amountUsdCents).toBe(2000);
112
- });
113
- it("does nothing when no new blocks", async () => {
114
- const mockRpc = vi.fn().mockResolvedValueOnce(`0x${(99).toString(16)}`);
115
- const watcher = new EvmWatcher({
116
- chain: "base",
117
- token: "USDC",
118
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
119
- decimals: 6,
120
- confirmations: 1,
121
- rpcCall: mockRpc,
122
- fromBlock: 100,
123
- watchedAddresses: ["0xdeadbeef"],
124
- onPayment: vi.fn(),
125
- });
126
- await watcher.poll();
127
- expect(watcher.cursor).toBe(100);
128
- expect(mockRpc).toHaveBeenCalledTimes(1);
129
- });
130
- it("early-returns when no watched addresses are set", async () => {
131
- const mockRpc = vi.fn();
132
- const watcher = new EvmWatcher({
133
- chain: "base",
134
- token: "USDC",
135
- contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
136
- decimals: 6,
137
- confirmations: 1,
138
- rpcCall: mockRpc,
139
- fromBlock: 0,
140
- onPayment: vi.fn(),
141
- });
142
- await watcher.poll();
143
- expect(mockRpc).not.toHaveBeenCalled(); // no RPC calls at all
144
- });
145
- });
@@ -1,66 +0,0 @@
1
- import type { IWatcherCursorStore } from "../cursor-store.js";
2
- import type { IPriceOracle } from "../oracle/types.js";
3
- import type { EvmChain } from "./types.js";
4
- type RpcCall = (method: string, params: unknown[]) => Promise<unknown>;
5
- /** Event emitted on each confirmation increment for a native ETH deposit. */
6
- export interface EthPaymentEvent {
7
- readonly chain: EvmChain;
8
- readonly from: string;
9
- readonly to: string;
10
- /** Raw value in wei (BigInt as string for serialization). */
11
- readonly valueWei: string;
12
- /** USD cents equivalent at detection time (integer). */
13
- readonly amountUsdCents: number;
14
- readonly txHash: string;
15
- readonly blockNumber: number;
16
- /** Current confirmation count (latest block - tx block). */
17
- readonly confirmations: number;
18
- /** Required confirmations for this chain. */
19
- readonly confirmationsRequired: number;
20
- }
21
- export interface EthWatcherOpts {
22
- chain: EvmChain;
23
- rpcCall: RpcCall;
24
- oracle: IPriceOracle;
25
- fromBlock: number;
26
- onPayment: (event: EthPaymentEvent) => void | Promise<void>;
27
- watchedAddresses?: string[];
28
- cursorStore?: IWatcherCursorStore;
29
- /** Required confirmations (from DB). */
30
- confirmations: number;
31
- }
32
- /**
33
- * Native ETH transfer watcher.
34
- *
35
- * Unlike the ERC-20 EvmWatcher which uses eth_getLogs for Transfer events,
36
- * this scans blocks for transactions where `to` matches a watched deposit
37
- * address and `value > 0`.
38
- *
39
- * Scans up to latest block (not just confirmed) to detect pending txs.
40
- * Emits events on each confirmation increment. Only advances cursor
41
- * past fully-confirmed blocks.
42
- */
43
- export declare class EthWatcher {
44
- private _cursor;
45
- private readonly chain;
46
- private readonly rpc;
47
- private readonly oracle;
48
- private readonly onPayment;
49
- private readonly confirmations;
50
- private readonly cursorStore?;
51
- private readonly watcherId;
52
- private _watchedAddresses;
53
- constructor(opts: EthWatcherOpts);
54
- /** Load cursor from DB. Call once at startup before first poll. */
55
- init(): Promise<void>;
56
- setWatchedAddresses(addresses: string[]): void;
57
- get cursor(): number;
58
- /**
59
- * Poll for native ETH transfers to watched addresses, including unconfirmed blocks.
60
- *
61
- * Scans from cursor to latest block. Emits events with current confirmation count.
62
- * Re-emits on each confirmation increment. Only advances cursor past fully-confirmed blocks.
63
- */
64
- poll(): Promise<void>;
65
- }
66
- export {};
@@ -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 {};