Web3 Signals
Passive wallet extension detection for Sybil resistance.
How Wallet Detection Works
FingerprintIQ detects installed Web3 wallet extensions without requiring a wallet connection. Detection is entirely passive — there is no popup, no permission request, and no signature prompt. The user is never aware that wallet detection occurred.
Wallet detection works by checking for known JavaScript objects that wallet extensions inject into the window scope. FingerprintIQ never reads wallet addresses, private keys, or any account data — it only detects which wallet software is installed.
Detection uses two complementary methods:
- EIP-6963 provider discovery — The modern standard where wallets announce themselves via a
eip6963:announceProviderevent. Works with all compliant wallets. - Legacy window object detection — Checks known wallet-specific properties (e.g.,
window.ethereum.isMetaMask) for wallets that predate EIP-6963.
Supported Wallets
| Wallet | Chain | Detection Method |
|---|---|---|
| MetaMask | EVM | window.ethereum.isMetaMask |
| Phantom | Solana | window.phantom.solana |
| Coinbase | EVM | window.ethereum.isCoinbaseWallet |
| Rabby | EVM | window.ethereum.isRabby |
| Rainbow | EVM | window.ethereum.isRainbow |
| Brave Wallet | EVM | window.ethereum.isBraveWallet |
| Zerion | EVM | window.ethereum.isZerion |
| Exodus | EVM | window.ethereum.isExodus |
| Solflare | Solana | window.solflare |
| Backpack | Solana | window.backpack |
| Keplr | Cosmos | window.keplr |
| UniSat | Bitcoin | window.unisat |
| OKX | Multi-chain | window.okxwallet |
| XDEFI | Multi-chain | window.xfi |
| TronLink | Tron | window.tronWeb |
Plus EIP-6963 discovery for any compliant wallet not listed above.
Response Format
json{ "wallets": { "detected": ["MetaMask", "Phantom"], "count": 2, "evmProviders": ["MetaMask"], "solanaProviders": ["Phantom"], "multipleWallets": true, "versions": { "MetaMask": "11.16.0" } }}
Wallet version information (versions) is available for wallets that expose it via their provider object. Not all wallets expose version numbers.
Connected Wallet Auto-Link
Detection tells you which wallets a visitor has installed. Auto-link goes one step further: when the user actually connects a wallet — through your dapp's existing connect button, Wagmi, RainbowKit, web3modal, or any other connector — FingerprintIQ notices the connection passively and ships the address to your project so it can be linked to the visitor and enriched on-chain.
You don't have to call any FingerprintIQ method for this to happen. The SDK attaches passive listeners (accountsChanged, EIP-6963, Solana connect) the moment you new FingerprintIQ(...). When an address shows up:
- The SDK debounces for ~300ms (in case more accounts arrive)
- It POSTs the address to
/v1/identify/wallet-linkwith thevisitorIdfrom your most recentidentify()call - The address is linked to the visitor in
visitor_wallets, the sybil score updates, and async enrichment fetches ENS, portfolio, wallet age, and labels
javascriptconst fiq = new FingerprintIQ({ apiKey });const result = await fiq.identify(); // visitorId is now known// ...later, your dapp shows its connect button.// User clicks → your wagmi/web3modal flow runs → MetaMask popup appears.// You don't need to call anything on `fiq` — the address is already on its// way to /v1/identify/wallet-link, the visitor profile is being enriched,// and any webhook you have configured for `wallet.identified` will fire.
Auto-link only runs after the first identify() returns. Addresses observed before that are buffered locally and shipped on the first identify call. If the wallet-link request fails (offline, etc.), the address stays in the local buffer and rides along on the next identify() call.
You can also subscribe to the same stream yourself for client-side personalization:
javascriptimport { onWalletAddress } from "@fingerprintiq/js";onWalletAddress((address, connection) => { // connection.method = "pre-connected" | "listener" | "manual" // connection.chain = "evm" | "solana" console.log("New wallet linked:", address, connection.provider);});
Sybil Detection Use Cases
Prevent users from claiming multiple airdrop allocations by linking different wallet addresses to the same device fingerprint.
javascriptconst result = await fiq.identify();const walletCount = result.web3?.walletsDetected.length ?? 0;// Same device claiming multiple airdrop walletsif (result.visitCount > 3 && walletCount > 1) { // This device has visited multiple times with multiple wallets // Flag as potential Sybil before allowing airdrop claim await flagSybilRisk(result.visitorId); showChallenge();}
Always verify the visitorId server-side before making airdrop decisions. Client-side checks alone can be bypassed.
Enforce one-device-one-vote to prevent governance manipulation by users running multiple wallet identities from a single machine.
javascriptconst result = await fiq.identify();// Check if this device has already votedconst hasVoted = await checkVoteStatus(result.visitorId);if (hasVoted) { showMessage("This device has already cast a vote in this proposal."); return;}// Record the vote linked to this device fingerprintawait recordVote(result.visitorId, selectedOption);
Enforce per-device minting limits to prevent bots from minting out collections.
javascriptconst result = await fiq.identify();// Block obvious bots before any wallet interactionif (result.botProbability > 0.5) { showCaptcha(); return;}// Enforce device-level mint limit regardless of wallet addressconst mintCount = await getDeviceMintCount(result.visitorId);if (mintCount >= MAX_MINTS_PER_DEVICE) { showMessage("Mint limit reached for this device."); return;}