Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
795 changes: 680 additions & 115 deletions agent/the-graph-agent-scaffold-eth/README.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,61 @@ export async function POST(req: Request) {
- tokenAddress: (Optional) The address of one of the tokens in the pair, for price orientation.
Example: getTokenOHLCByPool({ poolAddress: "0x...", networkId: "ethereum", resolution: "1d" })

9. For NFT collections, you can use the "getNFTCollections" tool to get collection metadata.
Parameters:
- contractAddress: (Required) The NFT contract address.
- networkId: (Optional) The network identifier.
Example: getNFTCollections({ contractAddress: "0x...", networkId: "ethereum" })

10. To get details for a specific NFT, use the "getNFTItems" tool.
Parameters:
- contractAddress: (Required) The NFT contract address.
- tokenId: (Required) The token ID of the specific NFT.
- networkId: (Optional) The network identifier.
Example: getNFTItems({ contractAddress: "0x...", tokenId: "1234", networkId: "ethereum" })

11. To fetch NFT sales data, use the "getNFTSales" tool.
Parameters:
- networkId: (Optional) The network identifier.
- any: (Optional) Filter by any address involved (buyer, seller, or contract).
- offerer: (Optional) Filter by seller address.
- recipient: (Optional) Filter by buyer address.
- token: (Optional) Filter by NFT contract address.
- startTime: (Optional) Start timestamp (Unix seconds).
- endTime: (Optional) End timestamp (Unix seconds).
- limit: (Optional) Maximum number of results.
- page: (Optional) Page number for pagination.
Example: getNFTSales({ networkId: "ethereum", token: "0x...", limit: 20 })

12. To get NFT holders for a collection, use the "getNFTHolders" tool.
Parameters:
- contractAddress: (Required) The NFT contract address.
- networkId: (Optional) The network identifier.
- page: (Optional) Page number for pagination.
- pageSize: (Optional) Number of results per page.
Example: getNFTHolders({ contractAddress: "0x...", networkId: "ethereum", pageSize: 50 })

13. To get NFT ownerships for a wallet, use the "getNFTOwnerships" tool.
Parameters:
- ownerAddress: (Required) The wallet address to query.
- networkId: (Optional) The network identifier.
- contractAddress: (Optional) Filter by specific NFT contract.
Example: getNFTOwnerships({ ownerAddress: "${userAddress}", networkId: "ethereum" })

14. To get NFT activities (transfers, mints, burns, etc.), use the "getNFTActivities" tool.
Parameters:
- networkId: (Optional) The network identifier.
- contractAddress: (Optional) Filter by NFT contract address.
- fromAddress: (Optional) Filter by from address.
- toAddress: (Optional) Filter by to address.
- tokenId: (Optional) Filter by specific token ID.
- activityType: (Optional) Filter by activity type (transfer, mint, burn, etc.).
- startTime: (Optional) Start timestamp (Unix seconds).
- endTime: (Optional) End timestamp (Unix seconds).
- limit: (Optional) Maximum number of results.
- page: (Optional) Page number for pagination.
Example: getNFTActivities({ networkId: "ethereum", contractAddress: "0x...", limit: 50 })

For Uniswap V3, use this exact endpoint:
"${uniswapEndpoint}"

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export { useTokenApi } from "./useTokenApi";
export { useTokenBalances } from "./useTokenBalances";
export { useTokenDetails } from "./useTokenDetails";
export { useTokenHolders } from "./useTokenHolders";
export { useTokenMetadata } from "./useTokenMetadata";
export { useTokenOHLCByContract } from "./useTokenOHLCByContract";
Expand All @@ -8,3 +9,11 @@ export { useTokenPools } from "./useTokenPools";
export { useTokenSwaps } from "./useTokenSwaps";
export { useTokenTransfers } from "./useTokenTransfers";
export { useHistoricalBalances } from "./useHistoricalBalances";

// NFT hooks
export { useNFTActivities } from "./useNFTActivities";
export { useNFTCollections } from "./useNFTCollections";
export { useNFTHolders } from "./useNFTHolders";
export { useNFTItems } from "./useNFTItems";
export { useNFTOwnerships } from "./useNFTOwnerships";
export { useNFTSales } from "./useNFTSales";
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"use client";

import type { NetworkId } from "./useTokenApi";
import { useTokenApi } from "./useTokenApi";

export interface NFTActivity {
"@type": "TRANSFER" | "MINT" | "BURN";
block_num: number;
block_hash: string;
timestamp: string;
tx_hash: string;
contract: string;
symbol?: string; // Optional based on schema (not in required list)
name?: string; // Optional based on schema (not in required list)
from: string;
to: string;
token_id: number;
amount: number;
transfer_type?: string; // Optional based on schema
token_standard?: string; // Optional based on schema
}

export interface NFTActivitiesResponse {
data: NFTActivity[];
statistics?: {
elapsed?: number;
rows_read?: number;
bytes_read?: number;
};
}

export interface UseNFTActivitiesOptions {
network?: NetworkId;
enabled?: boolean;
contract_address: string; // REQUIRED: NFT contract address - renamed from 'contract' to avoid conflict, will be mapped
any?: string; // Filter by any address involved (from, to, contract)
from?: string;
to?: string;
token_id?: string; // This was in the old options, but not in new schema for activities endpoint. Retaining for now, but will not be passed to API.
type?: string[]; // This was in the old options, API uses "@type". This will need to be handled or removed.
startTime?: number;
endTime?: number;
orderBy?: "timestamp";
orderDirection?: "asc" | "desc";
limit?: number;
page?: number;
// cursor?: string; // cursor is not in the new schema, using page for pagination
}

/**
* React hook to get NFT activities
*/
export function useNFTActivities(options: UseNFTActivitiesOptions) {
const { network, enabled = true, contract_address, token_id, type, ...apiParams } = options;

const endpoint = "nft/activities/evm";

// Prepare query parameters, mapping names if necessary
const queryParams: any = {
network_id: network,
...apiParams,
};

if (contract_address) {
queryParams.contract = contract_address;
}

// The 'type' (e.g. ["mint", "transfer"]) and 'token_id' params from the old hook
// are not directly supported by the new /nft/activities/evm endpoint in the provided schema.
// The API filters by `@type` for individual activity types (TRANSFER, MINT, BURN) but not as an array.
// Token ID is not a filter for the general activities endpoint.
// These will be ignored for the API call if not handled specifically.
// For simplicity, they are currently ignored.

return useTokenApi<NFTActivity[]>(endpoint, queryParams, {
skip: !enabled || !contract_address,
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
"use client";

/**
* Hook for fetching NFT collection data from The Graph Token API
*
* IMPORTANT: This hook requires authentication to work properly.
*
* Setup Instructions:
* 1. Get your API token from https://thegraph.com/market/
* 2. Create a .env.local file in packages/nextjs/ directory
* 3. Add: NEXT_PUBLIC_GRAPH_TOKEN=your_token_here
*
* Alternative: Use API Key instead of token:
* - Add: NEXT_PUBLIC_GRAPH_API_KEY=your_api_key_here
*
* Without proper authentication, you'll get 401 unauthorized errors.
*/
import type { NetworkId } from "./useTokenApi";
import { useTokenApi } from "./useTokenApi";

export interface NFTCollection {
token_standard: string; // ERC721, ERC1155, etc.
contract: string;
contract_creation: string;
contract_creator: string;
symbol: string;
name: string;
base_uri?: string; // Optional as per schema example, not in required list
total_supply: number;
total_unique_supply?: number; // Added based on the documentation example
owners: number;
total_transfers: number;
network_id: NetworkId;
}

export interface NFTCollectionsResponse {
data: NFTCollection[];
statistics?: {
elapsed?: number;
rows_read?: number;
bytes_read?: number;
};
}

// The actual API response structure (for reference)
export interface NFTCollectionsAPIResponse {
data: NFTCollection[];
statistics?: {
elapsed?: number;
rows_read?: number;
bytes_read?: number;
};
pagination?: {
offset?: number;
limit?: number;
};
results?: number;
total_results?: number;
}

export interface UseNFTCollectionsOptions {
contractAddress: string; // Changed from contract to contractAddress for clarity
network?: NetworkId;
enabled?: boolean;
}

/**
* Normalize contract address to ensure proper format
*/
function normalizeContractAddress(address: string): string {
if (!address) return address;

// Remove any whitespace
const trimmed = address.trim();

// Ensure it starts with 0x and is lowercase
if (trimmed.startsWith("0x") || trimmed.startsWith("0X")) {
return trimmed.toLowerCase();
} else {
return `0x${trimmed.toLowerCase()}`;
}
}

/**
* React hook to get NFT collections for a specific contract
*/
export function useNFTCollections(options: UseNFTCollectionsOptions) {
const { contractAddress, network = "mainnet", enabled = true } = options;

const normalizedContractAddress = normalizeContractAddress(contractAddress);

const endpoint = `nft/collections/evm/${normalizedContractAddress}`;

const result = useTokenApi<NFTCollection[]>(
endpoint,
{
network_id: network,
},
{
// Skip if contractAddress is not provided, or if the hook is disabled
skip: !normalizedContractAddress || !enabled,
},
);

// Enhanced debugging and error handling
if (normalizedContractAddress && !result.isLoading) {
const isWellKnownContract =
normalizedContractAddress.toLowerCase() === "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb"; // CryptoPunks

if (result.error) {
console.error(`🔴 Error fetching NFT collection for ${normalizedContractAddress}:`, result.error);

// Specific error handling for authentication issues
if (result.error.includes("401") || result.error.includes("unauthorized")) {
console.error(`🔑 Authentication error detected. Please check your Graph API credentials:
- Set NEXT_PUBLIC_GRAPH_TOKEN or NEXT_PUBLIC_GRAPH_API_KEY in your .env.local file
- Get your API token from https://thegraph.com/market/`);
}
} else if (!result.data || (Array.isArray(result.data) && result.data.length === 0)) {
console.log(`🔍 No data returned for contract ${normalizedContractAddress}:`, {
endpoint,
network,
enabled,
isWellKnownContract,
skip: !normalizedContractAddress || !enabled,
});

if (isWellKnownContract) {
console.warn(`⚠️ This is a well-known contract (CryptoPunks) that should return data.
This might indicate an authentication or network issue.`);
}
} else if (result.data && Array.isArray(result.data) && result.data.length > 0) {
console.log(
`✅ Successfully fetched NFT collection data for ${normalizedContractAddress}:`,
result.data[0].name || "Unknown Collection",
);
}
}

return result;
}
Loading