From alchemy-pack
Builds wallet portfolio tracker using Alchemy APIs to fetch token balances, NFTs for owner, asset transfers, and metadata. For token dashboards, NFT galleries, transaction history, and analytics.
npx claudepluginhub jeremylongshore/claude-code-plugins-plus-skills --plugin alchemy-packThis skill is limited to using the following tools:
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.
Integrates Alchemy APIs for EVM JSON-RPC calls, token balances, NFT ownership/metadata, transfers, prices, portfolio data, transaction simulation, webhooks, Solana RPC. Covers base URLs, auth, endpoints, pagination. Requires $ALCHEMY_API_KEY.
Generates TypeScript examples with Alchemy SDK to get ETH balance, fetch wallet NFTs, read ERC-20 token balances. For blockchain quickstarts, Alchemy setup testing.
Executes Alchemy CLI (@alchemy/cli) for live blockchain queries, transaction/NFT/token lookups, simulation, tracing, Solana RPC/DAS, webhooks, and app admin. For agent runtime tasks with local install.
Share bugs, ideas, or general feedback.
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.
alchemy-install-auth setupalchemy-sdk installed// 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 };
// 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 };
// 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 };
| 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 |
For NFT minting and smart contract interaction, see alchemy-core-workflow-b.