@hexora/address-guard 1.0.0 → 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.
Files changed (3) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +495 -0
  3. package/package.json +3 -2
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniil Sapielkin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,495 @@
1
+ # HEXORA
2
+
3
+ **Web3 Security SDK** — detects address poisoning attacks before users lose funds.
4
+
5
+ [![npm](https://img.shields.io/npm/v/hexora)](https://www.npmjs.com/package/hexora)
6
+ [![size](https://img.shields.io/bundlephobia/minzip/hexora)](https://bundlephobia.com/package/hexora)
7
+ [![license](https://img.shields.io/npm/l/hexora)](./LICENSE)
8
+
9
+ ---
10
+
11
+ ## The Problem
12
+
13
+ Address poisoning attacks cost millions every year. An attacker creates a wallet with the same first and last characters as a legitimate address, sends a tiny transaction to the victim, and waits for them to copy the wrong address from their history.
14
+
15
+ **MetaMask, Trust Wallet, Ledger Live** — none of them detect this. HEXORA fills that gap at the SDK level.
16
+
17
+ ```
18
+ Legit: 0xEF70efAf74A3caAbF254E786F834133864BC80E6
19
+ Fake: 0xe7d40975DD0396Fc81A39b0ED1f2b7aCE1BC80E6
20
+ ^^^^^ different middle ^^^^^
21
+ ```
22
+
23
+ These are real addresses from a live attack on BNB Chain. The victim `0x7265BD...075a91` had `0xEF70ef...BC80E6` in their history — the attacker deployed `0xe7d409...BC80E6` with matching first and last 4 chars and sent a [zero-value transfer](https://bscscan.com/tx/0x67e2b135f1255fa45db213d2da8231331c5b3c681031553b17e543bc7292acc8) to poison the history.
24
+
25
+ ---
26
+
27
+ ### Real case
28
+
29
+ https://x.com/incrypted/status/2029865814342398139
30
+
31
+ ---
32
+
33
+ ## Install
34
+
35
+ ```bash
36
+ npm install hexora
37
+ # or
38
+ pnpm add hexora
39
+ ```
40
+
41
+ ---
42
+
43
+ ## Quick Start
44
+
45
+ ```ts
46
+ import { checkAddress } from "hexora";
47
+
48
+ const result = await checkAddress({
49
+ userAddress: "0x...", // connected wallet
50
+ inputAddress: "0x...", // address the user is about to send to
51
+ provider: window.ethereum,
52
+ });
53
+
54
+ if (result.scam) {
55
+ console.warn(result.reason); // "batch_poisoning"
56
+ console.warn(result.riskLevel); // "critical"
57
+ }
58
+ ```
59
+
60
+ ---
61
+
62
+ ## How It Works
63
+
64
+ Every `checkAddress()` call runs through a multi-layer pipeline:
65
+
66
+ ```
67
+ 1. Provider Detection — auto-detects MetaMask / WalletConnect / Phantom / any EIP-1193
68
+ 2. Chain Resolution — reads chainId from provider, no manual chain param needed
69
+ 3. History Fetch — fetches last N transactions for both addresses in parallel
70
+ 4. Similarity Check — weighted prefix/suffix/Levenshtein comparison
71
+ 5. Poison Detection — zero-value transfers, batch poisoning, transferFrom spoofing, dust
72
+ 6. Input Addr Analysis — checks if inputAddress itself is a known attacker wallet
73
+ 7. Risk Scoring — combines all signals into a final structured result
74
+ ```
75
+
76
+ ---
77
+
78
+ ## Response
79
+
80
+ ```ts
81
+ {
82
+ scam: true,
83
+ reason: "zero_value_transfer",
84
+ riskLevel: "critical",
85
+ similarityScore: 87,
86
+ confidence: 96,
87
+ matchedAddress: "0xEF70efAf74A3caAbF254E786F834133864BC80E6",
88
+ details: {
89
+ chain: "bnb",
90
+ userAddress: "0x7265BDC334276e496d284D2Dcc2918aA59075a91",
91
+ inputAddress: "0xe7d40975DD0396Fc81A39b0ED1f2b7aCE1BC80E6",
92
+ historyScanned: 20,
93
+ poisonTxFound: true,
94
+ zeroValueFound: true,
95
+ dustFound: false,
96
+ },
97
+ error: null
98
+ }
99
+ ```
100
+
101
+ ### `riskLevel` values
102
+
103
+ | Value | Meaning |
104
+ | ---------- | ----------------------------------------- |
105
+ | `none` | No signals detected — address looks clean |
106
+ | `low` | Weak signal — worth monitoring |
107
+ | `medium` | Suspicious — consider warning the user |
108
+ | `high` | Strong signal — warn the user |
109
+ | `critical` | Confirmed attack pattern — block the tx |
110
+
111
+ ### `reason` values
112
+
113
+ | Value | Description |
114
+ | ------------------------ | ------------------------------------------------------------ |
115
+ | `address_poisoning` | Similar address found in user's transaction history |
116
+ | `zero_value_transfer` | Incoming ERC-20 transfer with value = 0 |
117
+ | `batch_poisoning` | Attacker contract poisoned history in a single batch tx |
118
+ | `transferfrom_spoofing` | Contract forced a zero-value transferFrom using your address |
119
+ | `dust_attack` | Micro-transaction received from unknown address |
120
+ | `new_suspicious_address` | Young address with outgoing-only activity |
121
+
122
+ ---
123
+
124
+ ## API Reference
125
+
126
+ ### `checkAddress(params)`
127
+
128
+ ```ts
129
+ import { checkAddress } from "hexora"
130
+
131
+ const result = await checkAddress(params: CheckAddressParams): Promise<CheckResult>
132
+ ```
133
+
134
+ #### `CheckAddressParams`
135
+
136
+ | Field | Type | Required | Default | Description |
137
+ | --------------------- | ----------------- | -------- | --------------------- | -------------------------------------- |
138
+ | `userAddress` | `string` | ✅ | — | The connected wallet address |
139
+ | `inputAddress` | `string` | ✅ | — | The address the user wants to send to |
140
+ | `provider` | `RawProvider` | ✅ | — | EIP-1193 or Phantom provider |
141
+ | `historyLimit` | `number` | ❌ | `20` | Transactions to scan (max 50) |
142
+ | `similarityThreshold` | `number` | ❌ | `85` | Minimum score to flag (0–100) |
143
+ | `dustThreshold` | `bigint` | ❌ | `10_000_000_000_000n` | Wei threshold for dust detection |
144
+ | `historyProvider` | `HistoryProvider` | ❌ | — | Custom transaction history source |
145
+ | `apiKeys.etherscan` | `string` | ❌ | — | Etherscan API key (higher rate limits) |
146
+ | `apiKeys.bscscan` | `string` | ❌ | — | BscScan API key |
147
+ | `apiKeys.polygonscan` | `string` | ❌ | — | Polygonscan API key |
148
+
149
+ ---
150
+
151
+ ## Supported Chains
152
+
153
+ | Chain | Status | Auto-detected from provider |
154
+ | --------- | ------ | --------------------------- |
155
+ | Ethereum | ✅ | `chainId: 0x1` |
156
+ | BNB Chain | ✅ | `chainId: 0x38` |
157
+ | Polygon | ✅ | `chainId: 0x89` |
158
+ | Avalanche | ✅ | `chainId: 0xa86a` |
159
+ | Arbitrum | ✅ | `chainId: 0xa4b1` |
160
+ | Optimism | ✅ | `chainId: 0xa` |
161
+ | Solana | 🔜 | Phantom provider |
162
+ | Bitcoin | 🔜 | Coming soon |
163
+ | Tron | 🔜 | Coming soon |
164
+
165
+ Chain is resolved **automatically** from the provider — you never pass `chain` manually.
166
+
167
+ ---
168
+
169
+ ## Supported Wallets & Providers
170
+
171
+ HEXORA auto-detects the provider type. Any wallet that implements **EIP-1193** works out of the box:
172
+
173
+ | Wallet | Works |
174
+ | ------------------- | ----- |
175
+ | MetaMask | ✅ |
176
+ | WalletConnect | ✅ |
177
+ | Coinbase Wallet | ✅ |
178
+ | Trust Wallet (EVM) | ✅ |
179
+ | Rainbow | ✅ |
180
+ | Rabby | ✅ |
181
+ | Ledger Live | ✅ |
182
+ | Phantom (Solana) | ✅ |
183
+ | Any EIP-1193 wallet | ✅ |
184
+
185
+ ---
186
+
187
+ ## Usage Examples
188
+
189
+ ### React
190
+
191
+ ```tsx
192
+ import { checkAddress } from "hexora";
193
+
194
+ const handleSend = async () => {
195
+ const result = await checkAddress({
196
+ userAddress: account,
197
+ inputAddress: pastedAddress,
198
+ provider: window.ethereum,
199
+ });
200
+
201
+ if (result.scam) {
202
+ alert(`⚠️ ${result.reason} — risk: ${result.riskLevel}`);
203
+ return;
204
+ }
205
+
206
+ // proceed with transaction
207
+ };
208
+ ```
209
+
210
+ ### Next.js
211
+
212
+ Works in client components only — `window.ethereum` is not available server-side.
213
+
214
+ ```tsx
215
+ "use client";
216
+ import { checkAddress } from "hexora";
217
+
218
+ export function SendForm() {
219
+ const handleSubmit = async (e: React.FormEvent) => {
220
+ e.preventDefault();
221
+ const result = await checkAddress({
222
+ userAddress: userWallet,
223
+ inputAddress: recipientInput,
224
+ provider: window.ethereum,
225
+ });
226
+ if (result.scam) setWarning(result.reason);
227
+ };
228
+ }
229
+ ```
230
+
231
+ ### Vue 3
232
+
233
+ ```ts
234
+ import { checkAddress } from "hexora";
235
+
236
+ async function onSend() {
237
+ const result = await checkAddress({
238
+ userAddress: wallet.address,
239
+ inputAddress: form.recipient,
240
+ provider: window.ethereum,
241
+ });
242
+
243
+ if (result.scam) {
244
+ warningMessage.value = result.reason;
245
+ return;
246
+ }
247
+ }
248
+ ```
249
+
250
+ ### Svelte
251
+
252
+ ```ts
253
+ import { checkAddress } from "hexora";
254
+
255
+ async function handleSend() {
256
+ const result = await checkAddress({
257
+ userAddress: $wallet.address,
258
+ inputAddress: recipientInput,
259
+ provider: window.ethereum,
260
+ });
261
+
262
+ if (result.scam) {
263
+ warning = result.reason;
264
+ }
265
+ }
266
+ ```
267
+
268
+ ### Angular
269
+
270
+ ```ts
271
+ import { Injectable } from "@angular/core";
272
+ import { checkAddress } from "hexora";
273
+
274
+ @Injectable({ providedIn: "root" })
275
+ export class HexoraService {
276
+ async check(userAddress: string, inputAddress: string) {
277
+ return checkAddress({
278
+ userAddress,
279
+ inputAddress,
280
+ provider: (window as any).ethereum,
281
+ });
282
+ }
283
+ }
284
+ ```
285
+
286
+ ### React Native
287
+
288
+ > Requires a WalletConnect or custom EIP-1193 compatible provider — `window.ethereum` is not available in React Native.
289
+
290
+ ```tsx
291
+ import { checkAddress } from "hexora";
292
+ import { useWalletConnectModal } from "@walletconnect/modal-react-native";
293
+
294
+ export function SendScreen() {
295
+ const { provider } = useWalletConnectModal();
296
+
297
+ const handleSend = async () => {
298
+ const result = await checkAddress({
299
+ userAddress: connectedAddress,
300
+ inputAddress: recipientAddress,
301
+ provider: provider, // WalletConnect EIP-1193 provider
302
+ });
303
+
304
+ if (result.scam) {
305
+ Alert.alert("⚠️ Warning", `Suspicious address: ${result.reason}`);
306
+ return;
307
+ }
308
+ };
309
+ }
310
+ ```
311
+
312
+ ### Node.js / Backend
313
+
314
+ Useful for pre-validating addresses in a backend before broadcasting transactions.
315
+
316
+ ```ts
317
+ import { checkAddress } from "hexora";
318
+ import type { EIP1193Provider } from "hexora";
319
+
320
+ // Node.js has no window.ethereum — use your own RPC provider adapter
321
+ class NodeProvider implements EIP1193Provider {
322
+ async request({ method }: { method: string }): Promise<unknown> {
323
+ if (method === "eth_chainId") return "0x1";
324
+ throw new Error(`Unsupported method: ${method}`);
325
+ }
326
+ }
327
+
328
+ const result = await checkAddress({
329
+ userAddress: "0x7265BDC334276e496d284D2Dcc2918aA59075a91",
330
+ inputAddress: "0xe7d40975DD0396Fc81A39b0ED1f2b7aCE1BC80E6",
331
+ provider: new NodeProvider(),
332
+ apiKeys: { etherscan: process.env.ETHERSCAN_API_KEY },
333
+ });
334
+ ```
335
+
336
+ ### Browser Extension (Chrome / Firefox)
337
+
338
+ ```ts
339
+ import { checkAddress } from "hexora";
340
+
341
+ chrome.runtime.onMessage.addListener(async (msg, _sender, sendResponse) => {
342
+ if (msg.type === "CHECK_ADDRESS") {
343
+ const result = await checkAddress({
344
+ userAddress: msg.userAddress,
345
+ inputAddress: msg.inputAddress,
346
+ provider: window.ethereum,
347
+ });
348
+ sendResponse(result);
349
+ }
350
+ return true; // keep channel open for async response
351
+ });
352
+ ```
353
+
354
+ ### With API Keys (higher rate limits)
355
+
356
+ ```ts
357
+ const result = await checkAddress({
358
+ userAddress: account,
359
+ inputAddress: pastedAddress,
360
+ provider: window.ethereum,
361
+ apiKeys: {
362
+ etherscan: "YOUR_ETHERSCAN_KEY",
363
+ bscscan: "YOUR_BSCSCAN_KEY",
364
+ },
365
+ });
366
+ ```
367
+
368
+ ### Custom History Provider
369
+
370
+ Implement `HistoryProvider` to plug in your own data source (Alchemy, Moralis, your own indexer):
371
+
372
+ ```ts
373
+ import { checkAddress } from "hexora";
374
+ import type { HistoryProvider, NormalizedTransaction, ChainId } from "hexora";
375
+
376
+ class AlchemyProvider implements HistoryProvider {
377
+ async getTransactions(
378
+ address: string,
379
+ chain: ChainId,
380
+ limit: number
381
+ ): Promise<NormalizedTransaction[]> {
382
+ // fetch from Alchemy / Moralis / your indexer
383
+ return [];
384
+ }
385
+ }
386
+
387
+ const result = await checkAddress({
388
+ userAddress: account,
389
+ inputAddress: pastedAddress,
390
+ provider: window.ethereum,
391
+ historyProvider: new AlchemyProvider(),
392
+ });
393
+ ```
394
+
395
+ ---
396
+
397
+ ## Platform Support
398
+
399
+ | Platform | Status | Notes |
400
+ | ------------------ | ------ | ----------------------------------------- |
401
+ | React | ✅ | Full support |
402
+ | Next.js | ✅ | Client components only |
403
+ | Vue 3 | ✅ | Full support |
404
+ | Svelte / SvelteKit | ✅ | Full support |
405
+ | Angular | ✅ | Full support |
406
+ | React Native | ✅ | Requires WalletConnect or custom provider |
407
+ | Node.js | ✅ | Custom EIP-1193 adapter required |
408
+ | Browser Extension | ✅ | Chrome, Firefox, Edge |
409
+ | Vanilla JS | ✅ | Full support |
410
+
411
+ ---
412
+
413
+ ## Coming Soon
414
+
415
+ Native SDK support is planned for mobile platforms — no JS bridge required:
416
+
417
+ | Platform | Status | Notes |
418
+ | ---------------- | ------ | -------------------------------------- |
419
+ | Swift / iOS | 🔜 | Native Swift SDK |
420
+ | Kotlin / Android | 🔜 | Native Kotlin SDK |
421
+ | Flutter | 🔜 | Dart package |
422
+ | Rust | 🔜 | For CLI tools and backend integrations |
423
+ | Python | 🔜 | For backend / analytics use cases |
424
+
425
+ > Want to contribute a native SDK? Open an issue or reach out.
426
+
427
+ ---
428
+
429
+ ## Error Handling
430
+
431
+ HEXORA never throws. All errors are returned in `result.error`:
432
+
433
+ ```ts
434
+ const result = await checkAddress({ ... })
435
+
436
+ if (result.error) {
437
+ console.error(result.error.code) // "network_unavailable"
438
+ console.error(result.error.message) // human-readable message
439
+ }
440
+ ```
441
+
442
+ ### Error codes
443
+
444
+ | Code | Description |
445
+ | ---------------------- | ----------------------------------- |
446
+ | `invalid_address` | Address format is invalid for chain |
447
+ | `unknown_provider` | Provider type could not be detected |
448
+ | `unsupported_chain` | Chain is not supported yet |
449
+ | `network_unavailable` | Could not connect to provider |
450
+ | `history_fetch_failed` | Transaction history fetch failed |
451
+ | `rate_limited` | API rate limit hit |
452
+ | `unknown` | Unexpected error |
453
+
454
+ ---
455
+
456
+ ## Packages
457
+
458
+ This is a monorepo. You can install the full SDK or individual packages:
459
+
460
+ | Package | Description | Size |
461
+ | ----------------------- | ---------------------------- | ----------- |
462
+ | `hexora` | Full SDK — all modules | ~3.5 kB |
463
+ | `@hexora/address-guard` | Address poisoning detection | ~3.5 kB |
464
+ | `@hexora/core` | Shared utilities and types | ~2.2 kB |
465
+ | `@hexora/domain-guard` | Domain phishing detection 🔜 | coming soon |
466
+
467
+ ---
468
+
469
+ ## Architecture
470
+
471
+ ```
472
+ hexora/
473
+ ├── packages/
474
+ │ ├── core/ — shared: types, provider detection, similarity, cache
475
+ │ ├── address-guard/ — address poisoning detection logic
476
+ │ ├── domain-guard/ — domain phishing detection (coming soon)
477
+ │ └── hexora/ — single entry point re-exporting all modules
478
+ ```
479
+
480
+ Each package is independent. `@hexora/core` is shared — never duplicate logic across modules.
481
+
482
+ ---
483
+
484
+ ## Privacy
485
+
486
+ - All analysis runs **locally** — no data is sent to HEXORA servers
487
+ - Transaction history is fetched directly from public block explorer APIs
488
+ - In-memory cache only (5 min TTL) — cleared when the page unloads
489
+ - No `localStorage`, no cookies, no tracking
490
+
491
+ ---
492
+
493
+ ## License
494
+
495
+ MIT — free for personal and commercial use.
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@hexora/address-guard",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
+ "license": "MIT",
4
5
  "type": "module",
5
6
  "main": "./dist/index.js",
6
7
  "module": "./dist/index.js",
@@ -15,7 +16,7 @@
15
16
  "dist"
16
17
  ],
17
18
  "dependencies": {
18
- "@hexora/core": "1.0.0"
19
+ "@hexora/core": "1.0.1"
19
20
  },
20
21
  "devDependencies": {
21
22
  "typescript": "^5.5.4",