Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
27c0de8
Merge pull request #138 from TTMordred/Staking
Woft257 Mar 25, 2025
8f35a38
Update NFT collection data in alchemyNFTApi.ts
TTMordred Mar 26, 2025
1f55f1b
Refactor attribute processing in AnimatedNFTCard to handle non-array …
TTMordred Mar 26, 2025
2521959
Add new NFT collections to alchemyNFTApi mock data
TTMordred Mar 26, 2025
63b1c14
Merge branch 'main' of https://github.com/TTMordred/CryptoPath into n…
TTMordred Mar 26, 2025
41b3ce3
Merge pull request #139 from TTMordred/nft_collection_2
TTMordred Mar 26, 2025
d2b556f
Remove Sepolia network references and update network handling logic
TTMordred Mar 26, 2025
ba3481c
Add IPFS utility functions and integrate IPFS image handling in Anima…
TTMordred Mar 26, 2025
a18a0da
Add Alchemy API routes for fetching block and transaction details
HungPhan-0612 Mar 26, 2025
c544682
Refactor network badge display in AnimatedNFTCard and CollectionCard3…
TTMordred Mar 26, 2025
d11237d
Merge pull request #140 from TTMordred/hungphannelan2
TTMordred Mar 26, 2025
53e8252
Add loading placeholders for trending collections in NFTCollectionPage
TTMordred Mar 26, 2025
ec7f79b
Merge branch 'main' of https://github.com/TTMordred/CryptoPath into n…
TTMordred Mar 26, 2025
b661f3c
Merge pull request #141 from TTMordred/nft_collection_2
TTMordred Mar 26, 2025
6e4ac03
Add remote patterns for DappRadar API in next.config.js
TTMordred Mar 26, 2025
3e2eb77
Add BIT LIFE CLUB collection and update image URLs in alchemyNFTApi; …
TTMordred Mar 26, 2025
54ede11
Merge pull request #142 from TTMordred/nft_collection_2
TTMordred Mar 26, 2025
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
3 changes: 0 additions & 3 deletions app/NFT/collection/[collectionId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ export default function CollectionDetailsPage() {
// Add network as a filter attribute
attributeMap['Network'] = [
networkId === '0x1' ? 'Ethereum' :
networkId === '0xaa36a7' ? 'Sepolia' :
networkId === '0x38' ? 'BNB Chain' :
'BNB Testnet'
];
Expand Down Expand Up @@ -339,7 +338,6 @@ export default function CollectionDetailsPage() {
const getExplorerLink = (address: string) => {
const chainConfig = {
'0x1': 'https://etherscan.io',
'0xaa36a7': 'https://sepolia.etherscan.io',
'0x38': 'https://bscscan.com',
'0x61': 'https://testnet.bscscan.com',
};
Expand All @@ -352,7 +350,6 @@ export default function CollectionDetailsPage() {
const getNetworkName = () => {
const networks = {
'0x1': 'Ethereum',
'0xaa36a7': 'Sepolia',
'0x38': 'BNB Chain',
'0x61': 'BNB Testnet',
};
Expand Down
140 changes: 94 additions & 46 deletions app/NFT/collection/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -202,17 +202,24 @@ export default function NFTCollectionPage() {
method: 'eth_chainId',
});
setChainId(chainId);
// Load collections immediately after setting chainId
loadCollections(chainId);
// Initial trending data - load for default period
loadTrendingCollections('24h');
} catch (error) {
console.error('Error checking network:', error);
// If there's an error, still load with default chainId
loadCollections(chainId);
loadTrendingCollections('24h');
}
} else {
// If no window.ethereum, load with default chainId
loadCollections(chainId);
loadTrendingCollections('24h');
}
};

checkNetwork();
loadCollections(chainId);

// Initial trending data
loadTrendingCollections('24h');
}, []);

// Load user NFTs when account or chain changes
Expand Down Expand Up @@ -304,6 +311,29 @@ export default function NFTCollectionPage() {
// Load trending collections
const loadTrendingCollections = useCallback(async (period: string) => {
try {
// Create placeholder data with empty array if collections not loaded yet
if (collections.length === 0) {
// Set mock empty data with period to display the section even when data is loading
setTrendingData({
period,
data: [
{
id: 'loading-1',
name: 'Loading...',
imageUrl: '/images/placeholder-nft.png',
chain: chainId,
floorPrice: '0.00',
priceChange: 0,
volume: '0.00',
volumeChange: 0,
},
// Add more placeholder items as needed
]
});
// Return early - real data will be loaded once collections are available
return;
}

// In real app this would fetch real data
const mockTrendingData = {
period,
Expand All @@ -323,7 +353,14 @@ export default function NFTCollectionPage() {
} catch (error) {
console.error('Error loading trending data:', error);
}
}, [collections]);
}, [collections, chainId]);

useEffect(() => {
// When collections are loaded, update the trending data
if (collections.length > 0) {
loadTrendingCollections(trendingPeriod);
}
}, [collections, loadTrendingCollections, trendingPeriod]);

const handleNetworkChange = (networkId: string) => {
setChainId(networkId);
Expand Down Expand Up @@ -516,52 +553,63 @@ export default function NFTCollectionPage() {
</tr>
</thead>
<tbody>
{trendingData.data.map((item, i) => (
<motion.tr
key={item.id}
whileHover={{ backgroundColor: "rgba(255,255,255,0.03)" }}
className="cursor-pointer border-b border-gray-800/50"
onClick={() => handleCardClick(item.id)}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + i * 0.05 }}
>
<td className="p-2 text-gray-400">{i + 1}</td>
<td className="p-2">
<div className="flex items-center gap-3">
<div className="relative h-10 w-10 rounded-lg overflow-hidden">
<Image
src={item.imageUrl}
alt={item.name}
fill
className="object-cover"
/>
</div>
<div>
<div className="font-medium flex items-center gap-1">
{item.name}
{(i === 0 || i === 2) && <Verified className="h-3.5 w-3.5 text-blue-400" />}
{trendingData.data.length > 0 ? (
trendingData.data.map((item, i) => (
<motion.tr
key={item.id}
whileHover={{ backgroundColor: "rgba(255,255,255,0.03)" }}
className="cursor-pointer border-b border-gray-800/50"
onClick={() => handleCardClick(item.id)}
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ delay: 0.3 + i * 0.05 }}
>
<td className="p-2 text-gray-400">{i + 1}</td>
<td className="p-2">
<div className="flex items-center gap-3">
<div className="relative h-10 w-10 rounded-lg overflow-hidden">
<Image
src={item.imageUrl}
alt={item.name}
fill
className="object-cover"
/>
</div>
<div className="text-xs text-gray-400">
{getNetworkName(item.chain)}
<div>
<div className="font-medium flex items-center gap-1">
{item.name}
{(i === 0 || i === 2) && <Verified className="h-3.5 w-3.5 text-blue-400" />}
</div>
<div className="text-xs text-gray-400">
{getNetworkName(item.chain)}
</div>
</div>
</div>
</td>
<td className="p-2 text-right font-medium">
{item.floorPrice} {item.chain === '0x1' || item.chain === '0xaa36a7' ? 'ETH' : 'BNB'}
</td>
<td className={`p-2 text-right ${item.priceChange >= 0 ? 'text-green-500' : 'text-red-500'}`}>
{item.priceChange >= 0 ? '+' : ''}{item.priceChange.toFixed(2)}%
</td>
<td className="p-2 text-right font-medium">
{item.volume} {item.chain === '0x1' || item.chain === '0xaa36a7' ? 'ETH' : 'BNB'}
</td>
<td className={`p-2 text-right ${item.volumeChange >= 0 ? 'text-green-500' : 'text-red-500'}`}>
{item.volumeChange >= 0 ? '+' : ''}{item.volumeChange.toFixed(2)}%
</td>
</motion.tr>
))
) : (
<tr className="border-b border-gray-800/50">
<td colSpan={6} className="p-4 text-center text-gray-400">
<div className="flex flex-col items-center gap-2">
<div className="h-5 w-5 border-2 border-t-transparent animate-spin rounded-full" />
<span>Loading trending collections...</span>
</div>
</td>
<td className="p-2 text-right font-medium">
{item.floorPrice} {item.chain === '0x1' || item.chain === '0xaa36a7' ? 'ETH' : 'BNB'}
</td>
<td className={`p-2 text-right ${item.priceChange >= 0 ? 'text-green-500' : 'text-red-500'}`}>
{item.priceChange >= 0 ? '+' : ''}{item.priceChange.toFixed(2)}%
</td>
<td className="p-2 text-right font-medium">
{item.volume} {item.chain === '0x1' || item.chain === '0xaa36a7' ? 'ETH' : 'BNB'}
</td>
<td className={`p-2 text-right ${item.volumeChange >= 0 ? 'text-green-500' : 'text-red-500'}`}>
{item.volumeChange >= 0 ? '+' : ''}{item.volumeChange.toFixed(2)}%
</td>
</motion.tr>
))}
</tr>
)}
</tbody>
</table>
</div>
Expand Down
114 changes: 114 additions & 0 deletions app/api/alchemy-block-txns/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { NextResponse } from "next/server";

const ALCHEMY_API_KEY = process.env.ALCHEMY_API_KEY;
const ALCHEMY_API_URL = `https://eth-mainnet.g.alchemy.com/v2/${ALCHEMY_API_KEY}`;

interface Transaction {
blockHash: string;
blockNumber: string;
from: string;
gas: string;
gasPrice: string;
hash: string;
input: string;
nonce: string;
to: string;
transactionIndex: string;
value: string;
type: string;
timestamp: number;
}

export async function GET(request: Request) {
const { searchParams } = new URL(request.url);
const blockNumber = searchParams.get("blockNumber");

if (!blockNumber) {
return NextResponse.json(
{ error: "Block number is required" },
{ status: 400 }
);
}

try {
// First, get block data to get timestamp
const blockResponse = await fetch(ALCHEMY_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_getBlockByNumber',
params: [
`0x${Number(blockNumber).toString(16)}`,
false
]
})
});

const blockData = await blockResponse.json();

if (!blockData.result) {
return NextResponse.json(
{ error: "Block not found" },
{ status: 404 }
);
}

const timestamp = parseInt(blockData.result.timestamp, 16);

// Get all transactions from the block
const txnsResponse = await fetch(ALCHEMY_API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'eth_getBlockByNumber',
params: [
`0x${Number(blockNumber).toString(16)}`,
true
]
})
});

const txnsData = await txnsResponse.json();

if (!txnsData.result || !txnsData.result.transactions) {
return NextResponse.json(
{ error: "Failed to fetch transactions" },
{ status: 500 }
);
}

const transactions: Transaction[] = txnsData.result.transactions.map((tx: any) => ({
blockHash: tx.blockHash,
blockNumber: parseInt(tx.blockNumber, 16).toString(),
from: tx.from,
gas: parseInt(tx.gas, 16).toString(),
gasPrice: parseInt(tx.gasPrice, 16).toString(),
hash: tx.hash,
input: tx.input,
nonce: parseInt(tx.nonce, 16).toString(),
to: tx.to || '0x0', // Contract creation if no 'to' address
transactionIndex: parseInt(tx.transactionIndex, 16).toString(),
value: (parseInt(tx.value, 16) / 1e18).toString(), // Convert from Wei to ETH
type: parseInt(tx.type, 16).toString(),
timestamp: timestamp
}));

return NextResponse.json({
blockNumber,
timestamp,
transactions,
total: transactions.length
});

} catch (error) {
console.error("Error fetching block transactions:", error);
return NextResponse.json(
{ error: "Failed to fetch block transactions" },
{ status: 500 }
);
}
}
Loading