From f6eb1c288ec7d28befd2fd164e7740ef71b344c4 Mon Sep 17 00:00:00 2001 From: Akachukwu Date: Fri, 10 Jan 2025 03:08:56 +0100 Subject: [PATCH] Added Bundle Checker --- next.config.ts | 32 ++++++++++ postcss.config.js | 6 ++ src/app/bundle-checker/page.tsx | 13 ++++ .../components/BundleChecker.tsx | 45 +++++++++++++ .../bundle-checker/hooks/useBundleAnalysis.ts | 29 +++++++++ src/features/bundle-checker/types/index.ts | 14 ++++ .../utils/analyzeTransactions.ts | 64 +++++++++++++++++++ tailwind.config.js | 9 +++ tsconfig.json | 6 +- 9 files changed, 215 insertions(+), 3 deletions(-) create mode 100644 postcss.config.js create mode 100644 src/app/bundle-checker/page.tsx create mode 100644 src/features/bundle-checker/components/BundleChecker.tsx create mode 100644 src/features/bundle-checker/hooks/useBundleAnalysis.ts create mode 100644 src/features/bundle-checker/types/index.ts create mode 100644 src/features/bundle-checker/utils/analyzeTransactions.ts create mode 100644 tailwind.config.js diff --git a/next.config.ts b/next.config.ts index 5fe04a1e..90fe5659 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,6 +16,38 @@ const nextConfig: NextConfig = { }); return config; }, + experimental: { + turbo: { + // Define unsupported webpack loaders + rules: { + '*.svg': { + loaders: ['@svgr/webpack'], // Example loader for SVG files + as: '*.js', + }, + }, + // Resolve aliases + resolveAlias: { + underscore: 'lodash', // Alias underscore to lodash + mocha: { browser: 'mocha/browser-entry.js' }, // Conditional alias for browser + }, + // Resolve custom extensions + resolveExtensions: [ + '.mdx', + '.tsx', + '.ts', + '.jsx', + '.js', + '.mjs', + '.json', + ], + // Assign module IDs + moduleIdStrategy: 'deterministic', // Use hashed IDs for better caching + // Enable tree shaking + treeShaking: true, + // Set a memory limit for Turbopack + memoryLimit: 1024 * 1024 * 512, // 512MB in bytes + }, + }, }; export default nextConfig; diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 00000000..33ad091d --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/src/app/bundle-checker/page.tsx b/src/app/bundle-checker/page.tsx new file mode 100644 index 00000000..067d724e --- /dev/null +++ b/src/app/bundle-checker/page.tsx @@ -0,0 +1,13 @@ +// src/app/bundle-checker/page.tsx +'use client'; + +import { BundleChecker } from "@/features/bundle-checker/components/BundleChecker"; + +export default function BundleCheckerPage() { + return ( +
+

Bundle/Sniper Checker

+ +
+ ); + } \ No newline at end of file diff --git a/src/features/bundle-checker/components/BundleChecker.tsx b/src/features/bundle-checker/components/BundleChecker.tsx new file mode 100644 index 00000000..ca6491b9 --- /dev/null +++ b/src/features/bundle-checker/components/BundleChecker.tsx @@ -0,0 +1,45 @@ +// src/features/bundle-checker/components/BundleChecker.tsx +'use client'; + +import { useState } from 'react'; +import { useBundleAnalysis } from '../hooks/useBundleAnalysis'; + +export function BundleChecker() { + const [mintAddress, setMintAddress] = useState(''); + const { bundles, loading, error, analyzeMint } = useBundleAnalysis(); + + return ( +
+
+ setMintAddress(e.target.value)} + placeholder="Enter mint address" + className="w-full p-2 border rounded" + /> + +
+ + {error && ( +
{error}
+ )} + + {bundles.map((bundle, i) => ( +
+

Bundle {i + 1}

+
Unique Wallets: {bundle.stats.uniqueWallets}
+
SOL Spent: {bundle.stats.solSpent}
+
Supply %: {bundle.stats.percentageSupply}%
+
Current Holdings: {bundle.stats.currentHoldings}
+
+ ))} +
+ ); +} diff --git a/src/features/bundle-checker/hooks/useBundleAnalysis.ts b/src/features/bundle-checker/hooks/useBundleAnalysis.ts new file mode 100644 index 00000000..410a6e81 --- /dev/null +++ b/src/features/bundle-checker/hooks/useBundleAnalysis.ts @@ -0,0 +1,29 @@ +// src/features/bundle-checker/hooks/useBundleAnalysis.ts +'use client'; + +import { useState } from 'react'; +import { Connection } from '@solana/web3.js'; +import { analyzeTransactions } from '../utils/analyzeTransactions'; +import type { Bundle } from '../types'; + +export function useBundleAnalysis() { + const [bundles, setBundles] = useState([]); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(null); + + async function analyzeMint(mintAddress: string) { + try { + setLoading(true); + setError(null); + const connection = new Connection('https://api.mainnet-beta.solana.com'); + const results = await analyzeTransactions(mintAddress, connection); + setBundles(results); + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred'); + } finally { + setLoading(false); + } + } + + return { bundles, loading, error, analyzeMint }; +} \ No newline at end of file diff --git a/src/features/bundle-checker/types/index.ts b/src/features/bundle-checker/types/index.ts new file mode 100644 index 00000000..75346cf7 --- /dev/null +++ b/src/features/bundle-checker/types/index.ts @@ -0,0 +1,14 @@ +// src/features/bundle-checker/types/index.ts +export interface BundleStats { + percentageSupply: number; + solSpent: number; + uniqueWallets: number; + currentHoldings: number; + timestamp: number; + } + + export interface Bundle { + transactions: string[]; + stats: BundleStats; + wallets: string[]; + } \ No newline at end of file diff --git a/src/features/bundle-checker/utils/analyzeTransactions.ts b/src/features/bundle-checker/utils/analyzeTransactions.ts new file mode 100644 index 00000000..0b4a8e05 --- /dev/null +++ b/src/features/bundle-checker/utils/analyzeTransactions.ts @@ -0,0 +1,64 @@ +// src/features/bundle-checker/utils/analyzeTransactions.ts +import { Connection, PublicKey } from '@solana/web3.js'; +import type { Bundle } from '../types'; + +export async function analyzeTransactions( + mintAddress: string, + connection: Connection +): Promise { + const mint = new PublicKey(mintAddress); + const signatures = await connection.getSignaturesForAddress(mint); + + // Group transactions that occurred within a small time window + const timeWindow = 1000; // 1 second window + let currentBundle: string[] = []; + let bundles: string[][] = []; + + for (let i = 0; i < signatures.length - 1; i++) { + const currentTx = signatures[i]; + const nextTx = signatures[i + 1]; + + if (!currentTx.blockTime || !nextTx.blockTime) continue; + + currentBundle.push(currentTx.signature); + + if (Math.abs(currentTx.blockTime - nextTx.blockTime) > timeWindow) { + if (currentBundle.length > 1) { + bundles.push([...currentBundle]); + } + currentBundle = []; + } + } + + // Analyze each bundle + const analyzedBundles: Bundle[] = await Promise.all( + bundles.map(async (bundleTxs) => { + const txData = await Promise.all( + bundleTxs.map(sig => connection.getTransaction(sig)) + ); + + const wallets = new Set(); + let solSpent = 0; + + txData.forEach(tx => { + if (!tx) return; + wallets.add(tx.transaction.message.accountKeys[0].toString()); + // Add SOL spent calculation here + }); + + return { + transactions: bundleTxs, + stats: { + percentageSupply: 0, // Calculate based on mint info + solSpent, + uniqueWallets: wallets.size, + currentHoldings: 0, // Need to fetch current token accounts + timestamp: txData[0]?.blockTime || 0 + }, + wallets: Array.from(wallets) + }; + }) + ); + + return analyzedBundles; +} \ No newline at end of file diff --git a/tailwind.config.js b/tailwind.config.js new file mode 100644 index 00000000..d947ab17 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,9 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/tsconfig.json b/tsconfig.json index c1334095..d118797b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,17 +7,17 @@ "strict": true, "noEmit": true, "esModuleInterop": true, - "module": "esnext", + "module": "esnext", "moduleResolution": "bundler", "resolveJsonModule": true, "isolatedModules": true, "jsx": "preserve", "incremental": true, - "plugins": [ + "plugins": [ { "name": "next" } - ], + ], "paths": { "@/*": ["./src/*"] }