@tribridge/triverify
v0.2.0
Published
A zero-dependency, type-safe TypeScript SDK for cross-chain crypto **address validation, chain detection, on-chain verification, domain resolution, and batch processing**.
Readme
@tribridge/triverify
A zero-dependency, type-safe TypeScript SDK for cross-chain crypto address validation, chain detection, on-chain verification, domain resolution, and batch processing.
One SDK. Every major chain. Validate any address before it ever touches your app.
Features
- Zero external dependencies — core validation is pure TypeScript, so it ships tiny and never bloats your bundle.
- Multi-chain format validation — strict per-chain rules (length, charset, prefix, EIP-55 checksum where applicable).
- Confidence-based auto-detection —
detect()scores every supported chain and reports the best match, flagging ambiguity when two chains share a format. - Real on-chain verification —
verifyOnChain()checks whether an address is actually present / active on the network using free public RPC endpoints (with automatic fallback). - Address-type detection — distinguish wallets (EOAs) from contracts / programs on-chain.
- Domain resolution — resolve
vitalik.eth,bonfida.sol,mysten.sui,name.apt,name.ton,name.tron, and Unstoppable.cryptodomains to raw addresses. - Batch validation — validate, detect, normalize, dedupe, and summarize thousands of addresses in parallel.
- First-class TypeScript — fully typed inputs and return values, tree-shakeable ESM + CJS builds.
Supported chains
| Chain | Format notes |
| ---------- | ----------------------------------------------------------------- |
| Ethereum | 0x + 40 hex (EIP-55 mixed-case checksum aware) |
| Polygon | EVM, 0x + 20 bytes (shares Ethereum format) |
| Bitcoin | Legacy (1…), P2SH (3…), SegWit / Bech32 (bc1…) |
| Solana | Base58, 32–44 chars |
| Tron | Base58, T… prefix, 34 chars |
| Sui | 0x + 32 bytes (64 hex) |
| Aptos | 0x + up to 32 bytes (leading zeros may be omitted) |
| TON | Raw (-1: / 0:) and user-friendly base64url |
Sui vs Aptos: both use an identical
0x+ 32-byte address space, so a 66-char0xaddress is format-ambiguous.detect()returns the address as the most likely chain and attaches anambiguityNotetelling you to verify on-chain. A 42-char0xaddress is unambiguously EVM/Polygon and is never reported as Aptos.
Installation
npm install @tribridge/triverify
# or
pnpm add @tribridge/triverify
yarn add @tribridge/triverifyQuick start
import { validate, detect, verifyOnChain, Chain } from '@tribridge/triverify';
// 1. Validate a single address against a known chain
validate('0x742d35Cc6634C0532925a3b844Bc454e4438f44e', Chain.Ethereum);
// → { isValid: true, chain: 'ethereum', confidence: 1, reason: '...' }
// 2. Auto-detect the chain from the address alone
detect('HN7cABqL36GNBbpR9S99S46ptP99P5y7aZ');
// → top match: { chain: 'solana', score: 100 }
// 3. Confirm it is live on-chain
await verifyOnChain('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', Chain.Ethereum);
// → { exists: true, active: true, type: 'wallet', balance: '...' }API Reference
validate(address, chain)
Validates a single address against a specific chain.
function validate(address: string, chain: Chain): ValidationResult;Returns ValidationResult:
interface ValidationResult {
isValid: boolean;
chain: Chain | null;
confidence: number; // 0–1
reason?: string; // human-readable explanation
}detect(address)
Detects which chain(s) an address most likely belongs to using confidence scoring (character set + prefix + length + format/checksum, each worth up to 25 points).
function detect(address: string): DetectResult;Returns DetectResult:
interface DetectResult {
address: string;
results: ConfidenceResult[]; // all chains that matched, sorted by score desc
isAmbiguous: boolean; // true when the top two chains are within 10 points
topMatch: ConfidenceResult | null;
ambiguityNote?: string | null; // set when the match collides with an identical-format chain (e.g. Sui/Aptos)
}
interface ConfidenceResult {
chain: Chain;
score: number; // 0–100
reasons: {
prefixMatch: boolean;
lengthMatch: boolean;
checksumValid: boolean;
characterSetMatch: boolean;
};
}Only chains that score as genuinely valid (≥ 60) are returned, so you never see misleading low-confidence noise.
import { detect } from '@tribridge/triverify';
const { results, ambiguityNote } = detect('0x1e2e...daa');
// results[0] → { chain: 'sui', score: 100, reasons: { ...: true } }
// ambiguityNote → "This address also matches the Aptos format …"verifyOnChain(address, chain, options?)
Goes beyond format checks and confirms whether the address is present / active on the blockchain.
async function verifyOnChain(
address: string,
chain: Chain,
options?: VerifyOptions
): Promise<VerifyResult>;VerifyOptions:
interface VerifyOptions {
rpcUrl?: string; // override the public endpoint
apiKey?: string; // e.g. Trongrid / Toncenter API key
timeoutMs?: number; // per-request timeout (default 8000)
}Returns VerifyResult:
interface VerifyResult {
address: string;
chain: Chain;
exists: boolean; // account present on-chain
active: boolean; // has balance / nonce / txns / code
type: 'wallet' | 'contract' | 'unknown';
balance?: string; // native balance (string, in base units)
transactionCount?: number;
source: string; // endpoint that was queried
supported: boolean;
error?: string; // present when the check could not be completed
}Endpoints used (no API key required for the defaults; Solana and Sui try multiple public nodes with automatic fallback):
| Chain | Default endpoint |
| -------- | ------------------------------------------------------------ |
| Ethereum | https://ethereum-rpc.publicnode.com |
| Polygon | https://polygon-bor-rpc.publicnode.com |
| Bitcoin | https://mempool.space/api |
| Solana | https://solana-rpc.publicnode.com (+ onfinality fallback) |
| Tron | https://api.trongrid.io |
| Sui | https://graphql.mainnet.sui.io/graphql (+ fallbacks) |
| Aptos | https://fullnode.mainnet.aptoslabs.com/v1 |
| TON | https://toncenter.com/api/v2 |
import { verifyOnChain, Chain } from '@tribridge/triverify';
const res = await verifyOnChain('0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', Chain.Ethereum);
console.log(res.exists, res.active, res.type, res.balance);getAddressType(address, chain, rpcUrl)
Classifies an on-chain address as a wallet (EOA) or contract / program.
async function getAddressType(
address: string,
chain: Chain,
rpcUrl: string
): Promise<AddressTypeResult>;interface AddressTypeResult {
type: 'wallet' | 'contract' | 'unknown';
supported: boolean;
reason?: string;
rawResponse?: any;
}resolveAddress(input, options?)
Resolves a human-readable domain to a raw address across supported naming services.
async function resolveAddress(
input: string,
options?: ResolutionOptions
): Promise<ResolutionResult>;interface ResolutionOptions {
rpcUrl?: string; // override the default RPC / gateway per service
apiKey?: string; // for Unstoppable Domains
}
interface ResolutionResult {
address: string | null; // resolved raw address (or the input if not a domain)
wasResolved: boolean;
domain: string | null;
error?: string;
}Supported TLDs: .eth (ENS), .sol (SNS), .sui (SuiNS), .apt (ANS), .ton (TON DNS), .tron (Tron), .crypto (Unstoppable Domains).
import { resolveAddress } from '@tribridge/triverify';
const { address, wasResolved } = await resolveAddress('vitalik.eth');
// → { address: '0xd8dA...6045', wasResolved: true }validateBatch(addresses, options?)
Validates a large list of addresses in parallel, with detection, normalization, dedupe, and ambiguity reporting.
async function validateBatch(
addresses: string[],
options?: BatchOptions
): Promise<BatchResult>;interface BatchOptions {
chain?: Chain; // validate against one chain only
detectAll?: boolean; // auto-detect chain per address
includeDuplicates?: boolean;
normalize?: boolean; // lowercase EVM/Sui/Aptos, leave Base58/Base64 as-is
}
interface BatchResult {
valid: Array<{ address: string; chain: Chain; confidence: number; normalized?: string }>;
invalid: Array<{ address: string; reason: string }>;
duplicates: string[];
ambiguous: Array<{ address: string; matches: ConfidenceResult[] }>;
summary: { total: number; validCount: number; invalidCount: number; duplicateCount: number; ambiguousCount: number };
}import { validateBatch } from '@tribridge/triverify';
const { valid, invalid, duplicates, ambiguous, summary } = await validateBatch(
['0x742d...', 'HN7cAB...', 'bc1qxy...'],
{ detectAll: true, normalize: true }
);
console.log(summary);Notes & caveats
- Format ≠ ownership. Validation only proves an address is well-formed. Use
verifyOnChain()to confirm it exists / is active. - Sui / Aptos share an identical address format; the SDK cannot tell them apart without an on-chain lookup. The
ambiguityNotefield is your cue to verify on-chain. - Public RPC endpoints are rate-limited and best-effort. For production, pass your own
rpcUrl(andapiKeywhere required) viaVerifyOptions.
License
MIT
