@wopr-network/crypto-plugins 1.0.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.
- package/.github/workflows/ci.yml +33 -0
- package/.github/workflows/publish.yml +12 -0
- package/biome.json +23 -0
- package/dist/__tests__/bitcoin-encoder.test.d.ts +2 -0
- package/dist/__tests__/bitcoin-encoder.test.d.ts.map +1 -0
- package/dist/__tests__/bitcoin-encoder.test.js +97 -0
- package/dist/__tests__/bitcoin-encoder.test.js.map +1 -0
- package/dist/__tests__/dogecoin-encoder.test.d.ts +2 -0
- package/dist/__tests__/dogecoin-encoder.test.d.ts.map +1 -0
- package/dist/__tests__/dogecoin-encoder.test.js +57 -0
- package/dist/__tests__/dogecoin-encoder.test.js.map +1 -0
- package/dist/__tests__/litecoin-encoder.test.d.ts +2 -0
- package/dist/__tests__/litecoin-encoder.test.d.ts.map +1 -0
- package/dist/__tests__/litecoin-encoder.test.js +44 -0
- package/dist/__tests__/litecoin-encoder.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +2 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +75 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/rpc.test.d.ts +2 -0
- package/dist/__tests__/rpc.test.d.ts.map +1 -0
- package/dist/__tests__/rpc.test.js +31 -0
- package/dist/__tests__/rpc.test.js.map +1 -0
- package/dist/__tests__/solana-encoder.test.d.ts +2 -0
- package/dist/__tests__/solana-encoder.test.d.ts.map +1 -0
- package/dist/__tests__/solana-encoder.test.js +85 -0
- package/dist/__tests__/solana-encoder.test.js.map +1 -0
- package/dist/__tests__/solana-watcher.test.d.ts +2 -0
- package/dist/__tests__/solana-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/solana-watcher.test.js +281 -0
- package/dist/__tests__/solana-watcher.test.js.map +1 -0
- package/dist/__tests__/sweep-key-parity.test.d.ts +2 -0
- package/dist/__tests__/sweep-key-parity.test.d.ts.map +1 -0
- package/dist/__tests__/sweep-key-parity.test.js +236 -0
- package/dist/__tests__/sweep-key-parity.test.js.map +1 -0
- package/dist/__tests__/tron-encoder.test.d.ts +2 -0
- package/dist/__tests__/tron-encoder.test.d.ts.map +1 -0
- package/dist/__tests__/tron-encoder.test.js +93 -0
- package/dist/__tests__/tron-encoder.test.js.map +1 -0
- package/dist/__tests__/utxo-watcher.test.d.ts +2 -0
- package/dist/__tests__/utxo-watcher.test.d.ts.map +1 -0
- package/dist/__tests__/utxo-watcher.test.js +218 -0
- package/dist/__tests__/utxo-watcher.test.js.map +1 -0
- package/dist/bitcoin/encoder.d.ts +15 -0
- package/dist/bitcoin/encoder.d.ts.map +1 -0
- package/dist/bitcoin/encoder.js +286 -0
- package/dist/bitcoin/encoder.js.map +1 -0
- package/dist/bitcoin/index.d.ts +4 -0
- package/dist/bitcoin/index.d.ts.map +1 -0
- package/dist/bitcoin/index.js +20 -0
- package/dist/bitcoin/index.js.map +1 -0
- package/dist/dogecoin/encoder.d.ts +19 -0
- package/dist/dogecoin/encoder.d.ts.map +1 -0
- package/dist/dogecoin/encoder.js +145 -0
- package/dist/dogecoin/encoder.js.map +1 -0
- package/dist/dogecoin/index.d.ts +4 -0
- package/dist/dogecoin/index.d.ts.map +1 -0
- package/dist/dogecoin/index.js +20 -0
- package/dist/dogecoin/index.js.map +1 -0
- package/dist/evm/encoder.d.ts +7 -0
- package/dist/evm/encoder.d.ts.map +1 -0
- package/dist/evm/encoder.js +43 -0
- package/dist/evm/encoder.js.map +1 -0
- package/dist/evm/eth-watcher.d.ts +38 -0
- package/dist/evm/eth-watcher.d.ts.map +1 -0
- package/dist/evm/eth-watcher.js +138 -0
- package/dist/evm/eth-watcher.js.map +1 -0
- package/dist/evm/index.d.ts +16 -0
- package/dist/evm/index.d.ts.map +1 -0
- package/dist/evm/index.js +34 -0
- package/dist/evm/index.js.map +1 -0
- package/dist/evm/types.d.ts +43 -0
- package/dist/evm/types.d.ts.map +1 -0
- package/dist/evm/types.js +101 -0
- package/dist/evm/types.js.map +1 -0
- package/dist/evm/watcher.d.ts +42 -0
- package/dist/evm/watcher.d.ts.map +1 -0
- package/dist/evm/watcher.js +162 -0
- package/dist/evm/watcher.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/litecoin/encoder.d.ts +8 -0
- package/dist/litecoin/encoder.d.ts.map +1 -0
- package/dist/litecoin/encoder.js +16 -0
- package/dist/litecoin/encoder.js.map +1 -0
- package/dist/litecoin/index.d.ts +4 -0
- package/dist/litecoin/index.d.ts.map +1 -0
- package/dist/litecoin/index.js +20 -0
- package/dist/litecoin/index.js.map +1 -0
- package/dist/shared/test-helpers/index.d.ts +9 -0
- package/dist/shared/test-helpers/index.d.ts.map +1 -0
- package/dist/shared/test-helpers/index.js +30 -0
- package/dist/shared/test-helpers/index.js.map +1 -0
- package/dist/shared/utxo/index.d.ts +5 -0
- package/dist/shared/utxo/index.d.ts.map +1 -0
- package/dist/shared/utxo/index.js +3 -0
- package/dist/shared/utxo/index.js.map +1 -0
- package/dist/shared/utxo/rpc.d.ts +24 -0
- package/dist/shared/utxo/rpc.d.ts.map +1 -0
- package/dist/shared/utxo/rpc.js +75 -0
- package/dist/shared/utxo/rpc.js.map +1 -0
- package/dist/shared/utxo/types.d.ts +40 -0
- package/dist/shared/utxo/types.d.ts.map +1 -0
- package/dist/shared/utxo/types.js +2 -0
- package/dist/shared/utxo/types.js.map +1 -0
- package/dist/shared/utxo/watcher.d.ts +55 -0
- package/dist/shared/utxo/watcher.d.ts.map +1 -0
- package/dist/shared/utxo/watcher.js +150 -0
- package/dist/shared/utxo/watcher.js.map +1 -0
- package/dist/solana/encoder.d.ts +13 -0
- package/dist/solana/encoder.d.ts.map +1 -0
- package/dist/solana/encoder.js +62 -0
- package/dist/solana/encoder.js.map +1 -0
- package/dist/solana/index.d.ts +17 -0
- package/dist/solana/index.d.ts.map +1 -0
- package/dist/solana/index.js +32 -0
- package/dist/solana/index.js.map +1 -0
- package/dist/solana/sweeper.d.ts +47 -0
- package/dist/solana/sweeper.d.ts.map +1 -0
- package/dist/solana/sweeper.js +151 -0
- package/dist/solana/sweeper.js.map +1 -0
- package/dist/solana/types.d.ts +49 -0
- package/dist/solana/types.d.ts.map +1 -0
- package/dist/solana/types.js +2 -0
- package/dist/solana/types.js.map +1 -0
- package/dist/solana/watcher.d.ts +59 -0
- package/dist/solana/watcher.d.ts.map +1 -0
- package/dist/solana/watcher.js +251 -0
- package/dist/solana/watcher.js.map +1 -0
- package/dist/sweep/evm-sweeper.d.ts +31 -0
- package/dist/sweep/evm-sweeper.d.ts.map +1 -0
- package/dist/sweep/evm-sweeper.js +229 -0
- package/dist/sweep/evm-sweeper.js.map +1 -0
- package/dist/sweep/index.d.ts +22 -0
- package/dist/sweep/index.d.ts.map +1 -0
- package/dist/sweep/index.js +290 -0
- package/dist/sweep/index.js.map +1 -0
- package/dist/sweep/tron-sweeper.d.ts +40 -0
- package/dist/sweep/tron-sweeper.d.ts.map +1 -0
- package/dist/sweep/tron-sweeper.js +363 -0
- package/dist/sweep/tron-sweeper.js.map +1 -0
- package/dist/sweep/utxo-sweeper.d.ts +14 -0
- package/dist/sweep/utxo-sweeper.d.ts.map +1 -0
- package/dist/sweep/utxo-sweeper.js +13 -0
- package/dist/sweep/utxo-sweeper.js.map +1 -0
- package/dist/tron/address-convert.d.ts +15 -0
- package/dist/tron/address-convert.d.ts.map +1 -0
- package/dist/tron/address-convert.js +95 -0
- package/dist/tron/address-convert.js.map +1 -0
- package/dist/tron/encoder.d.ts +20 -0
- package/dist/tron/encoder.d.ts.map +1 -0
- package/dist/tron/encoder.js +67 -0
- package/dist/tron/encoder.js.map +1 -0
- package/dist/tron/index.d.ts +6 -0
- package/dist/tron/index.d.ts.map +1 -0
- package/dist/tron/index.js +20 -0
- package/dist/tron/index.js.map +1 -0
- package/dist/tron/sha256.d.ts +6 -0
- package/dist/tron/sha256.d.ts.map +1 -0
- package/dist/tron/sha256.js +90 -0
- package/dist/tron/sha256.js.map +1 -0
- package/dist/tron/watcher.d.ts +42 -0
- package/dist/tron/watcher.d.ts.map +1 -0
- package/dist/tron/watcher.js +168 -0
- package/dist/tron/watcher.js.map +1 -0
- package/package.json +47 -0
- package/src/__tests__/bitcoin-encoder.test.ts +115 -0
- package/src/__tests__/dogecoin-encoder.test.ts +66 -0
- package/src/__tests__/litecoin-encoder.test.ts +51 -0
- package/src/__tests__/registry.test.ts +91 -0
- package/src/__tests__/rpc.test.ts +36 -0
- package/src/__tests__/solana-encoder.test.ts +103 -0
- package/src/__tests__/solana-watcher.test.ts +316 -0
- package/src/__tests__/sweep-key-parity.test.ts +302 -0
- package/src/__tests__/tron-encoder.test.ts +108 -0
- package/src/__tests__/utxo-watcher.test.ts +252 -0
- package/src/bitcoin/encoder.ts +320 -0
- package/src/bitcoin/index.ts +23 -0
- package/src/dogecoin/encoder.ts +161 -0
- package/src/dogecoin/index.ts +23 -0
- package/src/evm/encoder.ts +49 -0
- package/src/evm/eth-watcher.ts +168 -0
- package/src/evm/index.ts +46 -0
- package/src/evm/types.ts +146 -0
- package/src/evm/watcher.ts +189 -0
- package/src/index.ts +21 -0
- package/src/litecoin/encoder.ts +18 -0
- package/src/litecoin/index.ts +23 -0
- package/src/shared/test-helpers/index.ts +36 -0
- package/src/shared/utxo/index.ts +12 -0
- package/src/shared/utxo/rpc.ts +80 -0
- package/src/shared/utxo/types.ts +43 -0
- package/src/shared/utxo/watcher.ts +195 -0
- package/src/solana/encoder.ts +72 -0
- package/src/solana/index.ts +36 -0
- package/src/solana/sweeper.ts +196 -0
- package/src/solana/types.ts +52 -0
- package/src/solana/watcher.ts +282 -0
- package/src/sweep/evm-sweeper.ts +296 -0
- package/src/sweep/index.ts +353 -0
- package/src/sweep/tron-sweeper.ts +467 -0
- package/src/sweep/utxo-sweeper.ts +23 -0
- package/src/tron/address-convert.ts +91 -0
- package/src/tron/encoder.ts +74 -0
- package/src/tron/index.ts +23 -0
- package/src/tron/sha256.ts +100 -0
- package/src/tron/watcher.ts +208 -0
- package/tsconfig.json +17 -0
- package/vitest.config.ts +8 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SHA-256 implementation (pure TypeScript).
|
|
3
|
+
* Shared within the tron module for encoder and address-convert.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
function ror32(x: number, n: number): number {
|
|
7
|
+
return (x >>> n) | (x << (32 - n));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function sha256(data: Uint8Array): Uint8Array {
|
|
11
|
+
const K: number[] = [
|
|
12
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98,
|
|
13
|
+
0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786,
|
|
14
|
+
0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 0x983e5152, 0xa831c66d, 0xb00327c8,
|
|
15
|
+
0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
|
|
16
|
+
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819,
|
|
17
|
+
0xd6990624, 0xf40e3585, 0x106aa070, 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a,
|
|
18
|
+
0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7,
|
|
19
|
+
0xc67178f2,
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const msgLen = data.length;
|
|
23
|
+
const bitLen = msgLen * 8;
|
|
24
|
+
const padLen = ((msgLen + 9 + 63) & ~63) - msgLen;
|
|
25
|
+
const padded = new Uint8Array(msgLen + padLen);
|
|
26
|
+
padded.set(data);
|
|
27
|
+
padded[msgLen] = 0x80;
|
|
28
|
+
const view = new DataView(padded.buffer);
|
|
29
|
+
view.setUint32(padded.length - 4, bitLen, false);
|
|
30
|
+
|
|
31
|
+
let h0 = 0x6a09e667;
|
|
32
|
+
let h1 = 0xbb67ae85;
|
|
33
|
+
let h2 = 0x3c6ef372;
|
|
34
|
+
let h3 = 0xa54ff53a;
|
|
35
|
+
let h4 = 0x510e527f;
|
|
36
|
+
let h5 = 0x9b05688c;
|
|
37
|
+
let h6 = 0x1f83d9ab;
|
|
38
|
+
let h7 = 0x5be0cd19;
|
|
39
|
+
|
|
40
|
+
const w = new Int32Array(64);
|
|
41
|
+
|
|
42
|
+
for (let offset = 0; offset < padded.length; offset += 64) {
|
|
43
|
+
for (let i = 0; i < 16; i++) {
|
|
44
|
+
w[i] = view.getInt32(offset + i * 4, false);
|
|
45
|
+
}
|
|
46
|
+
for (let i = 16; i < 64; i++) {
|
|
47
|
+
const s0 = (ror32(w[i - 15]!, 7) ^ ror32(w[i - 15]!, 18) ^ (w[i - 15]! >>> 3)) | 0;
|
|
48
|
+
const s1 = (ror32(w[i - 2]!, 17) ^ ror32(w[i - 2]!, 19) ^ (w[i - 2]! >>> 10)) | 0;
|
|
49
|
+
w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) | 0;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
let a = h0;
|
|
53
|
+
let b = h1;
|
|
54
|
+
let c = h2;
|
|
55
|
+
let d = h3;
|
|
56
|
+
let e = h4;
|
|
57
|
+
let f = h5;
|
|
58
|
+
let g = h6;
|
|
59
|
+
let h = h7;
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < 64; i++) {
|
|
62
|
+
const S1 = (ror32(e, 6) ^ ror32(e, 11) ^ ror32(e, 25)) | 0;
|
|
63
|
+
const ch = ((e & f) ^ (~e & g)) | 0;
|
|
64
|
+
const temp1 = (h + S1 + ch + K[i]! + w[i]!) | 0;
|
|
65
|
+
const S0 = (ror32(a, 2) ^ ror32(a, 13) ^ ror32(a, 22)) | 0;
|
|
66
|
+
const maj = ((a & b) ^ (a & c) ^ (b & c)) | 0;
|
|
67
|
+
const temp2 = (S0 + maj) | 0;
|
|
68
|
+
|
|
69
|
+
h = g;
|
|
70
|
+
g = f;
|
|
71
|
+
f = e;
|
|
72
|
+
e = (d + temp1) | 0;
|
|
73
|
+
d = c;
|
|
74
|
+
c = b;
|
|
75
|
+
b = a;
|
|
76
|
+
a = (temp1 + temp2) | 0;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
h0 = (h0 + a) | 0;
|
|
80
|
+
h1 = (h1 + b) | 0;
|
|
81
|
+
h2 = (h2 + c) | 0;
|
|
82
|
+
h3 = (h3 + d) | 0;
|
|
83
|
+
h4 = (h4 + e) | 0;
|
|
84
|
+
h5 = (h5 + f) | 0;
|
|
85
|
+
h6 = (h6 + g) | 0;
|
|
86
|
+
h7 = (h7 + h) | 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const result = new Uint8Array(32);
|
|
90
|
+
const rv = new DataView(result.buffer);
|
|
91
|
+
rv.setInt32(0, h0, false);
|
|
92
|
+
rv.setInt32(4, h1, false);
|
|
93
|
+
rv.setInt32(8, h2, false);
|
|
94
|
+
rv.setInt32(12, h3, false);
|
|
95
|
+
rv.setInt32(16, h4, false);
|
|
96
|
+
rv.setInt32(20, h5, false);
|
|
97
|
+
rv.setInt32(24, h6, false);
|
|
98
|
+
rv.setInt32(28, h7, false);
|
|
99
|
+
return result;
|
|
100
|
+
}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IChainWatcher,
|
|
3
|
+
IWatcherCursorStore,
|
|
4
|
+
PaymentEvent,
|
|
5
|
+
WatcherOpts,
|
|
6
|
+
} from "@wopr-network/platform-core/crypto-plugin";
|
|
7
|
+
|
|
8
|
+
import { hexToTron, tronToHex } from "./address-convert.js";
|
|
9
|
+
|
|
10
|
+
/** Raw JSON-RPC log entry (same shape as EVM). */
|
|
11
|
+
interface RpcLog {
|
|
12
|
+
address: string;
|
|
13
|
+
topics: string[];
|
|
14
|
+
data: string;
|
|
15
|
+
blockNumber: string;
|
|
16
|
+
transactionHash: string;
|
|
17
|
+
logIndex: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type RpcCall = (method: string, params: unknown[]) => Promise<unknown>;
|
|
21
|
+
|
|
22
|
+
const TRANSFER_TOPIC = "0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef";
|
|
23
|
+
|
|
24
|
+
/** Create a JSON-RPC caller for Tron's EVM-compatible endpoint. */
|
|
25
|
+
function createRpcCaller(rpcUrl: string, extraHeaders?: Record<string, string>): RpcCall {
|
|
26
|
+
let id = 0;
|
|
27
|
+
const headers: Record<string, string> = { "Content-Type": "application/json", ...extraHeaders };
|
|
28
|
+
return async (method: string, params: unknown[]): Promise<unknown> => {
|
|
29
|
+
const res = await fetch(rpcUrl, {
|
|
30
|
+
method: "POST",
|
|
31
|
+
headers,
|
|
32
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: ++id, method, params }),
|
|
33
|
+
});
|
|
34
|
+
if (!res.ok) {
|
|
35
|
+
const body = await res.text().catch(() => "");
|
|
36
|
+
const hasApiKey = "TRON-PRO-API-KEY" in headers;
|
|
37
|
+
console.error(
|
|
38
|
+
`[rpc] ${method} ${res.status} auth=${hasApiKey} url=${rpcUrl.replace(/apikey=[^&]+/, "apikey=***")} body=${body.slice(0, 200)}`,
|
|
39
|
+
);
|
|
40
|
+
throw new Error(`RPC ${method} failed: ${res.status}`);
|
|
41
|
+
}
|
|
42
|
+
const data = (await res.json()) as { result?: unknown; error?: { message: string } };
|
|
43
|
+
if (data.error) throw new Error(`RPC ${method} error: ${data.error.message}`);
|
|
44
|
+
return data.result;
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Convert token raw amount (BigInt) to USD cents (integer).
|
|
50
|
+
* Stablecoins are 1:1 USD. Truncates fractional cents.
|
|
51
|
+
*/
|
|
52
|
+
function centsFromTokenAmount(rawAmount: bigint, decimals: number): number {
|
|
53
|
+
return Number((rawAmount * 100n) / 10n ** BigInt(decimals));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Tron EVM-compatible watcher.
|
|
58
|
+
*
|
|
59
|
+
* Tron exposes an EVM-compatible JSON-RPC endpoint that supports eth_getLogs.
|
|
60
|
+
* This watcher wraps the EVM watcher pattern but converts between Tron T...
|
|
61
|
+
* Base58Check addresses and 0x hex addresses at the boundary.
|
|
62
|
+
*
|
|
63
|
+
* - setWatchedAddresses: accepts T... addresses, converts to 0x hex for filtering
|
|
64
|
+
* - poll results: converts 0x hex addresses back to T... for PaymentEvent output
|
|
65
|
+
*/
|
|
66
|
+
export class TronEvmWatcher implements IChainWatcher {
|
|
67
|
+
private _cursor = 0;
|
|
68
|
+
private _stopped = false;
|
|
69
|
+
private readonly chain: string;
|
|
70
|
+
private readonly token: string;
|
|
71
|
+
private readonly rpc: RpcCall;
|
|
72
|
+
private readonly confirmations: number;
|
|
73
|
+
private readonly contractAddress: string;
|
|
74
|
+
private readonly decimals: number;
|
|
75
|
+
private readonly cursorStore: IWatcherCursorStore;
|
|
76
|
+
private readonly watcherId: string;
|
|
77
|
+
/** Hex addresses used for RPC filtering. */
|
|
78
|
+
private _watchedHexAddresses: string[] = [];
|
|
79
|
+
/** Map from lowercase hex -> original T... address for reverse lookup. */
|
|
80
|
+
private readonly _hexToTronMap: Map<string, string> = new Map();
|
|
81
|
+
|
|
82
|
+
constructor(opts: WatcherOpts) {
|
|
83
|
+
this.chain = opts.chain;
|
|
84
|
+
this.token = opts.token;
|
|
85
|
+
this.rpc = createRpcCaller(opts.rpcUrl, opts.rpcHeaders);
|
|
86
|
+
this._cursor = 0;
|
|
87
|
+
this.confirmations = opts.confirmations;
|
|
88
|
+
this.contractAddress = (opts.contractAddress ?? "").toLowerCase();
|
|
89
|
+
this.decimals = opts.decimals;
|
|
90
|
+
this.cursorStore = opts.cursorStore;
|
|
91
|
+
this.watcherId = `tron:${opts.chain}:${opts.token}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async init(): Promise<void> {
|
|
95
|
+
const saved = await this.cursorStore.get(this.watcherId);
|
|
96
|
+
if (saved !== null) this._cursor = saved;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Set watched addresses. Accepts Tron T... addresses.
|
|
101
|
+
* Converts to 0x hex internally for RPC filtering.
|
|
102
|
+
*/
|
|
103
|
+
setWatchedAddresses(addresses: string[]): void {
|
|
104
|
+
this._hexToTronMap.clear();
|
|
105
|
+
this._watchedHexAddresses = addresses.map((addr) => {
|
|
106
|
+
const hex = tronToHex(addr).toLowerCase();
|
|
107
|
+
this._hexToTronMap.set(hex, addr);
|
|
108
|
+
return hex;
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
getCursor(): number {
|
|
113
|
+
return this._cursor;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
stop(): void {
|
|
117
|
+
this._stopped = true;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Poll for TRC-20 Transfer events using the EVM-compatible JSON-RPC.
|
|
122
|
+
* Converts hex addresses in results back to Tron T... format.
|
|
123
|
+
*/
|
|
124
|
+
async poll(): Promise<PaymentEvent[]> {
|
|
125
|
+
if (this._stopped || this._watchedHexAddresses.length === 0) return [];
|
|
126
|
+
|
|
127
|
+
const latestHex = (await this.rpc("eth_blockNumber", [])) as string;
|
|
128
|
+
const latest = Number.parseInt(latestHex, 16);
|
|
129
|
+
const confirmed = latest - this.confirmations;
|
|
130
|
+
|
|
131
|
+
if (latest < this._cursor) return [];
|
|
132
|
+
|
|
133
|
+
// Filter by topic[2] (to address) using hex addresses
|
|
134
|
+
const toFilter =
|
|
135
|
+
this._watchedHexAddresses.length > 0
|
|
136
|
+
? this._watchedHexAddresses.map((a) => `0x000000000000000000000000${a.slice(2)}`)
|
|
137
|
+
: null;
|
|
138
|
+
|
|
139
|
+
const logs = (await this.rpc("eth_getLogs", [
|
|
140
|
+
{
|
|
141
|
+
address: this.contractAddress,
|
|
142
|
+
topics: [TRANSFER_TOPIC, null, toFilter],
|
|
143
|
+
fromBlock: `0x${this._cursor.toString(16)}`,
|
|
144
|
+
toBlock: `0x${latest.toString(16)}`,
|
|
145
|
+
},
|
|
146
|
+
])) as RpcLog[];
|
|
147
|
+
|
|
148
|
+
// Group logs by block
|
|
149
|
+
const logsByBlock = new Map<number, RpcLog[]>();
|
|
150
|
+
for (const log of logs) {
|
|
151
|
+
const bn = Number.parseInt(log.blockNumber, 16);
|
|
152
|
+
const arr = logsByBlock.get(bn);
|
|
153
|
+
if (arr) arr.push(log);
|
|
154
|
+
else logsByBlock.set(bn, [log]);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const events: PaymentEvent[] = [];
|
|
158
|
+
|
|
159
|
+
const blockNums = [...logsByBlock.keys()].sort((a, b) => a - b);
|
|
160
|
+
for (const blockNum of blockNums) {
|
|
161
|
+
const confs = latest - blockNum;
|
|
162
|
+
|
|
163
|
+
for (const log of logsByBlock.get(blockNum) ?? []) {
|
|
164
|
+
const txKey = `${log.transactionHash}:${log.logIndex}`;
|
|
165
|
+
|
|
166
|
+
const lastConf = await this.cursorStore.getConfirmationCount(this.watcherId, txKey);
|
|
167
|
+
if (lastConf !== null && confs <= lastConf) continue;
|
|
168
|
+
|
|
169
|
+
const toHex = `0x${log.topics[2].slice(26)}`.toLowerCase();
|
|
170
|
+
const fromHex = `0x${log.topics[1].slice(26)}`.toLowerCase();
|
|
171
|
+
const rawAmount = BigInt(log.data);
|
|
172
|
+
const amountUsdCents = centsFromTokenAmount(rawAmount, this.decimals);
|
|
173
|
+
|
|
174
|
+
// Convert hex addresses back to Tron T... format
|
|
175
|
+
const toTron = this._hexToTronMap.get(toHex) ?? hexToTron(toHex);
|
|
176
|
+
const fromTron = hexToTron(fromHex);
|
|
177
|
+
|
|
178
|
+
events.push({
|
|
179
|
+
chain: this.chain,
|
|
180
|
+
token: this.token,
|
|
181
|
+
from: fromTron,
|
|
182
|
+
to: toTron,
|
|
183
|
+
rawAmount: rawAmount.toString(),
|
|
184
|
+
amountUsdCents,
|
|
185
|
+
txHash: log.transactionHash,
|
|
186
|
+
blockNumber: blockNum,
|
|
187
|
+
confirmations: confs,
|
|
188
|
+
confirmationsRequired: this.confirmations,
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
await this.cursorStore.saveConfirmationCount(this.watcherId, txKey, confs);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (blockNum <= confirmed) {
|
|
195
|
+
this._cursor = blockNum + 1;
|
|
196
|
+
await this.cursorStore.save(this.watcherId, this._cursor);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Advance cursor if no logs found but confirmed blocks exist
|
|
201
|
+
if (blockNums.length === 0 && confirmed >= this._cursor) {
|
|
202
|
+
this._cursor = confirmed + 1;
|
|
203
|
+
await this.cursorStore.save(this.watcherId, this._cursor);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return events;
|
|
207
|
+
}
|
|
208
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"declaration": true,
|
|
10
|
+
"declarationMap": true,
|
|
11
|
+
"sourceMap": true,
|
|
12
|
+
"esModuleInterop": true,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"types": ["node"]
|
|
15
|
+
},
|
|
16
|
+
"include": ["src"]
|
|
17
|
+
}
|