如何从指定以太坊钱包地址获取ERC20、ERC721、ERC827代币列表及详情
Hey there! If you're looking to pull a list of ERC20, ERC721, and ERC827 tokens held by a specific Ethereum wallet—along with their names, symbols, logos, and balances—you’ve got two solid paths to choose from: using third-party data APIs (the quick, low-effort route) or querying an Ethereum node directly (great for custom workflows). Let’s break down both options with practical details:
Third-party services handle the heavy lifting of indexing blockchain data, so you can focus on integrating the results. Here are the most reliable ones:
Etherscan API
It has dedicated endpoints for token balances and metadata:- For ERC20 tokens: Use the
tokentxendpoint to fetch all token transfers involving the target address, then deduplicate contract addresses. You can then call thetokeninfoendpoint for each contract to get its name, symbol, and other details. - For ERC721 tokens: Use the
tokennfttxendpoint to retrieve NFT transfers, then fetch individual NFT metadata via contract-specific calls. - Logos: Etherscan hosts token logos on its CDN (format:
https://etherscan.io/token/images/[contract-address-prefix]_32.png), but this only works for tokens the platform has indexed.
Example pseudocode snippet:
// Fetch ERC20 transfer history to get contract addresses const erc20TxResponse = await fetch(`https://api.etherscan.io/api?module=account&action=tokentx&address=YOUR_WALLET_ADDRESS&startblock=0&endblock=99999999&sort=asc&apikey=YOUR_API_KEY`); // Deduplicate contract addresses const uniqueContracts = [...new Set(erc20TxResponse.result.map(tx => tx.contractAddress))]; // Fetch metadata for each contract const tokenMetadata = await Promise.all(uniqueContracts.map(addr => fetch(`https://api.etherscan.io/api?module=token&action=tokeninfo&contractaddress=${addr}&apikey=YOUR_API_KEY`) ));- For ERC20 tokens: Use the
Alchemy NFT API
If your focus is on ERC721 tokens, Alchemy’s NFT API lets you directly fetch all NFTs held by an address, including full metadata (images, descriptions) and balances. It also supports ERC20 token balance queries for a unified workflow.
If you need full control over data processing, you can use a Web3 library (like ethers.js or web3.js) to connect to an Ethereum mainnet node (via Infura, Alchemy, or your own self-hosted node).
Core Logic Breakdown:
ERC20 Tokens
- Ethereum doesn’t have a global token registry, so you’ll need to filter
Transferevents where the target wallet is the recipient. Deduplicate contract addresses from these events. - For each contract, call standard ERC20 methods:
name(),symbol(),balanceOf(YOUR_WALLET_ADDRESS), anddecimals()(to format balance correctly). - Logos: Parse the token’s metadata via
tokenURI()(if implemented) or rely on third-party indexed data.
- Ethereum doesn’t have a global token registry, so you’ll need to filter
ERC721 Tokens
- Filter
Transferevents (ERC721 uses a different signature that includes atokenId). Deduplicate contract addresses. - For each contract, call
name(),symbol(), andbalanceOf(YOUR_WALLET_ADDRESS)to get the number of NFTs held. - Use
tokenOfOwnerByIndex()to iterate through each heldtokenId, then calltokenURI(tokenId)to fetch metadata (including images/logos).
- Filter
ERC827 Tokens
ERC827 is an extension of ERC20, so you can use the same methods as ERC20 to fetch balances and metadata. Its additional functionality (executing calls with transfers) isn’t needed for basic token listing.
Example Code with ethers.js:
const { ethers } = require("ethers"); // Connect to Ethereum mainnet node const provider = new ethers.providers.JsonRpcProvider("YOUR_NODE_URL"); const targetWallet = "0xYOUR_WALLET_ADDRESS"; // Minimal ABIs for ERC20 and ERC721 const erc20Abi = [ "function name() view returns (string)", "function symbol() view returns (string)", "function balanceOf(address) view returns (uint256)", "function decimals() view returns (uint8)", "event Transfer(address indexed from, address indexed to, uint256 value)" ]; const erc721Abi = [ "function name() view returns (string)", "function symbol() view returns (string)", "function balanceOf(address) view returns (uint256)", "function tokenOfOwnerByIndex(address, uint256) view returns (uint256)", "function tokenURI(uint256) view returns (string)", "event Transfer(address indexed from, address indexed to, uint256 indexed tokenId)" ]; // Fetch ERC20 tokens async function fetchERC20Tokens() { const transferFilter = { topics: [ethers.utils.id("Transfer(address,address,uint256)")], to: targetWallet }; const events = await provider.getLogs(transferFilter); const uniqueContracts = [...new Set(events.map(e => e.address))]; const tokens = []; for (const addr of uniqueContracts) { const contract = new ethers.Contract(addr, erc20Abi, provider); try { const [name, symbol, balance, decimals] = await Promise.all([ contract.name(), contract.symbol(), contract.balanceOf(targetWallet), contract.decimals() ]); tokens.push({ contractAddress: addr, name, symbol, balance: ethers.utils.formatUnits(balance, decimals), logo: `https://etherscan.io/token/images/${addr.slice(2,6)}_32.png` // Etherscan CDN example }); } catch (err) { // Skip non-compliant contracts continue; } } return tokens; } // Fetch ERC721 tokens async function fetchERC721Tokens() { const transferFilter = { topics: [ethers.utils.id("Transfer(address,address,uint256)")], to: targetWallet }; const events = await provider.getLogs(transferFilter); const uniqueContracts = [...new Set(events.map(e => e.address))]; const nfts = []; for (const addr of uniqueContracts) { const contract = new ethers.Contract(addr, erc721Abi, provider); try { const [name, symbol, balance] = await Promise.all([ contract.name(), contract.symbol(), contract.balanceOf(targetWallet) ]); const ownedNFTs = []; for (let i = 0; i < balance.toNumber(); i++) { const tokenId = await contract.tokenOfOwnerByIndex(targetWallet, i); const tokenURI = await contract.tokenURI(tokenId); const metadata = await fetch(tokenURI).then(res => res.json()); ownedNFTs.push({ tokenId: tokenId.toString(), image: metadata.image }); } nfts.push({ contractAddress: addr, name, symbol, balance: balance.toString(), logo: ownedNFTs[0]?.image || null, ownedNFTs }); } catch (err) { continue; } } return nfts; } // Execute queries (async () => { const erc20s = await fetchERC20Tokens(); const erc721s = await fetchERC721Tokens(); console.log("ERC20 Tokens:", erc20s); console.log("ERC721 Tokens:", erc721s); })();
- Non-compliant Contracts: Some tokens might not follow ERC standards (e.g., missing
name()orsymbol()methods). Add error handling to skip these. - Logo Reliability: There’s no universal way to fetch logos—rely on third-party indexes or token metadata URIs.
- Performance: Direct node queries can be slow for wallets with long transaction histories. Limit your query to recent blocks or use pagination.
- API Rate Limits: Third-party APIs have request limits—use an API key and implement rate limiting in your code.
内容的提问来源于stack exchange,提问作者Oyewole A. Samuel

