A decentralized application that generates SEO analysis reports, stores them on IPFS via Pinata, and verifies their authenticity on the Avalanche C-Chain. Built with Next.js 14 and Web3 technologies.
- SEO Analysis: Generate comprehensive SEO reports for any website
- Decentralized Storage: Secure IPFS storage via Pinata with content addressing
- Blockchain Verification: Immutable proof of existence on Avalanche C-Chain
- PDF Generation & Download: Generate and download SEO reports as PDFs
- Web3 Authentication: Secure wallet connection via MetaMask
- Report Management: View, verify, and manage all your SEO reports
- Frontend: Next.js 14, React 18, TypeScript
- Styling: Tailwind CSS, Headless UI
- State Management: React Context, SWR
- Blockchain: Avalanche C-Chain
- Smart Contracts: Solidity (0.8.0+)
- Web3: ethers.js 6.x
- Decentralized Storage: IPFS via Pinata
- Authentication: MetaMask, Web3Modal
- API Routes: Next.js API Routes
- PDF Generation: html2pdf.js
- Form Handling: React Hook Form
- Data Validation: Zod
- HTTP Client: Axios
graph TD
A[User Submits URL] --> B[Generate SEO Analysis]
B --> C[Extract Key Metrics]
C --> D[Generate PDF Report]
D --> E[Compute SHA-256 Hash]
E --> F[Upload to Pinata/IPFS]
F --> G[Store Hash on Avalanche C-Chain]
G --> H[Save Metadata to Firestore]
H --> I[Display Results to User]
style A fill:#4CAF50,stroke:#388E3C,color:white
style I fill:#4CAF50,stroke:#388E3C,color:white
style F fill:#2196F3,stroke:#0D47A1,color:white
style G fill:#9C27B0,stroke:#6A1B9A,color:white
graph TD
A[User Uploads PDF] --> B[Extract Content]
B --> C[Compute SHA-256 Hash]
C --> D[Query Avalanche Contract]
D --> E{Hash Exists?}
E -->|Yes| F[Fetch Timestamp]
E -->|No| G[Return Not Found]
F --> H[Verify IPFS Link]
H --> I[Display Verification Results]
style A fill:#4CAF50,stroke:#388E3C,color:white
style I fill:#4CAF50,stroke:#388E3C,color:white
style D fill:#FF9800,stroke:#E65100,color:white
style H fill:#2196F3,stroke:#0D47A1,color:white
graph LR
A[Frontend] <-->|API Calls| B[Next.js API Routes]
B <--> C[(Firebase Firestore)]
B <--> D[Pinata/IPFS]
B <--> E[Avalanche C-Chain]
style A fill:#FFC107,stroke:#FF8F00,color:black
style B fill:#9C27B0,stroke:#6A1B9A,color:white
style C fill:#2196F3,stroke:#0D47A1,color:white
style D fill:#4CAF50,stroke:#2E7D32,color:white
style E fill:#F44336,stroke:#B71C1C,color:white
seo.ai/
βββ src/
β βββ app/ # Next.js 13+ App Router
β β βββ api/ # API routes
β β β βββ pinata/ # Pinata file operations
β β β βββ reports/ # Report management API
β β βββ profile/ # User profile and reports
β βββ components/ # Reusable UI components
β βββ context/ # React context providers
β βββ lib/ # Utility functions and configs
β βββ firebase.ts # Firebase Firestore integration
βββ scripts/ # Deployment and utility scripts
βββ deploy-contract.js # Contract deployment script
βββ check-contract.js # Contract verification script
βββ verify.md # Contract verification guide
- Node.js 18+ and npm
- MetaMask browser extension
- Pinata API keys
- Avalanche C-Chain RPC URL
-
Clone the repository:
git clone https://github.com/yourusername/seo.ai.git cd seo.ai -
Install dependencies:
npm install
-
Set up environment variables:
NEXT_PUBLIC_PINATA_API_KEY=your_pinata_api_key NEXT_PUBLIC_PINATA_SECRET_API_KEY=your_pinata_secret NEXT_PUBLIC_PINATA_JWT=your_pinata_jwt NEXT_PUBLIC_ALCHEMY_AVALANCHE_URL=your_alchemy_avalanche_url NEXT_PUBLIC_CONTRACT_ADDRESS=your_contract_address
-
Run the development server:
npm run dev
npm run dev- Start development servernpm run build- Create production buildnpm start- Start production servernpm run lint- Run ESLintnpm run format- Format code with Prettier
Deploys the smart contract to Avalanche C-Chain.
Usage:
node scripts/deploy-contract.js --network avalancheOptions:
--network: Target network (default: 'avalanche')--private-key: Private key for deployment (optional, uses .env if not provided)--rpc: Custom RPC URL (optional)
Verifies contract deployment and checks contract state.
Usage:
node scripts/check-contract.js --address 0xContractAddressOptions:
--address: Contract address to check (required)--network: Network to check (default: 'avalanche')--rpc: Custom RPC URL (optional)
The ReportVerification.sol contract provides a simple and secure way to store and verify document hashes on the Avalanche C-Chain.
- Store document hashes with timestamps
- Verify document existence and integrity
- Event emission for all state changes
- Simple and gas-efficient design
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
contract ReportVerification {
// Maps report hashes to their storage timestamps
mapping(string => uint256) private reportTimestamps;
// Event emitted when a new report is stored
event ReportStored(string indexed reportHash, uint256 timestamp);
/**
* @dev Store a report hash with the current block timestamp
* @param _reportHash The hash of the report to store
*/
function storeReport(string memory _reportHash) public {
require(reportTimestamps[_reportHash] == 0, "Report already stored");
reportTimestamps[_reportHash] = block.timestamp;
emit ReportStored(_reportHash, block.timestamp);
}
/**
* @dev Verify if a report hash exists and get its timestamp
* @param _reportHash The hash of the report to verify
* @return The timestamp when the report was stored, or 0 if not found
*/
function verifyReport(string memory _reportHash) public view returns (uint256) {
return reportTimestamps[_reportHash];
}
}storeReport(string memory _reportHash)- Store a new document hashverifyReport(string memory _reportHash) β uint256- Get the timestamp when a report was stored (returns 0 if not found)
POST /api/pinata/upload- Upload file to IPFS via PinataGET /api/pinata/download?cid=<cid>- Download file from IPFS
GET /api/reports/[walletAddress]- Get all reports for a walletPOST /api/reports/store- Store new report metadata
- All sensitive operations require wallet authentication
- Private keys are never stored or transmitted
- Smart contract includes access control and ownership management
- Environment variables are used for sensitive configuration
MIT
- Node.js 18+
- MetaMask browser extension
- Avalanche C-Chain testnet account (for development)
- Pinata API credentials
Create a .env.local file in the root directory:
# Blockchain
AVALANCHE_RPC_URL=https://api.avax-test.network/ext/bc/C/rpc
AVALANCHE_CONTRACT_ADDRESS=0x...
AVALANCHE_PRIVATE_KEY=0x... # For server-side transactions
# Pinata (choose one auth method)
PINATA_JWT=your_jwt_token
# OR
PINATA_API_KEY=your_api_key
PINATA_SECRET_API_KEY=your_secret_api_key
# Optional
PINATA_GATEWAY=your-subdomain.mypinata.cloud
NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID=your_walletconnect_id- Clone the repository
- Install dependencies:
npm install # or yarn install - Run the development server:
npm run dev # or yarn dev - Open http://localhost:3000 in your browser
- Navigate to the home page
- Enter a website URL and click "Generate Report"
- The system will:
- Analyze the website
- Generate a PDF report
- Upload to IPFS via Pinata
- Store the hash on Avalanche C-Chain
- View and download the report with verification details
- Go to
/verify - Upload a previously generated PDF
- The system will:
- Compute the file's SHA-256 hash
- Query the Avalanche contract
- Display verification status and timestamp
Traditional document verification systems suffer from several limitations:
- Centralized Trust: Reliance on a single authority that can be compromised
- Tamper Risk: Documents can be altered without detection
- Verification Complexity: Difficult to independently verify document authenticity
- Single Point of Failure: Centralized servers can go down or be censored
-
Immutable Record
- Once a document's hash is stored on the blockchain, it cannot be altered or deleted
- Provides cryptographic proof of the document's existence at a specific time
-
Decentralized Verification
- No single entity controls the verification process
- Anyone can independently verify a document without permission
- Eliminates the need to trust a central authority
-
Tamper-Evident
- The SHA-256 hash acts as a unique fingerprint for each document
- Even a single character change creates a completely different hash
- Any tampering is immediately detectable during verification
-
Transparent Audit Trail
- Blockchain provides a public, timestamped record of when the document was registered
- Full history of verifications is permanently stored and auditable
-
No Single Point of Failure
- The verification system remains operational as long as the blockchain exists
- No central server that can be taken down or censored
- Legal Documents: Prove the existence and integrity of contracts
- Academic Credentials: Verify the authenticity of certificates and degrees
- Financial Records: Ensure audit trails cannot be altered
- Intellectual Property: Timestamp and verify ownership of creative works
- Supply Chain: Track and verify the authenticity of documents at each step
- Private Key Management: The server's private key is used only for storing hashes on-chain. Never expose it client-side.
- File Hashing: SHA-256 is used to create content-addressable hashes of reports.
- Immutable Verification: Once stored on-chain, report hashes cannot be altered or deleted.
The application uses a hybrid approach:
-
Server-Side Transactions:
- The server handles the actual blockchain transactions using a dedicated wallet
- This ensures consistent gas payments and better reliability
-
Client-Side Verification:
- Users can verify reports without needing a wallet
- The verification is done by querying the blockchain directly
-
Wallet Connection:
- MetaMask integration for future features
- Web3Modal for wallet connection UI
Uploads a file to IPFS and stores its hash on-chain.
Request:
POST /api/pinata/upload
Content-Type: multipart/form-data
file: <PDF file>Response:
{
"success": true,
"cid": "QmXx...",
"ipfsUrl": "https://...",
"hash": "0x...",
"txHash": "0x...",
"blockNumber": 12345678,
"pinataUrl": "https://..."
}Verifies if a file's hash exists on-chain.
Request:
POST /api/verifyReport
Content-Type: multipart/form-data
file: <PDF file>Response (Success):
{
"valid": true,
"timestamp": 1634567890,
"hash": "0x..."
}Response (Not Found):
{
"valid": false,
"message": "Report not found on chain"
}- Push your code to a GitHub/GitLab repository
- Import the repository to Vercel
- Add the environment variables in the Vercel dashboard
- Deploy!
- Build the application:
npm run build
- Start the production server:
npm start
- Set up a reverse proxy (Nginx/Apache) if needed
MIT
- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This section documents the shapes we persist or exchange via APIs. Use this as the single source of truth for contracts between client, server, and chain.
-
users/{walletAddress}walletAddress: stringβ EVM address (checksum optional)createdAt: numberβ epoch secondsupdatedAt: numberβ epoch seconds
-
reports/{autoId}walletAddress: stringhash: stringβ SHA-256 hex string of the PDF/report contentipfsCid: stringβ IPFS CID returned by PinataipfsUrl: stringβ gateway URL (optional)title: stringβ report titlescore: numberβ overall SEO scorecreatedAt: numberβ epoch secondsonChainTxHash?: stringβ transaction hash for store operationonChainTimestamp?: numberβ timestamp returned by contract
-
POST /api/pinata/uploadmultipart form-data- request:
file: Blob - response success:
{ "success": true, "cid": "Qm...", "ipfsUrl": "https://...", "hash": "<sha256-hex>", "txHash": "0x...", "blockNumber": 123, "pinataUrl": "https://..." }
- request:
-
POST /api/verifyReportmultipart form-data- request:
file: Blob - response success:
{ "valid": true, "timestamp": 1712345678, "hash": "<sha256-hex>" } - response not-found:
{ "valid": false, "message": "Report not found on chain" }
- request:
-
GET /api/reports/[walletAddress]- response:
{ "success": true, "reports": [ /* Report documents as above */ ] }
- response:
AVALANCHE_RPC_URLβ HTTPS RPC endpoint to Avalanche C-Chain (testnet or mainnet)AVALANCHE_CONTRACT_ADDRESSβ DeployedReportVerificationcontract addressAVALANCHE_PRIVATE_KEYβ Server-side signer for write ops (secure, never exposed client-side)PINATA_JWTorPINATA_API_KEY+PINATA_SECRET_API_KEYβ Pinata authenticationPINATA_GATEWAYβ Optional dedicated gateway subdomainNEXT_PUBLIC_*β Variables safe for the browser. Keep sensitive keys server-side only.
Local setup checklist:
- Create
.env.localwith required variables - Ensure MetaMask is installed for local wallet connection
- Verify
scripts/deploy-contract.jsnetwork config matches your RPC - If using testnet, pre-fund the deployer address with test AVAX
src/app/β App Router pages and API routesapi/pinata/*β IPFS upload/download via Pinataapi/verifyReportβ Verifies uploaded PDF hash against on-chain recordapi/reports/*β Fetch reports for a given wallet
src/context/web3Context.tsxβ Wallet connection and basic statesrc/lib/firebase.tsβ Firestore/Storage interoperabilitysrc/lib/web3.tsβ Crypto utilities (e.g.,generateReportHash)ReportVerification.solβ Smart contract storing report hashes β timestamps
Interactions:
- Client generates SEO metrics β builds PDF β computes SHA-256
- Server uploads file to IPFS (Pinata) and stores hash on-chain via contract
- Server persists metadata in Firestore for fast listing
- Verification re-computes SHA-256 and cross-checks the contract mapping
- Generate report on
/generate - Confirm PDF preview β click Save/Upload
- Server handles upload and on-chain store, then returns CID + tx hash
- Report appears in user dashboard/profile with chain timestamp
- Verify any time by uploading the same PDF on
/verify
Manual verification via curl:
curl -X POST http://localhost:3000/api/verifyReport \
-F file=@./my-report.pdfQuery reports for a wallet:
curl http://localhost:3000/api/reports/0xYourWalletAddressABI excerpt (for reference):
[
{
"type": "function",
"name": "storeReport",
"inputs": [{ "name": "_reportHash", "type": "string" }],
"stateMutability": "nonpayable"
},
{
"type": "function",
"name": "verifyReport",
"inputs": [{ "name": "_reportHash", "type": "string" }],
"outputs": [{ "type": "uint256" }],
"stateMutability": "view"
},
{
"type": "event",
"name": "ReportStored",
"inputs": [
{ "name": "reportHash", "type": "string", "indexed": true },
{ "name": "timestamp", "type": "uint256", "indexed": false }
]
}
]Quick script (ethers v6) to read a hash:
import { JsonRpcProvider, Contract } from "ethers";
const provider = new JsonRpcProvider(process.env.AVALANCHE_RPC_URL!);
const abi = ["function verifyReport(string) view returns (uint256)"];
const contract = new Contract(
process.env.AVALANCHE_CONTRACT_ADDRESS!,
abi,
provider
);
const ts = await contract.verifyReport("<sha256-hex>");
console.log(Number(ts));- Unit test hashing utilities in
src/lib/web3.ts - Use a small set of fixture PDFs to validate end-to-end flow in dev
- Contract tests: run
scripts/check-contract.jsagainst a local or testnet RPC
- Deployment fails with missing env: ensure
.env.localprovidesAVALANCHE_RPC_URL,AVALANCHE_CONTRACT_ADDRESS, and Pinata credentials. - window.ethereum type errors during build: see
src/context/web3Context.tsxfor EIP-1193 provider typing. - Report not found on chain: Verify the computed SHA-256 matches the stored one. Even minor PDF changes alter the hash.
- Pinata upload errors: Check JWT/API keys and ensure file sizes are within Pinata limits.
- CORS or gateway access: Prefer your dedicated
PINATA_GATEWAYsubdomain.
-
What exactly is stored on-chain? Only the SHA-256 hash (a fixed-length fingerprint). No raw content or PII is written to the blockchain.
-
Can I verify without a wallet? Yes. Verification is a read-only chain query handled by the server.
-
Do I need AVAX to verify? No. Only write operations consume gas. Reads are free via the RPC provider.
-
What happens if my PDF changes slightly? The hash changes completely, so verification will fail. Re-generate and store again to create a new on-chain record.
- IPFS β InterPlanetary File System, content-addressed storage
- CID β Content Identifier (hash of content + multicodec info)
- EIP-1193 β Ethereum provider standard (e.g.,
window.ethereum) - C-Chain β Avalanche EVM-compatible chain
- Role-based access for organizational reports
- Encrypted, client-side generated PDFs
- Batch verification and bulk exports
- Walletless login via email magic links
- On-chain merkle batching to reduce gas
- Keep dependencies updated; see
package.json - Re-run
npm run lintbefore commits - Prefer server-side secrets; never expose private keys to the client
Senapati484 - @sayan4.vercel.app - sayansenapati2544@gmail.com
Project Link: https://github.com/senapati484/seo.ai
This project uses next/font to automatically optimize and load Geist, a new font family for Vercel.
To learn more about Next.js, take a look at the following resources:
- Next.js Documentation - learn about Next.js features and API.
- Learn Next.js - an interactive Next.js tutorial.
You can check out the Next.js GitHub repository - your feedback and contributions are welcome!
The easiest way to deploy your Next.js app is to use the Vercel Platform from the creators of Next.js.
Check out our Next.js deployment documentation for more details.