Skip to content

Commit 9d071bd

Browse files
authored
Merge pull request #10 from kmjones1979/main
updates for the nft endpoints of token api and readme
2 parents d26d5d7 + 9703c39 commit 9d071bd

File tree

13 files changed

+2100
-132
lines changed

13 files changed

+2100
-132
lines changed

agent/the-graph-agent-scaffold-eth/README.md

Lines changed: 680 additions & 115 deletions
Large diffs are not rendered by default.

agent/the-graph-agent-scaffold-eth/packages/nextjs/app/api/chat/route.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,61 @@ export async function POST(req: Request) {
127127
- tokenAddress: (Optional) The address of one of the tokens in the pair, for price orientation.
128128
Example: getTokenOHLCByPool({ poolAddress: "0x...", networkId: "ethereum", resolution: "1d" })
129129
130+
9. For NFT collections, you can use the "getNFTCollections" tool to get collection metadata.
131+
Parameters:
132+
- contractAddress: (Required) The NFT contract address.
133+
- networkId: (Optional) The network identifier.
134+
Example: getNFTCollections({ contractAddress: "0x...", networkId: "ethereum" })
135+
136+
10. To get details for a specific NFT, use the "getNFTItems" tool.
137+
Parameters:
138+
- contractAddress: (Required) The NFT contract address.
139+
- tokenId: (Required) The token ID of the specific NFT.
140+
- networkId: (Optional) The network identifier.
141+
Example: getNFTItems({ contractAddress: "0x...", tokenId: "1234", networkId: "ethereum" })
142+
143+
11. To fetch NFT sales data, use the "getNFTSales" tool.
144+
Parameters:
145+
- networkId: (Optional) The network identifier.
146+
- any: (Optional) Filter by any address involved (buyer, seller, or contract).
147+
- offerer: (Optional) Filter by seller address.
148+
- recipient: (Optional) Filter by buyer address.
149+
- token: (Optional) Filter by NFT contract address.
150+
- startTime: (Optional) Start timestamp (Unix seconds).
151+
- endTime: (Optional) End timestamp (Unix seconds).
152+
- limit: (Optional) Maximum number of results.
153+
- page: (Optional) Page number for pagination.
154+
Example: getNFTSales({ networkId: "ethereum", token: "0x...", limit: 20 })
155+
156+
12. To get NFT holders for a collection, use the "getNFTHolders" tool.
157+
Parameters:
158+
- contractAddress: (Required) The NFT contract address.
159+
- networkId: (Optional) The network identifier.
160+
- page: (Optional) Page number for pagination.
161+
- pageSize: (Optional) Number of results per page.
162+
Example: getNFTHolders({ contractAddress: "0x...", networkId: "ethereum", pageSize: 50 })
163+
164+
13. To get NFT ownerships for a wallet, use the "getNFTOwnerships" tool.
165+
Parameters:
166+
- ownerAddress: (Required) The wallet address to query.
167+
- networkId: (Optional) The network identifier.
168+
- contractAddress: (Optional) Filter by specific NFT contract.
169+
Example: getNFTOwnerships({ ownerAddress: "${userAddress}", networkId: "ethereum" })
170+
171+
14. To get NFT activities (transfers, mints, burns, etc.), use the "getNFTActivities" tool.
172+
Parameters:
173+
- networkId: (Optional) The network identifier.
174+
- contractAddress: (Optional) Filter by NFT contract address.
175+
- fromAddress: (Optional) Filter by from address.
176+
- toAddress: (Optional) Filter by to address.
177+
- tokenId: (Optional) Filter by specific token ID.
178+
- activityType: (Optional) Filter by activity type (transfer, mint, burn, etc.).
179+
- startTime: (Optional) Start timestamp (Unix seconds).
180+
- endTime: (Optional) End timestamp (Unix seconds).
181+
- limit: (Optional) Maximum number of results.
182+
- page: (Optional) Page number for pagination.
183+
Example: getNFTActivities({ networkId: "ethereum", contractAddress: "0x...", limit: 50 })
184+
130185
For Uniswap V3, use this exact endpoint:
131186
"${uniswapEndpoint}"
132187

agent/the-graph-agent-scaffold-eth/packages/nextjs/app/chat/_hooks/index.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
export { useTokenApi } from "./useTokenApi";
22
export { useTokenBalances } from "./useTokenBalances";
3+
export { useTokenDetails } from "./useTokenDetails";
34
export { useTokenHolders } from "./useTokenHolders";
45
export { useTokenMetadata } from "./useTokenMetadata";
56
export { useTokenOHLCByContract } from "./useTokenOHLCByContract";
@@ -8,3 +9,11 @@ export { useTokenPools } from "./useTokenPools";
89
export { useTokenSwaps } from "./useTokenSwaps";
910
export { useTokenTransfers } from "./useTokenTransfers";
1011
export { useHistoricalBalances } from "./useHistoricalBalances";
12+
13+
// NFT hooks
14+
export { useNFTActivities } from "./useNFTActivities";
15+
export { useNFTCollections } from "./useNFTCollections";
16+
export { useNFTHolders } from "./useNFTHolders";
17+
export { useNFTItems } from "./useNFTItems";
18+
export { useNFTOwnerships } from "./useNFTOwnerships";
19+
export { useNFTSales } from "./useNFTSales";
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
"use client";
2+
3+
import type { NetworkId } from "./useTokenApi";
4+
import { useTokenApi } from "./useTokenApi";
5+
6+
export interface NFTActivity {
7+
"@type": "TRANSFER" | "MINT" | "BURN";
8+
block_num: number;
9+
block_hash: string;
10+
timestamp: string;
11+
tx_hash: string;
12+
contract: string;
13+
symbol?: string; // Optional based on schema (not in required list)
14+
name?: string; // Optional based on schema (not in required list)
15+
from: string;
16+
to: string;
17+
token_id: number;
18+
amount: number;
19+
transfer_type?: string; // Optional based on schema
20+
token_standard?: string; // Optional based on schema
21+
}
22+
23+
export interface NFTActivitiesResponse {
24+
data: NFTActivity[];
25+
statistics?: {
26+
elapsed?: number;
27+
rows_read?: number;
28+
bytes_read?: number;
29+
};
30+
}
31+
32+
export interface UseNFTActivitiesOptions {
33+
network?: NetworkId;
34+
enabled?: boolean;
35+
contract_address: string; // REQUIRED: NFT contract address - renamed from 'contract' to avoid conflict, will be mapped
36+
any?: string; // Filter by any address involved (from, to, contract)
37+
from?: string;
38+
to?: string;
39+
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.
40+
type?: string[]; // This was in the old options, API uses "@type". This will need to be handled or removed.
41+
startTime?: number;
42+
endTime?: number;
43+
orderBy?: "timestamp";
44+
orderDirection?: "asc" | "desc";
45+
limit?: number;
46+
page?: number;
47+
// cursor?: string; // cursor is not in the new schema, using page for pagination
48+
}
49+
50+
/**
51+
* React hook to get NFT activities
52+
*/
53+
export function useNFTActivities(options: UseNFTActivitiesOptions) {
54+
const { network, enabled = true, contract_address, token_id, type, ...apiParams } = options;
55+
56+
const endpoint = "nft/activities/evm";
57+
58+
// Prepare query parameters, mapping names if necessary
59+
const queryParams: any = {
60+
network_id: network,
61+
...apiParams,
62+
};
63+
64+
if (contract_address) {
65+
queryParams.contract = contract_address;
66+
}
67+
68+
// The 'type' (e.g. ["mint", "transfer"]) and 'token_id' params from the old hook
69+
// are not directly supported by the new /nft/activities/evm endpoint in the provided schema.
70+
// The API filters by `@type` for individual activity types (TRANSFER, MINT, BURN) but not as an array.
71+
// Token ID is not a filter for the general activities endpoint.
72+
// These will be ignored for the API call if not handled specifically.
73+
// For simplicity, they are currently ignored.
74+
75+
return useTokenApi<NFTActivity[]>(endpoint, queryParams, {
76+
skip: !enabled || !contract_address,
77+
});
78+
}
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
"use client";
2+
3+
/**
4+
* Hook for fetching NFT collection data from The Graph Token API
5+
*
6+
* IMPORTANT: This hook requires authentication to work properly.
7+
*
8+
* Setup Instructions:
9+
* 1. Get your API token from https://thegraph.com/market/
10+
* 2. Create a .env.local file in packages/nextjs/ directory
11+
* 3. Add: NEXT_PUBLIC_GRAPH_TOKEN=your_token_here
12+
*
13+
* Alternative: Use API Key instead of token:
14+
* - Add: NEXT_PUBLIC_GRAPH_API_KEY=your_api_key_here
15+
*
16+
* Without proper authentication, you'll get 401 unauthorized errors.
17+
*/
18+
import type { NetworkId } from "./useTokenApi";
19+
import { useTokenApi } from "./useTokenApi";
20+
21+
export interface NFTCollection {
22+
token_standard: string; // ERC721, ERC1155, etc.
23+
contract: string;
24+
contract_creation: string;
25+
contract_creator: string;
26+
symbol: string;
27+
name: string;
28+
base_uri?: string; // Optional as per schema example, not in required list
29+
total_supply: number;
30+
total_unique_supply?: number; // Added based on the documentation example
31+
owners: number;
32+
total_transfers: number;
33+
network_id: NetworkId;
34+
}
35+
36+
export interface NFTCollectionsResponse {
37+
data: NFTCollection[];
38+
statistics?: {
39+
elapsed?: number;
40+
rows_read?: number;
41+
bytes_read?: number;
42+
};
43+
}
44+
45+
// The actual API response structure (for reference)
46+
export interface NFTCollectionsAPIResponse {
47+
data: NFTCollection[];
48+
statistics?: {
49+
elapsed?: number;
50+
rows_read?: number;
51+
bytes_read?: number;
52+
};
53+
pagination?: {
54+
offset?: number;
55+
limit?: number;
56+
};
57+
results?: number;
58+
total_results?: number;
59+
}
60+
61+
export interface UseNFTCollectionsOptions {
62+
contractAddress: string; // Changed from contract to contractAddress for clarity
63+
network?: NetworkId;
64+
enabled?: boolean;
65+
}
66+
67+
/**
68+
* Normalize contract address to ensure proper format
69+
*/
70+
function normalizeContractAddress(address: string): string {
71+
if (!address) return address;
72+
73+
// Remove any whitespace
74+
const trimmed = address.trim();
75+
76+
// Ensure it starts with 0x and is lowercase
77+
if (trimmed.startsWith("0x") || trimmed.startsWith("0X")) {
78+
return trimmed.toLowerCase();
79+
} else {
80+
return `0x${trimmed.toLowerCase()}`;
81+
}
82+
}
83+
84+
/**
85+
* React hook to get NFT collections for a specific contract
86+
*/
87+
export function useNFTCollections(options: UseNFTCollectionsOptions) {
88+
const { contractAddress, network = "mainnet", enabled = true } = options;
89+
90+
const normalizedContractAddress = normalizeContractAddress(contractAddress);
91+
92+
const endpoint = `nft/collections/evm/${normalizedContractAddress}`;
93+
94+
const result = useTokenApi<NFTCollection[]>(
95+
endpoint,
96+
{
97+
network_id: network,
98+
},
99+
{
100+
// Skip if contractAddress is not provided, or if the hook is disabled
101+
skip: !normalizedContractAddress || !enabled,
102+
},
103+
);
104+
105+
// Enhanced debugging and error handling
106+
if (normalizedContractAddress && !result.isLoading) {
107+
const isWellKnownContract =
108+
normalizedContractAddress.toLowerCase() === "0xb47e3cd837ddf8e4c57f05d70ab865de6e193bbb"; // CryptoPunks
109+
110+
if (result.error) {
111+
console.error(`🔴 Error fetching NFT collection for ${normalizedContractAddress}:`, result.error);
112+
113+
// Specific error handling for authentication issues
114+
if (result.error.includes("401") || result.error.includes("unauthorized")) {
115+
console.error(`🔑 Authentication error detected. Please check your Graph API credentials:
116+
- Set NEXT_PUBLIC_GRAPH_TOKEN or NEXT_PUBLIC_GRAPH_API_KEY in your .env.local file
117+
- Get your API token from https://thegraph.com/market/`);
118+
}
119+
} else if (!result.data || (Array.isArray(result.data) && result.data.length === 0)) {
120+
console.log(`🔍 No data returned for contract ${normalizedContractAddress}:`, {
121+
endpoint,
122+
network,
123+
enabled,
124+
isWellKnownContract,
125+
skip: !normalizedContractAddress || !enabled,
126+
});
127+
128+
if (isWellKnownContract) {
129+
console.warn(`⚠️ This is a well-known contract (CryptoPunks) that should return data.
130+
This might indicate an authentication or network issue.`);
131+
}
132+
} else if (result.data && Array.isArray(result.data) && result.data.length > 0) {
133+
console.log(
134+
`✅ Successfully fetched NFT collection data for ${normalizedContractAddress}:`,
135+
result.data[0].name || "Unknown Collection",
136+
);
137+
}
138+
}
139+
140+
return result;
141+
}

0 commit comments

Comments
 (0)