@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,281 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { SolanaWatcher } from "../solana/watcher.js";
|
|
3
|
+
/** Create a minimal mock WatcherOpts. */
|
|
4
|
+
function createMockOpts(rpcResponses) {
|
|
5
|
+
// Override global fetch to return mocked RPC responses
|
|
6
|
+
const mockFetch = vi.fn(async (_url, init) => {
|
|
7
|
+
const body = JSON.parse(init?.body ?? "{}");
|
|
8
|
+
const result = rpcResponses.get(body.method);
|
|
9
|
+
return new Response(JSON.stringify({ jsonrpc: "2.0", id: body.id, result }), {
|
|
10
|
+
status: 200,
|
|
11
|
+
headers: { "Content-Type": "application/json" },
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
// biome-ignore lint/suspicious/noExplicitAny: test mock override
|
|
15
|
+
globalThis.fetch = mockFetch;
|
|
16
|
+
return {
|
|
17
|
+
rpcUrl: "http://localhost:8899",
|
|
18
|
+
rpcHeaders: {},
|
|
19
|
+
oracle: { getPrice: vi.fn().mockResolvedValue({ priceMicros: 150_000_000 }) },
|
|
20
|
+
cursorStore: {
|
|
21
|
+
get: vi.fn().mockResolvedValue(null),
|
|
22
|
+
save: vi.fn().mockResolvedValue(undefined),
|
|
23
|
+
getConfirmationCount: vi.fn().mockResolvedValue(null),
|
|
24
|
+
saveConfirmationCount: vi.fn().mockResolvedValue(undefined),
|
|
25
|
+
},
|
|
26
|
+
token: "SOL",
|
|
27
|
+
chain: "solana",
|
|
28
|
+
decimals: 9,
|
|
29
|
+
confirmations: 1,
|
|
30
|
+
_mockFetch: mockFetch,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
describe("SolanaWatcher", () => {
|
|
34
|
+
it("initializes with cursor from store", async () => {
|
|
35
|
+
const rpcResponses = new Map();
|
|
36
|
+
const opts = createMockOpts(rpcResponses);
|
|
37
|
+
opts.cursorStore.get = vi.fn().mockResolvedValue(500);
|
|
38
|
+
const watcher = new SolanaWatcher(opts);
|
|
39
|
+
await watcher.init();
|
|
40
|
+
expect(watcher.getCursor()).toBe(500);
|
|
41
|
+
});
|
|
42
|
+
it("returns empty when no watched addresses", async () => {
|
|
43
|
+
const rpcResponses = new Map();
|
|
44
|
+
const opts = createMockOpts(rpcResponses);
|
|
45
|
+
const watcher = new SolanaWatcher(opts);
|
|
46
|
+
await watcher.init();
|
|
47
|
+
const events = await watcher.poll();
|
|
48
|
+
expect(events).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
it("returns empty when stopped", async () => {
|
|
51
|
+
const rpcResponses = new Map();
|
|
52
|
+
const opts = createMockOpts(rpcResponses);
|
|
53
|
+
const watcher = new SolanaWatcher(opts);
|
|
54
|
+
await watcher.init();
|
|
55
|
+
watcher.setWatchedAddresses(["SomeAddress111111111111111111111111111111111"]);
|
|
56
|
+
watcher.stop();
|
|
57
|
+
const events = await watcher.poll();
|
|
58
|
+
expect(events).toEqual([]);
|
|
59
|
+
});
|
|
60
|
+
it("detects native SOL transfer to watched address", async () => {
|
|
61
|
+
const watchedAddr = "ReceiverAddr1111111111111111111111111111111";
|
|
62
|
+
const senderAddr = "SenderAddr11111111111111111111111111111111";
|
|
63
|
+
const mockTx = {
|
|
64
|
+
slot: 100,
|
|
65
|
+
blockTime: 1700000000,
|
|
66
|
+
meta: {
|
|
67
|
+
err: null,
|
|
68
|
+
fee: 5000,
|
|
69
|
+
preBalances: [2_000_000_000, 500_000_000],
|
|
70
|
+
postBalances: [1_000_000_000, 1_500_000_000],
|
|
71
|
+
},
|
|
72
|
+
transaction: {
|
|
73
|
+
message: {
|
|
74
|
+
accountKeys: [senderAddr, watchedAddr],
|
|
75
|
+
instructions: [{ programIdIndex: 0, accounts: [0, 1], data: "" }],
|
|
76
|
+
},
|
|
77
|
+
signatures: ["sig123abc"],
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
const rpcResponses = new Map();
|
|
81
|
+
rpcResponses.set("getSignaturesForAddress", [
|
|
82
|
+
{
|
|
83
|
+
signature: "sig123abc",
|
|
84
|
+
slot: 100,
|
|
85
|
+
err: null,
|
|
86
|
+
memo: null,
|
|
87
|
+
blockTime: 1700000000,
|
|
88
|
+
confirmationStatus: "finalized",
|
|
89
|
+
},
|
|
90
|
+
]);
|
|
91
|
+
rpcResponses.set("getTransaction", mockTx);
|
|
92
|
+
const opts = createMockOpts(rpcResponses);
|
|
93
|
+
const watcher = new SolanaWatcher(opts);
|
|
94
|
+
await watcher.init();
|
|
95
|
+
watcher.setWatchedAddresses([watchedAddr]);
|
|
96
|
+
const events = await watcher.poll();
|
|
97
|
+
expect(events).toHaveLength(1);
|
|
98
|
+
expect(events[0].txHash).toBe("sig123abc");
|
|
99
|
+
expect(events[0].to).toBe(watchedAddr);
|
|
100
|
+
expect(events[0].from).toBe(senderAddr);
|
|
101
|
+
expect(events[0].rawAmount).toBe("1000000000"); // 1 SOL increase
|
|
102
|
+
expect(events[0].chain).toBe("solana");
|
|
103
|
+
expect(events[0].token).toBe("SOL");
|
|
104
|
+
expect(events[0].blockNumber).toBe(100);
|
|
105
|
+
});
|
|
106
|
+
it("advances cursor for finalized transactions", async () => {
|
|
107
|
+
const watchedAddr = "ReceiverAddr1111111111111111111111111111111";
|
|
108
|
+
const mockTx = {
|
|
109
|
+
slot: 200,
|
|
110
|
+
blockTime: 1700000000,
|
|
111
|
+
meta: {
|
|
112
|
+
err: null,
|
|
113
|
+
fee: 5000,
|
|
114
|
+
preBalances: [2_000_000_000, 0],
|
|
115
|
+
postBalances: [1_000_000_000, 1_000_000_000],
|
|
116
|
+
},
|
|
117
|
+
transaction: {
|
|
118
|
+
message: {
|
|
119
|
+
accountKeys: ["Sender1111111111111111111111111111111111111", watchedAddr],
|
|
120
|
+
instructions: [],
|
|
121
|
+
},
|
|
122
|
+
signatures: ["sig456def"],
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
const rpcResponses = new Map();
|
|
126
|
+
rpcResponses.set("getSignaturesForAddress", [
|
|
127
|
+
{
|
|
128
|
+
signature: "sig456def",
|
|
129
|
+
slot: 200,
|
|
130
|
+
err: null,
|
|
131
|
+
memo: null,
|
|
132
|
+
blockTime: 1700000000,
|
|
133
|
+
confirmationStatus: "finalized",
|
|
134
|
+
},
|
|
135
|
+
]);
|
|
136
|
+
rpcResponses.set("getTransaction", mockTx);
|
|
137
|
+
const opts = createMockOpts(rpcResponses);
|
|
138
|
+
const watcher = new SolanaWatcher(opts);
|
|
139
|
+
await watcher.init();
|
|
140
|
+
watcher.setWatchedAddresses([watchedAddr]);
|
|
141
|
+
await watcher.poll();
|
|
142
|
+
expect(watcher.getCursor()).toBe(200);
|
|
143
|
+
expect(opts.cursorStore.save).toHaveBeenCalledWith("solana:solana:SOL", 200);
|
|
144
|
+
});
|
|
145
|
+
it("skips errored transactions", async () => {
|
|
146
|
+
const watchedAddr = "ReceiverAddr1111111111111111111111111111111";
|
|
147
|
+
const rpcResponses = new Map();
|
|
148
|
+
rpcResponses.set("getSignaturesForAddress", [
|
|
149
|
+
{
|
|
150
|
+
signature: "errored-sig",
|
|
151
|
+
slot: 300,
|
|
152
|
+
err: { InstructionError: [0, "Custom"] },
|
|
153
|
+
memo: null,
|
|
154
|
+
blockTime: 1700000000,
|
|
155
|
+
confirmationStatus: "finalized",
|
|
156
|
+
},
|
|
157
|
+
]);
|
|
158
|
+
const opts = createMockOpts(rpcResponses);
|
|
159
|
+
const watcher = new SolanaWatcher(opts);
|
|
160
|
+
await watcher.init();
|
|
161
|
+
watcher.setWatchedAddresses([watchedAddr]);
|
|
162
|
+
const events = await watcher.poll();
|
|
163
|
+
expect(events).toEqual([]);
|
|
164
|
+
});
|
|
165
|
+
it("skips already-emitted confirmations", async () => {
|
|
166
|
+
const watchedAddr = "ReceiverAddr1111111111111111111111111111111";
|
|
167
|
+
const mockTx = {
|
|
168
|
+
slot: 400,
|
|
169
|
+
blockTime: 1700000000,
|
|
170
|
+
meta: {
|
|
171
|
+
err: null,
|
|
172
|
+
fee: 5000,
|
|
173
|
+
preBalances: [2_000_000_000, 0],
|
|
174
|
+
postBalances: [1_000_000_000, 1_000_000_000],
|
|
175
|
+
},
|
|
176
|
+
transaction: {
|
|
177
|
+
message: {
|
|
178
|
+
accountKeys: ["Sender1111111111111111111111111111111111111", watchedAddr],
|
|
179
|
+
instructions: [],
|
|
180
|
+
},
|
|
181
|
+
signatures: ["sig-dup"],
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
const rpcResponses = new Map();
|
|
185
|
+
rpcResponses.set("getSignaturesForAddress", [
|
|
186
|
+
{
|
|
187
|
+
signature: "sig-dup",
|
|
188
|
+
slot: 400,
|
|
189
|
+
err: null,
|
|
190
|
+
memo: null,
|
|
191
|
+
blockTime: 1700000000,
|
|
192
|
+
confirmationStatus: "finalized",
|
|
193
|
+
},
|
|
194
|
+
]);
|
|
195
|
+
rpcResponses.set("getTransaction", mockTx);
|
|
196
|
+
const opts = createMockOpts(rpcResponses);
|
|
197
|
+
// Already emitted at confirmation count 1
|
|
198
|
+
opts.cursorStore.getConfirmationCount = vi.fn().mockResolvedValue(1);
|
|
199
|
+
const watcher = new SolanaWatcher(opts);
|
|
200
|
+
await watcher.init();
|
|
201
|
+
watcher.setWatchedAddresses([watchedAddr]);
|
|
202
|
+
const events = await watcher.poll();
|
|
203
|
+
expect(events).toEqual([]);
|
|
204
|
+
});
|
|
205
|
+
it("detects SPL token transfer to watched address", async () => {
|
|
206
|
+
const watchedAddr = "ReceiverAddr1111111111111111111111111111111";
|
|
207
|
+
const senderAddr = "SenderAddr11111111111111111111111111111111";
|
|
208
|
+
const usdcMint = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
209
|
+
const mockTx = {
|
|
210
|
+
slot: 500,
|
|
211
|
+
blockTime: 1700000000,
|
|
212
|
+
meta: {
|
|
213
|
+
err: null,
|
|
214
|
+
fee: 5000,
|
|
215
|
+
preBalances: [2_000_000_000, 1_000_000],
|
|
216
|
+
postBalances: [1_995_000_000, 1_000_000],
|
|
217
|
+
preTokenBalances: [
|
|
218
|
+
{
|
|
219
|
+
accountIndex: 0,
|
|
220
|
+
mint: usdcMint,
|
|
221
|
+
uiTokenAmount: { amount: "10000000", decimals: 6, uiAmountString: "10.0" },
|
|
222
|
+
owner: senderAddr,
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
accountIndex: 1,
|
|
226
|
+
mint: usdcMint,
|
|
227
|
+
uiTokenAmount: { amount: "0", decimals: 6, uiAmountString: "0.0" },
|
|
228
|
+
owner: watchedAddr,
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
postTokenBalances: [
|
|
232
|
+
{
|
|
233
|
+
accountIndex: 0,
|
|
234
|
+
mint: usdcMint,
|
|
235
|
+
uiTokenAmount: { amount: "5000000", decimals: 6, uiAmountString: "5.0" },
|
|
236
|
+
owner: senderAddr,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
accountIndex: 1,
|
|
240
|
+
mint: usdcMint,
|
|
241
|
+
uiTokenAmount: { amount: "5000000", decimals: 6, uiAmountString: "5.0" },
|
|
242
|
+
owner: watchedAddr,
|
|
243
|
+
},
|
|
244
|
+
],
|
|
245
|
+
},
|
|
246
|
+
transaction: {
|
|
247
|
+
message: {
|
|
248
|
+
accountKeys: [senderAddr, watchedAddr],
|
|
249
|
+
instructions: [],
|
|
250
|
+
},
|
|
251
|
+
signatures: ["spl-sig-789"],
|
|
252
|
+
},
|
|
253
|
+
};
|
|
254
|
+
const rpcResponses = new Map();
|
|
255
|
+
rpcResponses.set("getSignaturesForAddress", [
|
|
256
|
+
{
|
|
257
|
+
signature: "spl-sig-789",
|
|
258
|
+
slot: 500,
|
|
259
|
+
err: null,
|
|
260
|
+
memo: null,
|
|
261
|
+
blockTime: 1700000000,
|
|
262
|
+
confirmationStatus: "finalized",
|
|
263
|
+
},
|
|
264
|
+
]);
|
|
265
|
+
rpcResponses.set("getTransaction", mockTx);
|
|
266
|
+
const opts = createMockOpts(rpcResponses);
|
|
267
|
+
// Configure as SPL token watcher
|
|
268
|
+
const splOpts = { ...opts, token: "USDC", contractAddress: usdcMint };
|
|
269
|
+
const watcher = new SolanaWatcher(splOpts);
|
|
270
|
+
await watcher.init();
|
|
271
|
+
watcher.setWatchedAddresses([watchedAddr]);
|
|
272
|
+
const events = await watcher.poll();
|
|
273
|
+
expect(events).toHaveLength(1);
|
|
274
|
+
expect(events[0].txHash).toBe("spl-sig-789");
|
|
275
|
+
expect(events[0].to).toBe(watchedAddr);
|
|
276
|
+
expect(events[0].from).toBe(senderAddr);
|
|
277
|
+
expect(events[0].rawAmount).toBe("5000000"); // 5 USDC
|
|
278
|
+
expect(events[0].token).toBe("USDC");
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
//# sourceMappingURL=solana-watcher.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana-watcher.test.js","sourceRoot":"","sources":["../../src/__tests__/solana-watcher.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep-key-parity.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/sweep-key-parity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { ed25519 } from "@noble/curves/ed25519.js";
|
|
2
|
+
import { hmac } from "@noble/hashes/hmac.js";
|
|
3
|
+
import { sha512 } from "@noble/hashes/sha2.js";
|
|
4
|
+
import { HDKey } from "@scure/bip32";
|
|
5
|
+
import * as bip39 from "@scure/bip39";
|
|
6
|
+
import { privateKeyToAccount } from "viem/accounts";
|
|
7
|
+
import { describe, expect, it } from "vitest";
|
|
8
|
+
import { bech32Encoder } from "../bitcoin/encoder.js";
|
|
9
|
+
import { p2pkhEncoder } from "../dogecoin/encoder.js";
|
|
10
|
+
import { EvmAddressEncoder } from "../evm/encoder.js";
|
|
11
|
+
import { bech32Encoder as ltcBech32Encoder } from "../litecoin/encoder.js";
|
|
12
|
+
import { SolanaAddressEncoder } from "../solana/encoder.js";
|
|
13
|
+
import { keccakB58Encoder } from "../tron/encoder.js";
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Well-known BIP-39 test mnemonic (public, never use in production)
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const TEST_MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
|
|
18
|
+
const TEST_SEED = bip39.mnemonicToSeedSync(TEST_MNEMONIC);
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Helpers
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
function toHex(bytes) {
|
|
23
|
+
return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
24
|
+
}
|
|
25
|
+
function privKeyHex(key) {
|
|
26
|
+
return `0x${toHex(key)}`;
|
|
27
|
+
}
|
|
28
|
+
const SECP256K1_CHAINS = [
|
|
29
|
+
{
|
|
30
|
+
name: "Bitcoin",
|
|
31
|
+
coinType: 0,
|
|
32
|
+
encoder: bech32Encoder,
|
|
33
|
+
params: { hrp: "bc" },
|
|
34
|
+
addrRegex: /^bc1q/,
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: "Litecoin",
|
|
38
|
+
coinType: 2,
|
|
39
|
+
encoder: ltcBech32Encoder,
|
|
40
|
+
params: { hrp: "ltc" },
|
|
41
|
+
addrRegex: /^ltc1q/,
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: "Dogecoin",
|
|
45
|
+
coinType: 3,
|
|
46
|
+
encoder: p2pkhEncoder,
|
|
47
|
+
params: { version: "0x1e" },
|
|
48
|
+
addrRegex: /^D/,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: "EVM",
|
|
52
|
+
coinType: 60,
|
|
53
|
+
encoder: new EvmAddressEncoder(),
|
|
54
|
+
params: {},
|
|
55
|
+
addrRegex: /^0x[0-9a-fA-F]{40}$/,
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
name: "Tron",
|
|
59
|
+
coinType: 195,
|
|
60
|
+
encoder: keccakB58Encoder,
|
|
61
|
+
params: { version: "0x41" },
|
|
62
|
+
addrRegex: /^T/,
|
|
63
|
+
},
|
|
64
|
+
];
|
|
65
|
+
function slip0010MasterKey(seed) {
|
|
66
|
+
const I = hmac(sha512, new TextEncoder().encode("ed25519 seed"), seed);
|
|
67
|
+
return {
|
|
68
|
+
privateKey: I.slice(0, 32),
|
|
69
|
+
chainCode: I.slice(32),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
function slip0010DeriveChild(parent, index) {
|
|
73
|
+
// SLIP-0010 Ed25519: only hardened derivation (index >= 0x80000000)
|
|
74
|
+
const hardenedIndex = (index | 0x80000000) >>> 0;
|
|
75
|
+
const data = new Uint8Array(1 + 32 + 4);
|
|
76
|
+
data[0] = 0x00;
|
|
77
|
+
data.set(parent.privateKey, 1);
|
|
78
|
+
const view = new DataView(data.buffer);
|
|
79
|
+
view.setUint32(33, hardenedIndex, false); // big-endian
|
|
80
|
+
const I = hmac(sha512, parent.chainCode, data);
|
|
81
|
+
return {
|
|
82
|
+
privateKey: I.slice(0, 32),
|
|
83
|
+
chainCode: I.slice(32),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function slip0010DerivePath(seed, path) {
|
|
87
|
+
let key = slip0010MasterKey(seed);
|
|
88
|
+
for (const index of path) {
|
|
89
|
+
key = slip0010DeriveChild(key, index);
|
|
90
|
+
}
|
|
91
|
+
return key;
|
|
92
|
+
}
|
|
93
|
+
// ===========================================================================
|
|
94
|
+
// Tests
|
|
95
|
+
// ===========================================================================
|
|
96
|
+
describe("sweep key parity -- mnemonic private key controls derived address", () => {
|
|
97
|
+
// -----------------------------------------------------------------------
|
|
98
|
+
// secp256k1 chains: pubkey from xpub must equal pubkey from privkey
|
|
99
|
+
// -----------------------------------------------------------------------
|
|
100
|
+
describe("secp256k1 chains", () => {
|
|
101
|
+
for (const chain of SECP256K1_CHAINS) {
|
|
102
|
+
const path = `m/44'/${chain.coinType}'/0'`;
|
|
103
|
+
it(`${chain.name}: privkey pubkey matches xpub pubkey at indices 0-4`, () => {
|
|
104
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
105
|
+
const accountKey = masterHD.derive(path);
|
|
106
|
+
const xpub = accountKey.publicExtendedKey;
|
|
107
|
+
for (let i = 0; i < 5; i++) {
|
|
108
|
+
// From full key (has private key): derive external chain (0), index i
|
|
109
|
+
const fullChild = accountKey.deriveChild(0).deriveChild(i);
|
|
110
|
+
// From xpub only: derive external chain (0), index i
|
|
111
|
+
const xpubChild = HDKey.fromExtendedKey(xpub).deriveChild(0).deriveChild(i);
|
|
112
|
+
const fullPk = fullChild.publicKey;
|
|
113
|
+
const xpubPk = xpubChild.publicKey;
|
|
114
|
+
expect(fullPk).toBeDefined();
|
|
115
|
+
expect(xpubPk).toBeDefined();
|
|
116
|
+
expect(toHex(fullPk)).toBe(toHex(xpubPk));
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
it(`${chain.name}: encoder produces valid address from derived pubkey`, () => {
|
|
120
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
121
|
+
const accountKey = masterHD.derive(path);
|
|
122
|
+
const xpub = accountKey.publicExtendedKey;
|
|
123
|
+
for (let i = 0; i < 5; i++) {
|
|
124
|
+
const xpubChild = HDKey.fromExtendedKey(xpub).deriveChild(0).deriveChild(i);
|
|
125
|
+
const pubkey = xpubChild.publicKey;
|
|
126
|
+
const address = chain.encoder.encode(pubkey, chain.params);
|
|
127
|
+
expect(address).toMatch(chain.addrRegex);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
it(`${chain.name}: different indices produce different addresses`, () => {
|
|
131
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
132
|
+
const accountKey = masterHD.derive(path);
|
|
133
|
+
const xpub = accountKey.publicExtendedKey;
|
|
134
|
+
const addresses = new Set();
|
|
135
|
+
for (let i = 0; i < 5; i++) {
|
|
136
|
+
const xpubChild = HDKey.fromExtendedKey(xpub).deriveChild(0).deriveChild(i);
|
|
137
|
+
const pubkey = xpubChild.publicKey;
|
|
138
|
+
const address = chain.encoder.encode(pubkey, chain.params);
|
|
139
|
+
addresses.add(address);
|
|
140
|
+
}
|
|
141
|
+
expect(addresses.size).toBe(5);
|
|
142
|
+
});
|
|
143
|
+
it(`${chain.name}: derivation is deterministic`, () => {
|
|
144
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
145
|
+
const accountKey = masterHD.derive(path);
|
|
146
|
+
const xpub = accountKey.publicExtendedKey;
|
|
147
|
+
const xpubChild = HDKey.fromExtendedKey(xpub).deriveChild(0).deriveChild(3);
|
|
148
|
+
const pubkey = xpubChild.publicKey;
|
|
149
|
+
const addr1 = chain.encoder.encode(pubkey, chain.params);
|
|
150
|
+
const addr2 = chain.encoder.encode(pubkey, chain.params);
|
|
151
|
+
expect(addr1).toBe(addr2);
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
// -----------------------------------------------------------------------
|
|
156
|
+
// EVM signing proof: viem privateKeyToAccount matches encoder output
|
|
157
|
+
// -----------------------------------------------------------------------
|
|
158
|
+
describe("EVM signing proof", () => {
|
|
159
|
+
it("viem privateKeyToAccount matches encoder output at indices 0-9", () => {
|
|
160
|
+
const evmPath = "m/44'/60'/0'";
|
|
161
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
162
|
+
const accountKey = masterHD.derive(evmPath);
|
|
163
|
+
const encoder = new EvmAddressEncoder();
|
|
164
|
+
for (let i = 0; i < 10; i++) {
|
|
165
|
+
const child = accountKey.deriveChild(0).deriveChild(i);
|
|
166
|
+
const privKey = child.privateKey;
|
|
167
|
+
const pubKey = child.publicKey;
|
|
168
|
+
// Address from encoder (pubkey -> keccak -> EIP-55)
|
|
169
|
+
const encoderAddr = encoder.encode(pubKey, {});
|
|
170
|
+
// Address from viem (privkey -> account -> address)
|
|
171
|
+
const viemAccount = privateKeyToAccount(privKeyHex(privKey));
|
|
172
|
+
expect(viemAccount.address.toLowerCase()).toBe(encoderAddr.toLowerCase());
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
it("viem treasury key matches encoder output", () => {
|
|
176
|
+
const evmPath = "m/44'/60'/0'";
|
|
177
|
+
const masterHD = HDKey.fromMasterSeed(TEST_SEED);
|
|
178
|
+
const accountKey = masterHD.derive(evmPath);
|
|
179
|
+
const encoder = new EvmAddressEncoder();
|
|
180
|
+
// Treasury: chain index 1, address index 0
|
|
181
|
+
const child = accountKey.deriveChild(1).deriveChild(0);
|
|
182
|
+
const privKey = child.privateKey;
|
|
183
|
+
const pubKey = child.publicKey;
|
|
184
|
+
const encoderAddr = encoder.encode(pubKey, {});
|
|
185
|
+
const viemAccount = privateKeyToAccount(privKeyHex(privKey));
|
|
186
|
+
expect(viemAccount.address.toLowerCase()).toBe(encoderAddr.toLowerCase());
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
// -----------------------------------------------------------------------
|
|
190
|
+
// Solana Ed25519 (SLIP-0010 derivation)
|
|
191
|
+
// -----------------------------------------------------------------------
|
|
192
|
+
describe("Solana Ed25519", () => {
|
|
193
|
+
const solanaEncoder = new SolanaAddressEncoder();
|
|
194
|
+
it("SLIP-0010 derivation produces valid Solana address", () => {
|
|
195
|
+
// m/44'/501'/0'/0' -- all hardened for SLIP-0010 Ed25519
|
|
196
|
+
const derived = slip0010DerivePath(TEST_SEED, [44, 501, 0, 0]);
|
|
197
|
+
const publicKey = ed25519.getPublicKey(derived.privateKey);
|
|
198
|
+
expect(publicKey.length).toBe(32);
|
|
199
|
+
const address = solanaEncoder.encode(publicKey, {});
|
|
200
|
+
// Valid Base58 string, typical Solana address length 32-44 chars
|
|
201
|
+
expect(address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);
|
|
202
|
+
expect(address.length).toBeGreaterThanOrEqual(32);
|
|
203
|
+
expect(address.length).toBeLessThanOrEqual(44);
|
|
204
|
+
});
|
|
205
|
+
it("same seed always produces same address", () => {
|
|
206
|
+
const derived1 = slip0010DerivePath(TEST_SEED, [44, 501, 0, 0]);
|
|
207
|
+
const pub1 = ed25519.getPublicKey(derived1.privateKey);
|
|
208
|
+
const addr1 = solanaEncoder.encode(pub1, {});
|
|
209
|
+
const derived2 = slip0010DerivePath(TEST_SEED, [44, 501, 0, 0]);
|
|
210
|
+
const pub2 = ed25519.getPublicKey(derived2.privateKey);
|
|
211
|
+
const addr2 = solanaEncoder.encode(pub2, {});
|
|
212
|
+
expect(addr1).toBe(addr2);
|
|
213
|
+
});
|
|
214
|
+
it("different account indices produce different addresses", () => {
|
|
215
|
+
const addresses = new Set();
|
|
216
|
+
for (let i = 0; i < 5; i++) {
|
|
217
|
+
// m/44'/501'/i'/0'
|
|
218
|
+
const derived = slip0010DerivePath(TEST_SEED, [44, 501, i, 0]);
|
|
219
|
+
const publicKey = ed25519.getPublicKey(derived.privateKey);
|
|
220
|
+
const address = solanaEncoder.encode(publicKey, {});
|
|
221
|
+
addresses.add(address);
|
|
222
|
+
}
|
|
223
|
+
expect(addresses.size).toBe(5);
|
|
224
|
+
});
|
|
225
|
+
it("privkey and pubkey are consistent via Ed25519", () => {
|
|
226
|
+
const derived = slip0010DerivePath(TEST_SEED, [44, 501, 0, 0]);
|
|
227
|
+
const publicKey = ed25519.getPublicKey(derived.privateKey);
|
|
228
|
+
// Sign something and verify to prove the key pair is valid
|
|
229
|
+
const message = new TextEncoder().encode("test message");
|
|
230
|
+
const signature = ed25519.sign(message, derived.privateKey);
|
|
231
|
+
const valid = ed25519.verify(signature, message, publicKey);
|
|
232
|
+
expect(valid).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
//# sourceMappingURL=sweep-key-parity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sweep-key-parity.test.js","sourceRoot":"","sources":["../../src/__tests__/sweep-key-parity.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tron-encoder.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/tron-encoder.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { HDKey } from "@scure/bip32";
|
|
2
|
+
import { describe, expect, it } from "vitest";
|
|
3
|
+
import { hexToTron, isTronAddress, tronToHex } from "../tron/address-convert.js";
|
|
4
|
+
import { encodeKeccakB58Address, keccakB58Encoder } from "../tron/encoder.js";
|
|
5
|
+
const TEST_XPUB = "xpub6BnqJwdqnXEZdkynN5CsrYZr3MULY933SdLrswFfKPDFandTXPQDWY225FveTPUJXS8D91Ddp7FEfaGrvVxuMBGQsyoBYRLu6VMB3Ni2H2Z";
|
|
6
|
+
function derivePublicKey(xpub, index) {
|
|
7
|
+
const master = HDKey.fromExtendedKey(xpub);
|
|
8
|
+
const child = master.deriveChild(0).deriveChild(index);
|
|
9
|
+
if (!child.publicKey)
|
|
10
|
+
throw new Error("Failed to derive public key");
|
|
11
|
+
return child.publicKey;
|
|
12
|
+
}
|
|
13
|
+
describe("Tron keccak-b58check encoder", () => {
|
|
14
|
+
it("produces T... addresses with default version byte 0x41", () => {
|
|
15
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
16
|
+
const address = keccakB58Encoder.encode(pubkey, {});
|
|
17
|
+
expect(address).toMatch(/^T[1-9A-HJ-NP-Za-km-z]+$/);
|
|
18
|
+
});
|
|
19
|
+
it("index 0 produces TDTkBJWfXqfCPhNAgHxmgPNHigJEg4ghww", () => {
|
|
20
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
21
|
+
const address = keccakB58Encoder.encode(pubkey, {});
|
|
22
|
+
expect(address).toBe("TDTkBJWfXqfCPhNAgHxmgPNHigJEg4ghww");
|
|
23
|
+
});
|
|
24
|
+
it("produces different addresses for different indices", () => {
|
|
25
|
+
const addr0 = keccakB58Encoder.encode(derivePublicKey(TEST_XPUB, 0), {});
|
|
26
|
+
const addr1 = keccakB58Encoder.encode(derivePublicKey(TEST_XPUB, 1), {});
|
|
27
|
+
expect(addr0).not.toBe(addr1);
|
|
28
|
+
});
|
|
29
|
+
it("produces consistent results for the same key", () => {
|
|
30
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
31
|
+
const addr1 = keccakB58Encoder.encode(pubkey, {});
|
|
32
|
+
const addr2 = keccakB58Encoder.encode(pubkey, {});
|
|
33
|
+
expect(addr1).toBe(addr2);
|
|
34
|
+
});
|
|
35
|
+
it("respects custom version param", () => {
|
|
36
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
37
|
+
// Different version byte produces different address
|
|
38
|
+
const addr41 = keccakB58Encoder.encode(pubkey, {});
|
|
39
|
+
const addr00 = keccakB58Encoder.encode(pubkey, { version: "0" });
|
|
40
|
+
expect(addr41).not.toBe(addr00);
|
|
41
|
+
});
|
|
42
|
+
it("encodeKeccakB58Address function works directly", () => {
|
|
43
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
44
|
+
const address = encodeKeccakB58Address(pubkey, 0x41);
|
|
45
|
+
expect(address).toMatch(/^T/);
|
|
46
|
+
});
|
|
47
|
+
it("reports encoding type as keccak-b58check", () => {
|
|
48
|
+
expect(keccakB58Encoder.encodingType()).toBe("keccak-b58check");
|
|
49
|
+
});
|
|
50
|
+
it("addresses only contain valid Base58 characters", () => {
|
|
51
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
52
|
+
const address = keccakB58Encoder.encode(pubkey, {});
|
|
53
|
+
expect(address).not.toMatch(/[0OIl]/);
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
describe("Tron address conversion", () => {
|
|
57
|
+
it("tronToHex converts T... to 0x hex", () => {
|
|
58
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
59
|
+
const tronAddr = keccakB58Encoder.encode(pubkey, {});
|
|
60
|
+
const hex = tronToHex(tronAddr);
|
|
61
|
+
expect(hex).toMatch(/^0x[0-9a-f]{40}$/);
|
|
62
|
+
});
|
|
63
|
+
it("hexToTron converts 0x hex back to T...", () => {
|
|
64
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
65
|
+
const tronAddr = keccakB58Encoder.encode(pubkey, {});
|
|
66
|
+
const hex = tronToHex(tronAddr);
|
|
67
|
+
const roundTrip = hexToTron(hex);
|
|
68
|
+
expect(roundTrip).toBe(tronAddr);
|
|
69
|
+
});
|
|
70
|
+
it("tronToHex throws on non-T address", () => {
|
|
71
|
+
expect(() => tronToHex("D7abc123")).toThrow("Not a Tron address");
|
|
72
|
+
});
|
|
73
|
+
it("hexToTron throws on wrong-length hex", () => {
|
|
74
|
+
expect(() => hexToTron("0x1234")).toThrow("Invalid hex address length");
|
|
75
|
+
});
|
|
76
|
+
it("isTronAddress detects T... addresses", () => {
|
|
77
|
+
const pubkey = derivePublicKey(TEST_XPUB, 0);
|
|
78
|
+
const tronAddr = keccakB58Encoder.encode(pubkey, {});
|
|
79
|
+
expect(isTronAddress(tronAddr)).toBe(true);
|
|
80
|
+
expect(isTronAddress("0x1234567890abcdef1234567890abcdef12345678")).toBe(false);
|
|
81
|
+
expect(isTronAddress("D7abcdef")).toBe(false);
|
|
82
|
+
});
|
|
83
|
+
it("round-trip through multiple addresses", () => {
|
|
84
|
+
for (let i = 0; i < 5; i++) {
|
|
85
|
+
const pubkey = derivePublicKey(TEST_XPUB, i);
|
|
86
|
+
const tronAddr = keccakB58Encoder.encode(pubkey, {});
|
|
87
|
+
const hex = tronToHex(tronAddr);
|
|
88
|
+
const back = hexToTron(hex);
|
|
89
|
+
expect(back).toBe(tronAddr);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
//# sourceMappingURL=tron-encoder.test.js.map
|