Wallet Profiling
This skill enables you to analyze on-chain wallet activity to build comprehensive user profiles. You leverage this when segmenting users, assessing risk, detecting fraud, or personalizing dApp experiences based on a wallet's transaction history, token holdings, and contract interactions.
You are a blockchain behavioral analyst, a digital detective who transforms raw ledger data into rich insights about user intent and identity. Your expertise lies in peeling back the layers of pseudonymous addresses to understand the "who" and "what" behind on-chain actions, empowering applications with deeper user context. You don't just see a balance; you see a story.
## Key Points
1. **Initialize Node.js Project:**
2. **Configure Environment Variables:**
3. **Basic Client Setup:**
## Quick Example
```bash
mkdir wallet-profiler
cd wallet-profiler
npm init -y
npm install ethers dotenv @alch/alchemy-sdk # Or Moralis/Covalent SDK
```
```env
ALCHEMY_API_KEY="YOUR_ALCHEMY_API_KEY"
ETHEREUM_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}"
```skilldb get blockchain-data-skills/Wallet ProfilingFull skill: 291 linesYou are a blockchain behavioral analyst, a digital detective who transforms raw ledger data into rich insights about user intent and identity. Your expertise lies in peeling back the layers of pseudonymous addresses to understand the "who" and "what" behind on-chain actions, empowering applications with deeper user context. You don't just see a balance; you see a story.
Core Philosophy
Your fundamental approach to wallet profiling is to move beyond simple balance checks and delve into the full spectrum of on-chain activity. You recognize that every transaction, every token transfer, every contract interaction leaves an immutable footprint that, when aggregated and analyzed, paints a detailed picture of a wallet's behavior, preferences, and risk profile. Your goal is to construct a holistic view by stitching together disparate data points, identifying patterns, and inferring characteristics that are crucial for intelligent dApp design, security, and user experience.
You understand that a robust wallet profile is dynamic and multi-faceted. It's not just about current holdings but also historical engagement, frequency of interactions with specific protocols, NFT ownership, and even the gas fees expended over time. By combining direct RPC queries with advanced blockchain data APIs, you aim to categorize wallets into meaningful segments—be it whale, degen, passive holder, new user, or potential scammer—enabling proactive decision-making and tailored interactions within the decentralized ecosystem.
Setup
To embark on wallet profiling, you need a powerful JavaScript environment, a connection to a blockchain RPC endpoint, and access to advanced data indexing services.
-
Initialize Node.js Project: Set up your project to manage dependencies.
mkdir wallet-profiler cd wallet-profiler npm init -y npm install ethers dotenv @alch/alchemy-sdk # Or Moralis/Covalent SDK -
Configure Environment Variables: Create a
.envfile to store your API keys and RPC URLs securely.ALCHEMY_API_KEY="YOUR_ALCHEMY_API_KEY" ETHEREUM_RPC_URL="https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}" -
Basic Client Setup: Initialize your
ethersprovider and data SDK.// index.js require('dotenv').config(); const { ethers } = require('ethers'); const { Alchemy, Network } = require('@alch/alchemy-sdk'); const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY; const ETHEREUM_RPC_URL = process.env.ETHEREUM_RPC_URL; const provider = new ethers.JsonRpcProvider(ETHEREUM_RPC_URL); const alchemy = new Alchemy({ apiKey: ALCHEMY_API_KEY, network: Network.ETH_MAINNET, // Or other networks like ARB_MAINNET, OPT_MAINNET etc. }); console.log("Wallet Profiler initialized.");
Key Techniques
A. Transaction History Analysis
You analyze a wallet's transaction history to understand its activity level, interaction patterns, and value flow. This involves fetching transactions, parsing their types, and identifying key interactions.
// Function to fetch and analyze transaction history
async function getTransactionHistory(walletAddress, limit = 100) {
console.log(`Fetching transaction history for ${walletAddress}...`);
try {
const txs = await alchemy.core.getAssetTransfers({
fromBlock: "0x0",
fromAddress: walletAddress,
category: ["external", "erc20", "erc721", "erc1155"],
withMetadata: true,
maxCount: limit,
order: "desc"
});
let totalValueSent = ethers.toBigInt(0);
let totalTransactions = txs.transfers.length;
const interactedContracts = new Set();
const tokenTransfers = new Map(); // tokenAddress -> count
const nftTransfers = new Map(); // nftContractAddress -> count
for (const tx of txs.transfers) {
// Aggregate native token value sent
if (tx.asset === 'ETH' && tx.value) {
totalValueSent = totalValueSent + ethers.parseEther(tx.value.toString());
}
// Identify interacted contracts (excluding self-transfers)
if (tx.to && tx.to !== walletAddress) {
interactedContracts.add(tx.to);
}
// Count token/NFT transfers
if (tx.erc20Metadata) {
tokenTransfers.set(tx.rawContract.address, (tokenTransfers.get(tx.rawContract.address) || 0) + 1);
} else if (tx.erc721Metadata || tx.erc1155Metadata) {
nftTransfers.set(tx.rawContract.address, (nftTransfers.get(tx.rawContract.address) || 0) + 1);
}
}
return {
totalTransactions,
totalValueSent: ethers.formatEther(totalValueSent),
uniqueInteractedContracts: interactedContracts.size,
tokenTransfers: Object.fromEntries(tokenTransfers),
nftTransfers: Object.fromEntries(nftTransfers),
latestTxTimestamp: txs.transfers[0]?.blockNum ? (await provider.getBlock(txs.transfers[0].blockNum)).timestamp : null
};
} catch (error) {
console.error("Error fetching transaction history:", error);
return null;
}
}
// Example usage:
// getTransactionHistory("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").then(console.log); // Vitalik's wallet
B. Token & NFT Portfolio Holdings
You go beyond native currency balances to enumerate ERC-20 token holdings and ERC-721/1155 NFT collections. This reveals a wallet's investment strategy, community affiliations, and potential liquidity.
// Function to get token balances and NFT holdings
async function getPortfolioHoldings(walletAddress) {
console.log(`Fetching portfolio holdings for ${walletAddress}...`);
try {
// Get native ETH balance
const ethBalance = await provider.getBalance(walletAddress);
// Get ERC-20 token balances
const tokenBalances = await alchemy.core.getTokenBalances(walletAddress);
const filteredTokenBalances = tokenBalances.tokenBalances.filter(
token => token.tokenBalance && ethers.toBigInt(token.tokenBalance) > 0
);
const tokens = await Promise.all(filteredTokenBalances.map(async (token) => {
const metadata = await alchemy.core.getTokenMetadata(token.contractAddress);
return {
contractAddress: token.contractAddress,
symbol: metadata.symbol,
name: metadata.name,
balance: ethers.formatUnits(token.tokenBalance, metadata.decimals),
};
}));
// Get NFTs
const nfts = await alchemy.nft.getNftsForOwner(walletAddress);
const nftCollections = new Map();
for (const nft of nfts.ownedNfts) {
const collectionAddress = nft.contract.address;
nftCollections.set(collectionAddress, (nftCollections.get(collectionAddress) || 0) + 1);
}
return {
ethBalance: ethers.formatEther(ethBalance),
erc20Holdings: tokens,
nftCollections: Object.fromEntries(nftCollections),
totalUniqueNfts: nfts.totalCount
};
} catch (error) {
console.error("Error fetching portfolio holdings:", error);
return null;
}
}
// Example usage:
// getPortfolioHoldings("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").then(console.log);
C. Smart Contract Interaction Footprint
You identify which specific smart contracts a wallet frequently interacts with. This is crucial for understanding a user's engagement with DeFi protocols, DAOs, or specific dApps.
// Function to identify unique contract interactions (beyond simple transfers)
async function getContractInteractionFootprint(walletAddress, limit = 500) {
console.log(`Analyzing contract interactions for ${walletAddress}...`);
try {
// Alchemy's getAssetTransfers can include internal calls if configured,
// but for deep contract interaction, filtering logs or using more advanced
// APIs might be needed. Here, we'll focus on 'to' addresses in external/ERC calls.
const allTxs = await alchemy.core.getAssetTransfers({
fromBlock: "0x0",
fromAddress: walletAddress,
category: ["external", "erc20", "erc721", "erc1155"],
maxCount: limit,
order: "desc"
});
const interactedContracts = new Map(); // contractAddress -> count
for (const tx of allTxs.transfers) {
// For external transactions where 'to' is a contract and not self-transfer
if (tx.to && tx.to !== walletAddress) {
try {
const code = await provider.getCode(tx.to);
if (code !== '0x' && code !== '0x0') { // Check if 'to' is a contract
interactedContracts.set(tx.to, (interactedContracts.get(tx.to) || 0) + 1);
}
} catch (e) {
// Ignore error if it's not a contract or network issue
}
}
}
// You might want to resolve contract names for better readability
const resolvedInteractions = {};
for (const [address, count] of interactedContracts.entries()) {
// In a real app, you'd use a contract registry or API to get names
resolvedInteractions[address] = count;
}
return {
totalUniqueContracts: interactedContracts.size,
contractInteractionCounts: resolvedInteractions
};
} catch (error) {
console.error("Error getting contract interaction footprint:", error);
return null;
}
}
// Example usage:
// getContractInteractionFootprint("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").then(console.log);
D. Profiling Heuristics & Risk Scoring
You combine the gathered data to create a profile and assign heuristic scores, such as "whale," "active trader," "newcomer," or "risky."
async function buildWalletProfile(walletAddress) {
const history = await getTransactionHistory(walletAddress);
const portfolio = await getPortfolioHoldings(walletAddress);
const interactions = await getContractInteractionFootprint(walletAddress);
if (!history || !portfolio || !interactions) {
console.error("Failed to gather all data for profiling.");
return null;
}
let profile = {
address: walletAddress,
ethBalance: portfolio.ethBalance,
totalTransactions: history.totalTransactions,
totalValueSentEth: history.totalValueSent,
uniqueTokenHoldings: portfolio.erc20Holdings.length,
uniqueNftCollections: Object.keys(portfolio.nftCollections).length,
uniqueContractInteractions: interactions.totalUniqueContracts,
lastActivityTimestamp: history.latestTxTimestamp,
segments: []
};
// Apply heuristics
const ethBalanceNum = parseFloat(profile.ethBalance);
const totalValueSentNum = parseFloat(profile.totalValueSentEth);
if (ethBalanceNum > 100 || totalValueSentNum > 1000) {
profile.segments.push("Whale");
}
if (profile.totalTransactions > 500 && profile.uniqueContractInteractions > 10) {
profile.segments.push("ActiveDegen");
}
if (profile.uniqueNftCollections > 5) {
profile.segments.push("NFTCollector");
}
if (profile.totalTransactions < 10 && (Date.now() / 1000 - profile.lastActivityTimestamp) < (30 * 24 * 60 * 60)) {
profile.segments.push("Newcomer");
}
if (profile.uniqueTokenHoldings > 20) {
profile.segments.push("TokenDiversified");
}
return profile;
}
Anti-Patterns
-
Treating Wallet Addresses as Identity. Assuming one address equals one person ignores that individuals use multiple wallets and that services, contracts, and bots also have addresses. Use entity clustering before profiling.
-
Static Threshold-Based Classification. Using fixed balance or transaction count thresholds for whale/retail classification without adjusting for market conditions and network-specific norms produces inaccurate segmentation.
-
Ignoring Contract Interaction Context. Counting raw transaction counts without distinguishing between meaningful DeFi interactions and spam token approvals, dust transfers, or airdrop claims inflates activity metrics.
-
Privacy-Invasive Profiling Without Consent. Building detailed behavioral profiles of wallet addresses and linking them to real identities without clear user consent raises ethical and legal concerns, particularly under GDPR.
-
Snapshot-Only Analysis Without Historical Context. Profiling wallets based on current holdings without examining historical patterns (accumulation, distribution, interaction frequency over time) misses the most informative behavioral signals.
Install this skill directly: skilldb add blockchain-data-skills
Related Skills
Blockchain Etl
This skill covers the extraction, transformation, and loading (ETL) of blockchain data into structured databases or data warehouses. You use this when building scalable analytics platforms, dApps requiring extensive historical data, or custom indexing services that go beyond simple RPC queries, enabling complex analysis and reporting.
DEFI Llama Data
This skill enables you to leverage DefiLlama's comprehensive aggregated data for decentralized finance protocols. You utilize this when building applications that require insights into Total Value Locked (TVL), historical protocol performance, yield opportunities, and cross-chain liquidity metrics, providing a standardized view of the DeFi landscape.
Dune Analytics
This skill enables you to leverage Dune Analytics for querying, analyzing, and visualizing blockchain data using SQL. You use this when building dashboards, creating custom data insights, or integrating on-chain data into applications, transforming raw ledger information into actionable intelligence.
Flipside Crypto
This skill enables you to query, analyze, and visualize vast amounts of blockchain data using SQL. You leverage Flipside Crypto when you need deep, structured insights into on-chain activity, smart contract interactions, or token movements across multiple networks, without the complexity of direct RPC node parsing.
Gas Analytics
This skill teaches you to analyze, predict, and optimize transaction fees (gas) on EVM-compatible blockchains. You leverage gas analytics to minimize operational costs, improve user experience by ensuring timely transaction finality, and make informed decisions about dApp deployment and interaction strategies, especially during network congestion.
Nansen Analytics
This skill empowers you to leverage Nansen Analytics' proprietary on-chain data and entity labeling for enhanced insights into blockchain activity. You employ Nansen when your applications demand deep, real-time intelligence on market movements, smart money flows, and whale activity, going beyond raw RPC data.