> alchemy-core-workflow-a
Build a complete wallet portfolio tracker using Alchemy Enhanced APIs. Use when implementing token balance dashboards, NFT galleries, transaction history views, or wallet analytics applications. Trigger: "alchemy wallet tracker", "alchemy portfolio", "alchemy token dashboard", "alchemy transaction history", "build dApp with alchemy".
curl "https://skillshub.wtf/jeremylongshore/claude-code-plugins-plus-skills/alchemy-core-workflow-a?format=md"Alchemy Core Workflow A — Wallet Portfolio Tracker
Overview
Primary workflow: build a wallet portfolio tracker using Alchemy's Enhanced APIs. Combines getTokenBalances, getNftsForOwner, getAssetTransfers, and token metadata to create a complete wallet view across ERC-20, ERC-721, and ERC-1155 assets.
Prerequisites
- Completed
alchemy-install-authsetup alchemy-sdkinstalled- Understanding of Ethereum address format and token standards
Instructions
Step 1: Portfolio Data Fetcher
// src/portfolio/fetcher.ts
import { Alchemy, Network, AssetTransfersCategory } from 'alchemy-sdk';
const alchemy = new Alchemy({
apiKey: process.env.ALCHEMY_API_KEY,
network: Network.ETH_MAINNET,
});
interface TokenHolding {
contractAddress: string;
symbol: string;
name: string;
balance: number;
decimals: number;
}
interface NftHolding {
contractAddress: string;
collectionName: string;
tokenId: string;
name: string;
imageUrl: string | null;
}
interface WalletPortfolio {
address: string;
ethBalance: string;
tokens: TokenHolding[];
nfts: NftHolding[];
recentTransactions: any[];
fetchedAt: string;
}
async function fetchPortfolio(address: string): Promise<WalletPortfolio> {
// Parallel fetch all portfolio data
const [ethBalance, tokenBalances, nftResponse, transfers] = await Promise.all([
alchemy.core.getBalance(address),
alchemy.core.getTokenBalances(address),
alchemy.nft.getNftsForOwner(address, { pageSize: 100 }),
alchemy.core.getAssetTransfers({
fromAddress: address,
category: [
AssetTransfersCategory.EXTERNAL,
AssetTransfersCategory.ERC20,
AssetTransfersCategory.ERC721,
],
maxCount: 25,
order: 'desc',
}),
]);
// Resolve token metadata
const tokens: TokenHolding[] = [];
for (const tb of tokenBalances.tokenBalances) {
if (tb.tokenBalance && tb.tokenBalance !== '0x0') {
const metadata = await alchemy.core.getTokenMetadata(tb.contractAddress);
const balance = parseInt(tb.tokenBalance, 16) / Math.pow(10, metadata.decimals || 18);
if (balance > 0.001) { // Filter dust
tokens.push({
contractAddress: tb.contractAddress,
symbol: metadata.symbol || 'UNKNOWN',
name: metadata.name || 'Unknown Token',
balance,
decimals: metadata.decimals || 18,
});
}
}
}
// Map NFTs
const nfts: NftHolding[] = nftResponse.ownedNfts.map(nft => ({
contractAddress: nft.contract.address,
collectionName: nft.contract.name || 'Unknown Collection',
tokenId: nft.tokenId,
name: nft.name || `#${nft.tokenId}`,
imageUrl: nft.image?.cachedUrl || null,
}));
return {
address,
ethBalance: (parseInt(ethBalance.toString()) / 1e18).toFixed(6),
tokens: tokens.sort((a, b) => b.balance - a.balance),
nfts,
recentTransactions: transfers.transfers,
fetchedAt: new Date().toISOString(),
};
}
export { fetchPortfolio, WalletPortfolio };
Step 2: Transaction History Analyzer
// src/portfolio/transactions.ts
import { Alchemy, Network, AssetTransfersCategory, SortingOrder } from 'alchemy-sdk';
const alchemy = new Alchemy({
apiKey: process.env.ALCHEMY_API_KEY,
network: Network.ETH_MAINNET,
});
async function getTransactionHistory(address: string, maxCount: number = 50) {
// Get both sent and received transactions
const [sent, received] = await Promise.all([
alchemy.core.getAssetTransfers({
fromAddress: address,
category: [AssetTransfersCategory.EXTERNAL, AssetTransfersCategory.ERC20],
maxCount,
order: 'desc',
}),
alchemy.core.getAssetTransfers({
toAddress: address,
category: [AssetTransfersCategory.EXTERNAL, AssetTransfersCategory.ERC20],
maxCount,
order: 'desc',
}),
]);
// Merge and sort by block number
const allTransfers = [
...sent.transfers.map(t => ({ ...t, direction: 'sent' as const })),
...received.transfers.map(t => ({ ...t, direction: 'received' as const })),
].sort((a, b) => parseInt(b.blockNum) - parseInt(a.blockNum));
return allTransfers;
}
export { getTransactionHistory };
Step 3: Multi-Chain Portfolio Aggregator
// src/portfolio/multi-chain.ts
import { Alchemy, Network } from 'alchemy-sdk';
const CHAINS = [
{ name: 'Ethereum', network: Network.ETH_MAINNET },
{ name: 'Polygon', network: Network.MATIC_MAINNET },
{ name: 'Arbitrum', network: Network.ARB_MAINNET },
{ name: 'Optimism', network: Network.OPT_MAINNET },
{ name: 'Base', network: Network.BASE_MAINNET },
];
async function getMultiChainBalances(address: string) {
const results = await Promise.allSettled(
CHAINS.map(async (chain) => {
const client = new Alchemy({
apiKey: process.env.ALCHEMY_API_KEY,
network: chain.network,
});
const balance = await client.core.getBalance(address);
const tokens = await client.core.getTokenBalances(address);
const nonZeroTokens = tokens.tokenBalances.filter(
t => t.tokenBalance && t.tokenBalance !== '0x0'
);
return {
chain: chain.name,
nativeBalance: (parseInt(balance.toString()) / 1e18).toFixed(6),
tokenCount: nonZeroTokens.length,
};
})
);
return results
.filter((r): r is PromiseFulfilledResult<any> => r.status === 'fulfilled')
.map(r => r.value);
}
export { getMultiChainBalances };
Output
- Complete wallet portfolio: ETH + ERC-20 tokens + NFTs
- Transaction history with sent/received classification
- Multi-chain balance aggregation across 5 networks
- Sorted holdings with dust filtering
Error Handling
| Error | Cause | Solution |
|---|---|---|
429 Rate Limit | Too many metadata calls | Batch with delays or cache metadata |
| Empty token list | Address has no tokens | Verify address is correct |
| Missing NFT images | IPFS gateway timeout | Use Alchemy's cached URL fallback |
getAssetTransfers empty | Wrong category filter | Include all relevant categories |
Resources
Next Steps
For NFT minting and smart contract interaction, see alchemy-core-workflow-b.
> related_skills --same-repo
> fathom-cost-tuning
Optimize Fathom API usage and plan selection. Trigger with phrases like "fathom cost", "fathom pricing", "fathom plan".
> fathom-core-workflow-b
Sync Fathom meeting data to CRM and build automated follow-up workflows. Use when integrating Fathom with Salesforce, HubSpot, or custom CRMs, or creating automated post-meeting email summaries. Trigger with phrases like "fathom crm sync", "fathom salesforce", "fathom follow-up", "fathom post-meeting workflow".
> fathom-core-workflow-a
Build a meeting analytics pipeline with Fathom transcripts and summaries. Use when extracting insights from meetings, building CRM sync, or creating automated meeting follow-up workflows. Trigger with phrases like "fathom analytics", "fathom meeting pipeline", "fathom transcript analysis", "fathom action items sync".
> fathom-common-errors
Diagnose and fix Fathom API errors including auth failures and missing data. Use when API calls fail, transcripts are empty, or webhooks are not firing. Trigger with phrases like "fathom error", "fathom not working", "fathom api failure", "fix fathom".