diff --git a/.env.example b/.env.example
index d5d7e6af..dd7f82bc 100644
--- a/.env.example
+++ b/.env.example
@@ -1,17 +1,5 @@
-NEXT_PUBLIC_NEYNAR_CLIENT_ID=
-NEYNAR_CLIENT_ID=
-NEYNAR_API_KEY=
-NEXT_PUBLIC_REFERRAL=
-SOUND_API_KEY=
-NEYNAR_CLIENT_ID=
-SUPABASE_URL=
-SUPABASE_KEY=
-NEXT_PUBLIC_SPOTIFY_CLIENT_ID=
-SPOTIFY_CLIENT_SECRET=
-STACK_SYSTEM_ID=
-STACK_API_KEY=
-NEXT_PUBLIC_ANON_KEY=
-NEXT_PUBLIC_SUPABASE_URL=
-NEXT_PUBLIC_THIRDWEB_CLIENT_ID=
PRIVY_APP_ID=
-PRIVY_APP_SECRET=
\ No newline at end of file
+PRIVY_APP_SECRET=
+REWARDS_DEPOSIT_POINT_SYSTEM_ID=
+STACK_API_KEY=
+STACK_POINT_SYSTEM_ID=
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 406a929d..83677105 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2024 Sonata
+Copyright (c) 2024 sweetman.eth
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index e1102b2a..693d8316 100644
--- a/README.md
+++ b/README.md
@@ -1,43 +1,78 @@
-# sonata
+# myco API 🌟
-music client for farcaster
+myco API is a powerful tool designed to help developers and creators leverage the Zora ecosystem more effectively. It provides a suite of endpoints that simplify interaction with Zora's blockchain data and offer valuable insights. 🚀
-supports music on
+## Tech Stack 🛠️
-- Spotify
-- Soundcloud
-- Sound.xyz
-- Zora (coming soon)
+- **Frontend**: Next.js 14, React 18, TypeScript
+- **Styling**: Tailwind CSS, shadcn/ui
+- **Backend**: Next.js API Routes
+- **Blockchain Interaction**: viem
+- **Authentication**: Privy
+- **Analytics**: Stack
+- **Deployment**: Vercel
-# Setup
+## Getting Started 🏁
-1. Download and install Docker: https://docs.docker.com/get-docker/
-2. Open the Docker App on your machine. Verify with `docker info`.
-3. Use node v20 with `nvm use 20`.
-4. Run the following in the root of the sonata codebase:
- `npx supabase init`.
-5. Run the starting function
- `npx supabase start`. This will now have the database live with some production data available to you.
-6. Run
- `npx supabase status`
-7. Now go to .env file and fill the env variables
+1. Clone the repository:
-# DB Issues
+ ```
+ git clone https://github.com/sweetmantech/myco-api.git
+ cd myco-api
+ ```
-- If you see any errors happening db related or, need to make DB migrations run the following: `npx supabase db reset`
+2. Install dependencies:
-```
-NEXT_PUBLIC_SUPABASE_URL={API URL}
-NEXT_PUBLIC_ANON_KEY={anon key}
-SUPABASE_URL={API URL}
-SUPABASE_KEY={service_role key}
-```
+ ```
+ yarn install
+ ```
-## Authors
+3. Set up environment variables:
+ Create a `.env.local` file in the root directory and add the following variables:
-- [@sweetmantech](https://github.com/sweetmantech) ([warpcast](https://warpcast.com/sweetman-eth))
-- [@ramiechaarani](https://github.com/ramiechaarani) ([warpcast](https://warpcast.com/ramie/))
+ ```
+ STACK_API_KEY=your_stack_api_key
+ PRIVY_APP_ID=your_privy_app_id
+ PRIVY_APP_SECRET=your_privy_app_secret
+ ```
+
+4. Run the development server:
+
+ ```
+ yarn dev
+ ```
+
+5. Open [http://localhost:3000](http://localhost:3000) in your browser to see the application.
+
+## How myco API Helps Developers 👨💻
+
+1. **Simplified Zora Integration**: Access Zora protocol data through easy-to-use REST API endpoints. 🔗
+2. **Profile Information**: Retrieve comprehensive Zora profile data for any address. 👤
+3. **Token and Collection Data**: Easily fetch information about Zora tokens and collections. 🖼️
+4. **Rewards System**: Access and track Zora rewards for different activities. 🏆
+5. **Leaderboard**: Implement gamification features using the built-in leaderboard system. 🏅
+6. **Scoring System**: Utilize the Zora score calculation for user engagement metrics. 📊
+
+## How myco API Helps Creators 🎨
+
+1. **Profile Insights**: Gain valuable insights into your Zora profile, including completeness score and follower metrics. 📈
+2. **Reward Tracking**: Easily monitor your earnings from creator rewards, referrals, and first minter bonuses. 💰
+3. **Collection Management**: Access detailed information about your created collections and their performance. 🗂️
+4. **Engagement Metrics**: Understand your audience better with follower and following scores. 🤝
+5. **Leaderboard Participation**: Increase visibility and engagement by participating in the platform's leaderboard. 🌟
-## License
+## API Endpoints 🔌
-This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details
+For a complete list of available API endpoints and their usage, please refer to our [API documentation](https://api.myco.wtf).
+
+## Contributing 🤝
+
+We welcome contributions! Please open a pull request to contribute.
+
+## License 📄
+
+This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details.
+
+## Authors ✍️
+
+- [@sweetmantech](https://github.com/sweetmantech) ([warpcast](https://warpcast.com/sweetman-eth))
diff --git a/abis/hypersub.json b/abis/hypersub.json
deleted file mode 100644
index 0e5e202b..00000000
--- a/abis/hypersub.json
+++ /dev/null
@@ -1,802 +0,0 @@
-[
- { "inputs": [], "stateMutability": "nonpayable", "type": "constructor" },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "owner", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "approved", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "Approval",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "owner", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "operator", "type": "address" },
- { "indexed": false, "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "ApprovalForAll",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "uint256", "name": "tokens", "type": "uint256" }
- ],
- "name": "FeeAllocated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "from", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "to", "type": "address" }
- ],
- "name": "FeeCollectorChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "from", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "to", "type": "address" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokensTransferred",
- "type": "uint256"
- }
- ],
- "name": "FeeTransfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "indexed": false, "internalType": "uint256", "name": "secondsGranted", "type": "uint256" },
- { "indexed": false, "internalType": "uint256", "name": "expiresAt", "type": "uint256" }
- ],
- "name": "Grant",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [{ "indexed": false, "internalType": "uint8", "name": "version", "type": "uint8" }],
- "name": "Initialized",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "OwnershipTransferStarted",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "previousOwner", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "newOwner", "type": "address" }
- ],
- "name": "OwnershipTransferred",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "Paused",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokensTransferred",
- "type": "uint256"
- },
- { "indexed": false, "internalType": "uint256", "name": "timePurchased", "type": "uint256" },
- { "indexed": false, "internalType": "uint256", "name": "rewardPoints", "type": "uint256" },
- { "indexed": false, "internalType": "uint256", "name": "expiresAt", "type": "uint256" }
- ],
- "name": "Purchase",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "uint256", "name": "id", "type": "uint256" },
- { "indexed": false, "internalType": "uint16", "name": "rewardBps", "type": "uint16" }
- ],
- "name": "ReferralCreated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [{ "indexed": false, "internalType": "uint256", "name": "id", "type": "uint256" }],
- "name": "ReferralDestroyed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "indexed": true, "internalType": "address", "name": "referrer", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "referralId", "type": "uint256" },
- { "indexed": false, "internalType": "uint256", "name": "rewardAmount", "type": "uint256" }
- ],
- "name": "ReferralPayout",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokensTransferred",
- "type": "uint256"
- },
- { "indexed": false, "internalType": "uint256", "name": "timeReclaimed", "type": "uint256" }
- ],
- "name": "Refund",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "uint256", "name": "tokensIn", "type": "uint256" }
- ],
- "name": "RefundTopUp",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "slasher", "type": "address" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "rewardPointsSlashed",
- "type": "uint256"
- }
- ],
- "name": "RewardPointsSlashed",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokensTransferred",
- "type": "uint256"
- }
- ],
- "name": "RewardWithdraw",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "uint256", "name": "tokens", "type": "uint256" }
- ],
- "name": "RewardsAllocated",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "uint256", "name": "supplyCap", "type": "uint256" }
- ],
- "name": "SupplyCapChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "from", "type": "address" },
- { "indexed": true, "internalType": "address", "name": "to", "type": "address" },
- { "indexed": true, "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "Transfer",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "recipient", "type": "address" }
- ],
- "name": "TransferRecipientChange",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": false, "internalType": "address", "name": "account", "type": "address" }
- ],
- "name": "Unpaused",
- "type": "event"
- },
- {
- "anonymous": false,
- "inputs": [
- { "indexed": true, "internalType": "address", "name": "account", "type": "address" },
- {
- "indexed": false,
- "internalType": "uint256",
- "name": "tokensTransferred",
- "type": "uint256"
- }
- ],
- "name": "Withdraw",
- "type": "event"
- },
- {
- "inputs": [],
- "name": "acceptOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "approve",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "balanceOf",
- "outputs": [{ "internalType": "uint256", "name": "numSeconds", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "baseTokenURI",
- "outputs": [{ "internalType": "string", "name": "uri", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address[]", "name": "accounts", "type": "address[]" }],
- "name": "canRefund",
- "outputs": [{ "internalType": "bool", "name": "refundable", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "contractURI",
- "outputs": [{ "internalType": "string", "name": "uri", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "code", "type": "uint256" },
- { "internalType": "uint16", "name": "bps", "type": "uint16" }
- ],
- "name": "createReferralCode",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "creatorBalance",
- "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "code", "type": "uint256" }],
- "name": "deleteReferralCode",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "erc20Address",
- "outputs": [{ "internalType": "address", "name": "erc20", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "feeBalance",
- "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "feeSchedule",
- "outputs": [
- { "internalType": "address", "name": "feeCollector", "type": "address" },
- { "internalType": "uint16", "name": "feeBps", "type": "uint16" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }],
- "name": "getApproved",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address[]", "name": "accounts", "type": "address[]" },
- { "internalType": "uint256", "name": "secondsToAdd", "type": "uint256" }
- ],
- "name": "grantTime",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "string", "name": "name", "type": "string" },
- { "internalType": "string", "name": "symbol", "type": "string" },
- { "internalType": "string", "name": "contractUri", "type": "string" },
- { "internalType": "string", "name": "tokenUri", "type": "string" },
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "uint256", "name": "tokensPerSecond", "type": "uint256" },
- { "internalType": "uint256", "name": "minimumPurchaseSeconds", "type": "uint256" },
- { "internalType": "uint16", "name": "rewardBps", "type": "uint16" },
- { "internalType": "uint8", "name": "numRewardHalvings", "type": "uint8" },
- { "internalType": "uint16", "name": "feeBps", "type": "uint16" },
- { "internalType": "address", "name": "feeRecipient", "type": "address" },
- { "internalType": "address", "name": "erc20TokenAddr", "type": "address" }
- ],
- "internalType": "struct Shared.InitParams",
- "name": "params",
- "type": "tuple"
- }
- ],
- "name": "initialize",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "owner", "type": "address" },
- { "internalType": "address", "name": "operator", "type": "address" }
- ],
- "name": "isApprovedForAll",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "minPurchaseSeconds",
- "outputs": [{ "internalType": "uint256", "name": "numSeconds", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "name": "mint",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "account", "type": "address" },
- { "internalType": "uint256", "name": "numTokens", "type": "uint256" }
- ],
- "name": "mintFor",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "numTokens", "type": "uint256" },
- { "internalType": "uint256", "name": "referralCode", "type": "uint256" },
- { "internalType": "address", "name": "referrer", "type": "address" }
- ],
- "name": "mintWithReferral",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "account", "type": "address" },
- { "internalType": "uint256", "name": "numTokens", "type": "uint256" },
- { "internalType": "uint256", "name": "referralCode", "type": "uint256" },
- { "internalType": "address", "name": "referrer", "type": "address" }
- ],
- "name": "mintWithReferralFor",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "name",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "owner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }],
- "name": "ownerOf",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "pause",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "paused",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "pendingOwner",
- "outputs": [{ "internalType": "address", "name": "", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "reconcileERC20Balance",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "reconcileNativeBalance",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "tokenAddress", "type": "address" },
- { "internalType": "address", "name": "recipientAddress", "type": "address" },
- { "internalType": "uint256", "name": "tokenAmount", "type": "uint256" }
- ],
- "name": "recoverERC20",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "recipient", "type": "address" }],
- "name": "recoverNativeTokens",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "code", "type": "uint256" }],
- "name": "referralCodeBps",
- "outputs": [{ "internalType": "uint16", "name": "bps", "type": "uint16" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "uint256", "name": "numTokensIn", "type": "uint256" },
- { "internalType": "address[]", "name": "accounts", "type": "address[]" }
- ],
- "name": "refund",
- "outputs": [],
- "stateMutability": "payable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "refundableBalanceOf",
- "outputs": [{ "internalType": "uint256", "name": "numSeconds", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address[]", "name": "accounts", "type": "address[]" }],
- "name": "refundableTokenBalanceOfAll",
- "outputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "renounceOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "rewardBalanceOf",
- "outputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "rewardBps",
- "outputs": [{ "internalType": "uint16", "name": "bps", "type": "uint16" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "rewardMultiplier",
- "outputs": [{ "internalType": "uint256", "name": "multiplier", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "rewardPoolBalance",
- "outputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "internalType": "bytes", "name": "data", "type": "bytes" }
- ],
- "name": "safeTransferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "operator", "type": "address" },
- { "internalType": "bool", "name": "approved", "type": "bool" }
- ],
- "name": "setApprovalForAll",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "supplyCap", "type": "uint256" }],
- "name": "setSupplyCap",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "recipient", "type": "address" }],
- "name": "setTransferRecipient",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "slashRewards",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "subscriptionOf",
- "outputs": [
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" },
- { "internalType": "uint256", "name": "refundableAmount", "type": "uint256" },
- { "internalType": "uint256", "name": "rewardPoints", "type": "uint256" },
- { "internalType": "uint256", "name": "expiresAt", "type": "uint256" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "supplyDetail",
- "outputs": [
- { "internalType": "uint256", "name": "count", "type": "uint256" },
- { "internalType": "uint256", "name": "cap", "type": "uint256" }
- ],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "bytes4", "name": "interfaceId", "type": "bytes4" }],
- "name": "supportsInterface",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "symbol",
- "outputs": [{ "internalType": "string", "name": "", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "name": "timeValue",
- "outputs": [{ "internalType": "uint256", "name": "numSeconds", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "uint256", "name": "tokenId", "type": "uint256" }],
- "name": "tokenURI",
- "outputs": [{ "internalType": "string", "name": "uri", "type": "string" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalCreatorEarnings",
- "outputs": [{ "internalType": "uint256", "name": "total", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "totalRewardPoints",
- "outputs": [{ "internalType": "uint256", "name": "numPoints", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "tps",
- "outputs": [{ "internalType": "uint256", "name": "numTokens", "type": "uint256" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "transferAllBalances",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "transferFees",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "address", "name": "from", "type": "address" },
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "tokenId", "type": "uint256" }
- ],
- "name": "transferFrom",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "newOwner", "type": "address" }],
- "name": "transferOwnership",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "transferRecipient",
- "outputs": [{ "internalType": "address", "name": "recipient", "type": "address" }],
- "stateMutability": "view",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "unpause",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "newCollector", "type": "address" }],
- "name": "updateFeeRecipient",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- { "internalType": "string", "name": "contractUri", "type": "string" },
- { "internalType": "string", "name": "tokenUri", "type": "string" }
- ],
- "name": "updateMetadata",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "withdraw",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "withdrawAndTransferFees",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [],
- "name": "withdrawRewards",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [{ "internalType": "address", "name": "account", "type": "address" }],
- "name": "withdrawTo",
- "outputs": [],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- { "stateMutability": "payable", "type": "receive" }
-]
diff --git a/app/(feeds)/[username]/page.tsx b/app/(feeds)/[username]/page.tsx
deleted file mode 100644
index 4fddf8c5..00000000
--- a/app/(feeds)/[username]/page.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { getFrameMetadata } from '@coinbase/onchainkit';
-import type { Metadata } from 'next';
-import { DEFAULT_FRAME, DESCRIPTION, TITLE, VERCEL_URL } from '@/lib/consts';
-import Feeds from '../feeds';
-
-const frameMetadata = { ...getFrameMetadata(DEFAULT_FRAME), 'of:accepts:xmtp': '2024-02-01' };
-
-export const metadata: Metadata = {
- title: TITLE,
- description: DESCRIPTION,
- openGraph: {
- title: TITLE,
- description: DESCRIPTION,
- images: `${VERCEL_URL}/images/og.png`,
- },
- icons: [`${VERCEL_URL}/images/logo2.png`],
- other: {
- ...frameMetadata,
- },
-};
-
-export default function ProfileHome() {
- return ;
-}
diff --git a/app/(feeds)/channel/[channelId]/page.tsx b/app/(feeds)/channel/[channelId]/page.tsx
deleted file mode 100644
index 2bb29ec5..00000000
--- a/app/(feeds)/channel/[channelId]/page.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import Feeds from '@/app/(feeds)/feeds';
-import { CHANNELS } from '@/lib/consts';
-
-export async function generateStaticParams() {
- return CHANNELS.map((channel) => {
- return { channelId: channel.value };
- });
-}
-
-export default function Channel({ params }: { params: { channelId: string } }) {
- return ;
-}
diff --git a/app/(feeds)/feeds.tsx b/app/(feeds)/feeds.tsx
deleted file mode 100644
index e8e9fc34..00000000
--- a/app/(feeds)/feeds.tsx
+++ /dev/null
@@ -1,33 +0,0 @@
-'use client';
-
-import { useEffect } from 'react';
-import Feed from '@/components/Feed';
-import Loader from '@/components/Loader';
-import { useFeedProvider } from '@/providers/FeedProvider';
-import InfiniteScroll from 'react-infinite-scroll-component';
-
-const Feeds = ({ channelId }: { channelId?: string }) => {
- const { feed, fetchMore, hasMore, updateFilter, filter } = useFeedProvider();
-
- useEffect(() => {
- if (!filter.channel && channelId !== '/') {
- updateFilter({ channel: channelId });
- }
- }, [channelId]);
-
- return (
- fetchMore(feed.length)}
- hasMore={hasMore}
- loader={}
- endMessage={{`That's All!`}
}
- className="!overflow-y-hidden"
- scrollableTarget="feed-container"
- >
-
-
- );
-};
-
-export default Feeds;
diff --git a/app/(feeds)/layout.tsx b/app/(feeds)/layout.tsx
deleted file mode 100644
index 057b9b0d..00000000
--- a/app/(feeds)/layout.tsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-
-import { ReactNode } from 'react';
-import Sidebar from '@/components/Sidebar';
-import { Sheet, SheetContent } from '@/components/ui/sheet';
-import { useUi } from '@/providers/UiProvider';
-import Header from '@/components/Header';
-import { Separator } from '@/components/ui/separator';
-import GlobalPlayer from '@/components/GlobalPlayer';
-import CreatePost from '@/components/CreatePost';
-import { useNeynarProvider } from '@/providers/NeynarProvider';
-import FeedProvider from '@/providers/FeedProvider';
-import ProfileProvider from '@/providers/ProfileProvider';
-import { useParams } from 'next/navigation';
-import TipsList from '@/components/TipsList';
-
-export default function FeedLayout({ children }: { children: ReactNode }) {
- const { menuOpen, setMenuOpen } = useUi();
- const { username } = useParams();
- const { user } = useNeynarProvider();
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {user && !username && }
- {children}
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/app/(feeds)/loading.tsx b/app/(feeds)/loading.tsx
deleted file mode 100644
index 9fb9cda6..00000000
--- a/app/(feeds)/loading.tsx
+++ /dev/null
@@ -1,9 +0,0 @@
-import { Loader2 } from 'lucide-react';
-
-export default function Loading() {
- return (
-
-
-
- );
-}
diff --git a/app/(feeds)/page.tsx b/app/(feeds)/page.tsx
deleted file mode 100644
index 016efefd..00000000
--- a/app/(feeds)/page.tsx
+++ /dev/null
@@ -1,24 +0,0 @@
-import { getFrameMetadata } from '@coinbase/onchainkit';
-import type { Metadata } from 'next';
-import { DEFAULT_FRAME, DESCRIPTION, TITLE, VERCEL_URL } from '@/lib/consts';
-import Feeds from './feeds';
-
-const frameMetadata = { ...getFrameMetadata(DEFAULT_FRAME), 'of:accepts:xmtp': '2024-02-01' };
-
-export const metadata: Metadata = {
- title: TITLE,
- description: DESCRIPTION,
- openGraph: {
- title: TITLE,
- description: DESCRIPTION,
- images: `${VERCEL_URL}/images/og.png`,
- },
- icons: [`${VERCEL_URL}/images/logo2.png`],
- other: {
- ...frameMetadata,
- },
-};
-
-export default function FeedsHome() {
- return ;
-}
diff --git a/app/api/channel/stats/route.ts b/app/api/channel/stats/route.ts
deleted file mode 100644
index 4cff34e8..00000000
--- a/app/api/channel/stats/route.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { CHANNELS } from "@/lib/consts";
-import combinePrivyAccountWithChannelStats from "@/lib/privy/combineAccountsWithStats";
-import sortChannels from "@/lib/sortChannels";
-import getChannelStats from "@/lib/supabase/getChannelStats";
-import { ChannelStats } from "@/types/ChannelStats";
-import { NextRequest } from "next/server";
-
-export async function GET(req: NextRequest) {
- const applyChannelFilter = req.nextUrl.searchParams.get('apply_channel_filter');
- const onlyChannelIds = req.nextUrl.searchParams.get('only_channel_ids');
-
- try {
- const channelStats = await getChannelStats(!!applyChannelFilter);
- console.log('channelStats count', channelStats.length);
-
- const splitChannelStats = channelStats.reduce<
- [ChannelStats[], ChannelStats[]]
- >((previous, current) => {
- const inChannels = CHANNELS.some(channel => channel.value === current.channelId);
- if (inChannels) previous[0].push(current);
- else previous[1].push(current);
-
- return previous;
- }, [[], []]);
-
- const channelStatsWithAddresses =
- await combinePrivyAccountWithChannelStats(splitChannelStats[0]);
- console.log('channelStatsWithAddresses count', channelStatsWithAddresses.length);
-
- const allChannelStats = sortChannels([...channelStatsWithAddresses, ...splitChannelStats[1]]);
- const channels = onlyChannelIds ? allChannelStats.map(channel => channel.channelId) : allChannelStats;
-
- return Response.json({ message: 'success', channels });
- } catch (error) {
- console.error('Error:', error);
- return Response.json({ message: 'failed' }, { status: 400 });
- }
-}
diff --git a/app/api/claimAirdrop/route.ts b/app/api/claimAirdrop/route.ts
deleted file mode 100644
index af1d41ef..00000000
--- a/app/api/claimAirdrop/route.ts
+++ /dev/null
@@ -1,56 +0,0 @@
-import verifySignerUUID from '@/lib/neynar/verifySigner';
-import { stack } from '@/lib/stack/client';
-import { createClient } from '@supabase/supabase-js';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const getResponse = async (req: NextRequest): Promise => {
- const body = await req.json();
- const { wallet_address, signer_uuid } = body;
-
- const verify = await verifySignerUUID(signer_uuid);
-
- if (!verify.status) {
- return NextResponse.json(
- { message: `Invalid Signer UUID`, tipRemaining: 0, totalTipOnPost: 0 },
- { status: 400 },
- );
- }
-
- const airdropAmount = await callRedeemAirdrop(wallet_address);
-
- if (airdropAmount > 0) {
- await stack.track(`airdrop`, { account: wallet_address, points: airdropAmount });
- }
-
- return NextResponse.json(
- {
- message: `Airdropped ${airdropAmount} NOTES`,
- },
- { status: 200 },
- );
-};
-
-async function callRedeemAirdrop(walletAddressInput: string) {
- const { data, error } = await supabase.rpc('redeem_airdrop', {
- wallet_address_input: walletAddressInput,
- });
-
- if (error) {
- console.error('Error calling function:', error);
- return null;
- }
-
- console.log('Function returned:', data);
- return data;
-}
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/degenTip/route.ts b/app/api/degenTip/route.ts
deleted file mode 100644
index e48aa83c..00000000
--- a/app/api/degenTip/route.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import postDegenTipComment from '@/lib/neynar/postDegenTipComment';
-import verifySignerUUID from '@/lib/neynar/verifySigner';
-import { createClient } from '@supabase/supabase-js';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const getResponse = async (req: NextRequest): Promise => {
- const body = await req.json();
- const { signer_uuid, tipAmount, postHash } = body;
-
- const verify = await verifySignerUUID(signer_uuid);
-
- if (!verify.status) {
- return NextResponse.json(
- { message: 'Invalid Signer UUID', tipRemaining: 0, totalTipOnPost: 0 },
- { status: 400 },
- );
- }
-
- const totalDegenOnPost = await callAllocateDegenTip(tipAmount, postHash);
-
- await postDegenTipComment(signer_uuid, tipAmount, postHash);
-
- return NextResponse.json(
- {
- message: `Tipped ${tipAmount} DEGEN`,
- usedTip: tipAmount,
- totalTipOnPost: totalDegenOnPost,
- },
- { status: 200 },
- );
-};
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-async function callAllocateDegenTip(tipAmount: number, postHashInput: string) {
- const { data, error } = await supabase.rpc('degen_tip_post', {
- tip_amount: tipAmount,
- post_hash_input: postHashInput,
- });
-
- if (error) {
- console.error('Error calling function:', error);
- return null;
- }
-
- console.log('Function returned:', data);
- return data;
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/feed/route.ts b/app/api/feed/route.ts
deleted file mode 100644
index 08c6caf2..00000000
--- a/app/api/feed/route.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-import { FeedType } from '@/types/Feed';
-import { getFeed } from '@/lib/feed/getFeed';
-
-export async function GET(req: NextRequest) {
- try {
- const { searchParams } = new URL(req.url);
- const feedType = searchParams.get('feedType') || FeedType.Trending;
- const viewerFid = searchParams.get('viewerFid');
- const channelId = searchParams.get('channelId');
-
- const filteredPosts = await getFeed(channelId, feedType as FeedType, Number(viewerFid));
-
- return NextResponse.json({ posts: filteredPosts });
- } catch (error) {
- console.error(error);
- return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
- }
-}
diff --git a/app/api/frame/route.ts b/app/api/frame/route.ts
deleted file mode 100644
index a941ffa2..00000000
--- a/app/api/frame/route.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-import { VERCEL_URL } from '@/lib/consts';
-import getButtons from '@/lib/getButtons';
-import { FrameRequest, getFrameHtmlResponse } from '@coinbase/onchainkit';
-import { NextRequest, NextResponse } from 'next/server';
-import { getFarcasterConnectedAccount } from '@/lib/airstack/getFarcasterConnectedAccount';
-
-const getResponse = async (req: NextRequest): Promise => {
- let address;
- try {
- const body: FrameRequest = await req.json();
- const { untrustedData } = body;
- const { fid } = untrustedData;
- const response = await getFarcasterConnectedAccount(fid.toString());
- const firstSocial = response?.data?.Socials?.Social?.[0];
- const firstConnectedAccount = firstSocial?.connectedAddresses?.[0]?.address;
- const fallback = firstSocial?.userAddress;
- address = firstConnectedAccount || fallback;
- } catch (error) {
- console.error('Error parsing JSON from request', error);
- }
- const buttons = getButtons(address);
- const imageSrc = `${VERCEL_URL}/api/images/collector/collections?address=${address}`;
- const frame = {
- buttons,
- image: {
- src: imageSrc,
- },
- postUrl: `${VERCEL_URL}/api/frame`,
- };
-
- return new NextResponse(getFrameHtmlResponse(frame));
-};
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/getLeaderboardRank/route.ts b/app/api/getLeaderboardRank/route.ts
deleted file mode 100644
index a84e962c..00000000
--- a/app/api/getLeaderboardRank/route.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { stack } from '@/lib/stack/client';
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- const wallet_address = req.nextUrl.searchParams.get('wallet_address');
-
- if (!wallet_address) {
- return new Response(JSON.stringify({ error: 'wallet_address is required' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
- const leaderBoard = await stack.getLeaderboardRank(wallet_address);
-
- return new Response(JSON.stringify({ leaderBoard: leaderBoard }), {
- status: 200,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-}
diff --git a/app/api/getPoints/route.ts b/app/api/getPoints/route.ts
deleted file mode 100644
index 55abd331..00000000
--- a/app/api/getPoints/route.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { stack } from '@/lib/stack/client';
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- // Retrieve the wallet_address from the query parameters
- const wallet_address = req.nextUrl.searchParams.get('wallet_address');
-
- if (!wallet_address) {
- return new Response(JSON.stringify({ error: 'wallet_address is required' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
- const currentBalance = await stack.getPoints(wallet_address);
-
- return new Response(JSON.stringify({ notes: currentBalance }), {
- status: 200,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
-}
diff --git a/app/api/jobs/getMissingCasts/route.ts b/app/api/jobs/getMissingCasts/route.ts
deleted file mode 100644
index 2aca5df2..00000000
--- a/app/api/jobs/getMissingCasts/route.ts
+++ /dev/null
@@ -1,136 +0,0 @@
-import { CHANNELS } from '@/lib/consts';
-import createPostReply from '@/lib/neynar/createPostReply';
-import getChannelIdFromCast from '@/lib/neynar/getChannelIdFromCast';
-import getFeedFromTime from '@/lib/neynar/getFeedFromTime';
-import { Cast } from '@neynar/nodejs-sdk/build/neynar-api/v2';
-import { createClient } from '@supabase/supabase-js';
-import { isEmpty, isNil } from 'lodash';
-import { NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-const BOT_SIGNER_UUID = process.env.BOT_SIGNER_UUID as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
- auth: {
- autoRefreshToken: false,
- persistSession: false,
- detectSessionInUrl: false,
- },
-});
-
-const processEntriesInBatches = async (entries: any[], batchSize = 50) => {
- console.log('jobs::getMissingCasts', `${entries.length} new entries being added`);
- for (let i = 0; i < entries.length; i += batchSize) {
- const batch = entries.slice(i, i + batchSize);
- await Promise.all(batch.map((entry: any) => processSingleEntry(entry)));
- }
-};
-
-const processSingleEntry = async (cast: Cast) => {
- const address = cast?.author?.verifications ? cast?.author?.verifications : undefined;
-
- if (!isEmpty(address)) {
- await createCast(cast);
- }
-};
-
-const getResponse = async (): Promise => {
- 'use server';
-
- const dateFourHoursAgo = new Date();
- dateFourHoursAgo.setHours(dateFourHoursAgo.getHours() - 6);
-
- const lastChecked = dateFourHoursAgo;
-
- console.log('jobs::getMissingCasts', `Starting Job from ${lastChecked}`);
-
- const formattedLastChecked = new Date(`${lastChecked}`);
-
- const [spotify, soundCloud, soundxyz, youtube] = await Promise.all([
- getFeedFromTime('spotify.com/track', formattedLastChecked),
- getFeedFromTime('soundcloud.com', formattedLastChecked),
- getFeedFromTime('sound.xyz', formattedLastChecked),
- getFeedFromTime('youtube.com/watch', formattedLastChecked),
- ]);
- const allEntries: any[] = [];
- allEntries.push(...spotify, ...soundCloud, ...soundxyz);
-
- const youtubeFiltered = youtube.filter((entry) => {
- const channelId = getChannelIdFromCast(entry);
- return channelId && CHANNELS.find((channel) => channel.value === channelId);
- });
-
- console.log('jobs::getNewCasts', 'ytEntries', youtubeFiltered);
- allEntries.push(...youtubeFiltered);
-
- console.log('jobs::getMissingCasts', `${allEntries.length} new entries`);
-
- if (allEntries.length > 0) {
- await processEntriesInBatches(allEntries);
- }
-
- return NextResponse.json({ message: 'success', allEntries }, { status: 200 });
-};
-
-async function createCast(cast: Cast) {
- const likes = (cast as any).reactions.likes_count;
- const parentUrl = cast.parent_url;
- let channelId = null;
- if (parentUrl) {
- const match = /\/channel\/([^/]+)$/.exec(parentUrl);
- if (match) {
- channelId = match[1];
- }
- }
-
- const { data: existingPosts } = await supabase
- .from('posts')
- .select()
- .eq('post_hash', cast.hash)
- .single();
-
- if (isNil(existingPosts) && isEmpty(existingPosts)) {
- const { data, error } = await supabase.from('posts').insert({
- post_hash: cast.hash,
- likes,
- created_at: new Date(cast.timestamp),
- embeds: cast.embeds,
- author: cast.author,
- channelId,
- });
-
- console.log('jobs::getMissingCasts', `Successfully created/updated ${cast.hash}`);
-
- console.log(data);
- if (error) {
- console.error('Error calling function:', error);
- return null;
- } else {
- sendBotCast(cast);
- }
- }
-
- return { success: true };
-}
-
-async function sendBotCast(cast: Cast) {
- await createPostReply(
- BOT_SIGNER_UUID,
- cast.hash,
- `This song is now available on @sonatatips where you earn NOTES when people tip you.\n\nSee you over there!\n\nhttps://sonata.tips/cast/${cast.author.username}/${cast.hash.substring(0, 8)}`,
- );
-
- return { success: true };
-}
-
-export async function GET(): Promise {
- const response = await getResponse().catch((error) => {
- console.error('Error in background task:', error);
- });
- return response as NextResponse;
-}
-
-export const dynamic = 'force-dynamic';
-export const fetchCache = 'force-no-store';
-export const revalidate = 0;
diff --git a/app/api/jobs/getNewCasts/route.ts b/app/api/jobs/getNewCasts/route.ts
deleted file mode 100644
index c561c350..00000000
--- a/app/api/jobs/getNewCasts/route.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-import filterCastsByChannels from '@/lib/filterCastsByChannels';
-import getPlatformFeedFromTime from '@/lib/neynar/getPlatformFeedFromTime';
-import sendBotCast from '@/lib/sonata/sendBotCast';
-import getSpotifyWithAlternatives from '@/lib/spotify/getSpotifyWithAlternatives';
-import upsertCast from '@/lib/supabase/upsertCast';
-import filterZoraFeed from '@/lib/zora/filterCasts';
-import { Cast } from '@neynar/nodejs-sdk/build/neynar-api/v2';
-import { createClient } from '@supabase/supabase-js';
-import { isEmpty } from 'lodash';
-import { NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
- auth: {
- autoRefreshToken: false,
- persistSession: false,
- detectSessionInUrl: false,
- },
-});
-
-const processEntriesInBatches = async (entries: any[], batchSize = 50) => {
- console.log('jobs::getNewCasts', `${entries.length} new entries being added`);
- for (let i = 0; i < entries.length; i += batchSize) {
- const batch = entries.slice(i, i + batchSize);
- await Promise.all(batch.map((entry: any) => processSingleEntry(entry)));
- }
-};
-
-const processSingleEntry = async (cast: Cast) => {
- const address = cast?.author?.verifications ? cast?.author?.verifications : undefined;
-
- if (!isEmpty(address)) {
- const data = await upsertCast(cast);
- if (data.statusText == 'Created') await sendBotCast(cast);
- }
-};
-
-const getResponse = async (): Promise => {
- 'use server';
- const { data: cast_query_date } = await supabase
- .from('cast_query_date')
- .select('lastcheck')
- .eq('id', 1)
- .single();
- console.log('jobs::getNewCasts', `Starting Job from ${cast_query_date?.lastcheck}`);
-
- const twoMinutesAgo = new Date(new Date().getTime() - 2 * 60 * 1000).toISOString();
-
- const lastChecked = cast_query_date ? cast_query_date.lastcheck : twoMinutesAgo;
-
- const formattedLastChecked = new Date(`${lastChecked}`);
-
- const feeds = await getPlatformFeedFromTime(formattedLastChecked);
-
- const spotifyWithAlternatives = await getSpotifyWithAlternatives(feeds.spotify);
- console.log('jobs::getNewCasts', 'spotifyEntries', spotifyWithAlternatives);
-
- const youtubeFiltered = filterCastsByChannels(feeds.youtube);
- console.log('jobs::getNewCasts', 'ytEntries', youtubeFiltered);
-
- const zoraFiltered = await filterZoraFeed(feeds.zora);
- console.log('jobs::getNewCasts', 'zoraEntriesCount', zoraFiltered.length);
-
- const allEntries: Cast[] = [
- ...feeds.soundcloud,
- ...feeds.soundxyz,
- ...spotifyWithAlternatives,
- ...youtubeFiltered,
- ...zoraFiltered,
- ];
-
- console.log('jobs::getNewCasts', `${allEntries.length} new entries`);
- if (allEntries.length > 0) {
- await processEntriesInBatches(allEntries);
- }
-
- let newLastChecked: string = allEntries.reduce((max, cast) => {
- const current = new Date(cast.timestamp as string);
- return current > new Date(max) ? cast.timestamp : max;
- }, lastChecked);
-
- console.log('jobs::getNewCasts', `About to set cast_query_date to ${newLastChecked}`);
-
- if (isEmpty(newLastChecked)) {
- newLastChecked = twoMinutesAgo;
- }
-
- const { data, error } = await supabase
- .from('cast_query_date')
- .upsert({ id: 1, last_checked: newLastChecked, lastcheck: newLastChecked });
-
- console.log(data, error);
- return NextResponse.json({ message: 'success', allEntries }, { status: 200 });
-};
-
-export async function GET(): Promise {
- const response = await getResponse().catch((error) => {
- console.error('Error in background task:', error);
- });
- return response as NextResponse;
-}
-
-export const dynamic = 'force-dynamic';
-export const fetchCache = 'force-no-store';
-export const revalidate = 0;
diff --git a/app/api/jobs/reward_trending_posts/route.ts b/app/api/jobs/reward_trending_posts/route.ts
deleted file mode 100644
index 6d1ea6e6..00000000
--- a/app/api/jobs/reward_trending_posts/route.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { stack } from '@/lib/stack/client';
-import { createClient } from '@supabase/supabase-js';
-import { isNil } from 'lodash';
-import { NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-const TIP_AWARD_PER = 1000;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const getResponse = async (): Promise => {
- const topPosts = await fetchTopPosts();
-
- if (isNil(topPosts)) return NextResponse.json({ message: 'No top posts' }, { status: 400 });
-
- for (let i = 0; i < topPosts.length; i++) {
- const post = topPosts[i];
- const verifications = post.verifications;
- if (!isNil(verifications) && verifications.length > 0) {
- const authorWallet = verifications[0];
- stack.track(`trending_reward_${authorWallet}`, {
- account: authorWallet,
- points: TIP_AWARD_PER,
- });
- }
- }
-
- return NextResponse.json({ message: 'success' }, { status: 200 });
-};
-
-async function fetchTopPosts() {
- const { data, error } = await supabase
- .from('trending_posts')
- .select('*')
- .order('score', { ascending: false })
- .limit(100);
-
- if (error) {
- console.error('Error fetching posts:', error);
- return null;
- }
-
- return data;
-}
-
-export async function GET(): Promise {
- await getResponse().catch((error) => {
- console.error('Error in background task:', error);
- });
- return NextResponse.json({ message: 'success' }, { status: 200 });
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/jobs/updateHypersub/route.ts b/app/api/jobs/updateHypersub/route.ts
deleted file mode 100644
index 9d79ba7b..00000000
--- a/app/api/jobs/updateHypersub/route.ts
+++ /dev/null
@@ -1,110 +0,0 @@
-import { createClient } from '@supabase/supabase-js';
-import { NextResponse } from 'next/server';
-import { base } from 'viem/chains';
-import HyperSub from '@/abis/hypersub.json';
-import { createPublicClient, http } from 'viem';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-const NEYNAR_API_KEY = process.env.NEYNAR_API_KEY as string;
-const BASEAlchemyKey = process.env.BASE_ALCHEMY_KEY as string;
-const hypersubContractAddress = process.env.HYPERSUB_CONTRACT_ADDRESS as `0x${string}`;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
- auth: {
- autoRefreshToken: false,
- persistSession: false,
- detectSessionInUrl: false,
- },
-});
-
-const fetchUserData = async (fids: string[]): Promise => {
- const options = {
- method: 'GET',
- headers: { accept: 'application/json', api_key: NEYNAR_API_KEY },
- } as any;
- const queryParams = new URLSearchParams({ fids: fids.join(',') });
-
- const response = await fetch(
- `https://api.neynar.com/v2/farcaster/user/bulk?${queryParams}`,
- options,
- );
- return response.json();
-};
-
-const checkBalances = async (verifications: string[]): Promise => {
- const BASEpublicServerClient = createPublicClient({
- chain: base,
- transport: http(`https://base-mainnet.g.alchemy.com/v2/${BASEAlchemyKey}`),
- });
-
- for (const address of verifications) {
- const balance = (await BASEpublicServerClient.readContract({
- address: hypersubContractAddress,
- abi: HyperSub,
- functionName: 'balanceOf',
- args: [address],
- })) as number;
-
- if (balance > 0) {
- return true;
- }
- }
-
- return false;
-};
-
-const updateSupabaseEntry = async (fid: string, hasBalance: boolean): Promise => {
- const now = new Date().toISOString();
- const updateData = hasBalance
- ? { hypersub_subscribed_since: now }
- : { hypersub_subscribed_since: null };
-
- await supabase
- .from('tips')
- .update(updateData)
- .eq('fid', fid)
- .is('hypersub_subscribed_since', null);
-};
-
-const getResponse = async (): Promise => {
- 'use server';
- const { data: fids, error } = await supabase.from('tips').select('fid');
- if (error) {
- return NextResponse.json({ message: 'Error retrieving fids', error }, { status: 500 });
- }
-
- const fidChunks = [];
- const chunkSize = 10; // Reduce chunk size for more parallel batches
- for (let i = 0; i < fids.length; i += chunkSize) {
- fidChunks.push(fids.slice(i, i + chunkSize));
- }
-
- const processChunks = fidChunks.map(async (chunk) => {
- const { users } = await fetchUserData(chunk.map((fid: any) => fid.fid));
- const results = await Promise.all(
- users.map(async (user: any) => {
- const hasBalance = await checkBalances(user.verifications);
- await updateSupabaseEntry(user.fid, hasBalance);
- return { fid: user.fid, hasBalance }; // Return result if needed
- }),
- );
- return results;
- });
-
- await Promise.all(processChunks);
-
- return NextResponse.json({ message: 'success' }, { status: 200 });
-};
-
-export async function GET(): Promise {
- const response = await getResponse().catch((error) => {
- console.error('Error in background task:', error);
- return NextResponse.json({ message: 'Error in background task', error }, { status: 500 });
- });
- return response as NextResponse;
-}
-
-export const dynamic = 'force-dynamic';
-export const fetchCache = 'force-no-store';
-export const revalidate = 0;
diff --git a/app/api/jobs/update_tips/route.ts b/app/api/jobs/update_tips/route.ts
deleted file mode 100644
index f5dc67d2..00000000
--- a/app/api/jobs/update_tips/route.ts
+++ /dev/null
@@ -1,121 +0,0 @@
-import filterCastsByChannels from '@/lib/filterCastsByChannels';
-import getTipAllocationFeedFromTime from '@/lib/neynar/getTipAllocationFeedFromTime';
-import { createClient } from '@supabase/supabase-js';
-import { isEmpty } from 'lodash';
-import { NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const processEntriesInBatches = async (entries: any[], batchSize = 50) => {
- for (let i = 0; i < entries.length; i += batchSize) {
- const batch = entries.slice(i, i + batchSize);
- await Promise.all(batch.map((entry: any) => processSingleEntry(entry)));
- }
-};
-
-const processSingleEntry = async (cast: {
- reactions: { likes: string | any[] };
- author: {
- power_badge: any;
- verifications: any;
- fid: any;
- };
- timestamp: any;
-}) => {
- const likes = cast.reactions?.likes?.length ?? 0;
- const address = cast?.author?.verifications ? cast?.author?.verifications[0] : undefined;
- const powerBadge = cast?.author?.power_badge ?? false;
- const timestamp = cast?.timestamp;
- const fid = cast?.author?.fid;
-
- if (!isEmpty(address)) {
- await callUpdateTips(address, fid, likes, 1, timestamp, powerBadge);
- }
-};
-
-const getResponse = async (): Promise => {
- 'use server';
- const { data: tip_query_date } = await supabase
- .from('tip_query_date')
- .select('last_checked')
- .eq('id', 1)
- .single();
- console.log('jobs::update_tips', `Starting Job from ${tip_query_date?.last_checked}`);
-
- const lastChecked = tip_query_date ? tip_query_date.last_checked : '';
- const formattedLastChecked = new Date(`${lastChecked}`);
-
- const [spotify, soundCloud, soundxyz, youtube] = await Promise.all([
- getTipAllocationFeedFromTime('spotify.com/track', formattedLastChecked),
- getTipAllocationFeedFromTime('soundcloud.com', formattedLastChecked),
- getTipAllocationFeedFromTime('sound.xyz', formattedLastChecked),
- getTipAllocationFeedFromTime('youtube.com/watch', formattedLastChecked),
- ]);
-
- const allEntries: any[] = [];
-
- allEntries.push(...spotify, ...soundCloud, ...soundxyz);
-
- const youtubeFiltered = filterCastsByChannels(youtube);
- console.log('jobs::update_tips', 'ytEntries', youtubeFiltered);
- allEntries.push(...youtubeFiltered);
-
- console.log('jobs::update_tips', `New entries:`, allEntries.length);
-
- await processEntriesInBatches(allEntries);
-
- const newLastChecked: string = allEntries.reduce((max, cast) => {
- const current = new Date(cast.timestamp as string);
- return current > new Date(max) ? cast.timestamp : max;
- }, lastChecked);
-
- console.log('jobs::update_tips', `About to call update_daily_tip_allocation`);
- await supabase.rpc('update_daily_tip_allocation');
-
- console.log('jobs::update_tips', `About to set last_checked to ${newLastChecked}`);
- await supabase.from('tip_query_date').upsert({ id: 1, last_checked: newLastChecked });
-
- return NextResponse.json({ message: 'success' }, { status: 200 });
-};
-
-async function callUpdateTips(
- walletAddress: string,
- fid: string,
- totalLikes: number,
- numPosts: number,
- firstPostDate: string,
- power_badge: boolean,
-) {
- const firstPostDateISO = new Date(firstPostDate).toISOString();
-
- const { data, error } = await supabase.rpc('update_tips', {
- p_wallet_address: walletAddress,
- p_fid: fid,
- p_total_likes: totalLikes,
- p_num_posts: numPosts,
- p_first_post_date: firstPostDateISO,
- p_power_badge: power_badge,
- });
-
- if (error) {
- console.error('Error calling function:', error);
- return null;
- }
-
- console.log('Function called successfully, result:', data);
- return data;
-}
-
-export async function GET(): Promise {
- await getResponse().catch((error) => {
- console.error('Error in background task:', error);
- });
- return NextResponse.json({ message: 'success' }, { status: 200 });
-}
-
-export const dynamic = 'force-dynamic';
-export const fetchCache = 'force-no-store';
-export const revalidate = 0;
diff --git a/app/api/neynar/createReaction/route.ts b/app/api/neynar/createReaction/route.ts
deleted file mode 100644
index 59b0cba5..00000000
--- a/app/api/neynar/createReaction/route.ts
+++ /dev/null
@@ -1,62 +0,0 @@
-import getCastLikesCount from '@/lib/neynar/getCastLikesCount';
-import hasUserLikedCast from '@/lib/neynar/hasUserLiked';
-import verifySignerUUID from '@/lib/neynar/verifySigner';
-import { createClient } from '@supabase/supabase-js';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const getResponse = async (req: NextRequest): Promise => {
- const body = await req.json();
- const { signer_uuid, reaction_type, target } = body;
-
- const options = {
- method: 'POST',
- headers: {
- accept: 'application/json',
- api_key: process.env.NEYNAR_API_KEY,
- 'content-type': 'application/json',
- },
- body: JSON.stringify({
- signer_uuid,
- reaction_type,
- target,
- }),
- } as any;
-
- try {
- const { fid } = await verifySignerUUID(signer_uuid);
-
- let likes_count = await getCastLikesCount(target);
- const isFidIncluded = await hasUserLikedCast(target, fid);
-
- if (!isFidIncluded) {
- await fetch(`https://api.neynar.com/v2/farcaster/reaction?`, options);
-
- likes_count++;
- await supabase.from('posts').upsert(
- {
- post_hash: target,
- likes: likes_count,
- },
- {
- onConflict: 'post_hash',
- },
- );
- }
-
- return NextResponse.json({ success: true, likes: likes_count }, { status: 200 });
- } catch (error) {
- console.error(error);
- return NextResponse.json({ errors: 'Something went wrong' }, { status: 400 });
- }
-};
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/neynar/getFeed/route.ts b/app/api/neynar/getFeed/route.ts
deleted file mode 100644
index 58404abd..00000000
--- a/app/api/neynar/getFeed/route.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { isEmpty } from 'lodash';
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- const embedUrl = req.nextUrl.searchParams.get('embedUrl') as string;
-
- if (isEmpty(embedUrl)) {
- return new Response(JSON.stringify({ error: 'embedUrl required' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
-
- const options = {
- method: 'GET',
- headers: { accept: 'application/json', api_key: process.env.NEYNAR_API_KEY },
- next: {
- revalidate: 60,
- },
- } as any;
-
- try {
- const queryParams = new URLSearchParams({
- feed_type: 'filter',
- filter_type: 'embed_url',
- embed_url: embedUrl,
- with_recasts: 'false',
- limit: '100',
- });
-
- const response = await fetch(
- `https://api.neynar.com/v2/farcaster/feed?${queryParams}`,
- options,
- );
- const data = await response.json();
- return new Response(JSON.stringify(data), {
- status: 200,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- } catch (error) {
- console.error(error);
- return new Response(JSON.stringify({ error: 'getUser Failed' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
-}
diff --git a/app/api/neynar/getFollowers/route.ts b/app/api/neynar/getFollowers/route.ts
deleted file mode 100644
index ee8b37dc..00000000
--- a/app/api/neynar/getFollowers/route.ts
+++ /dev/null
@@ -1,27 +0,0 @@
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- const fid = req.nextUrl.searchParams.get('fid');
- if (!fid) return Response.json({ message: 'fid required' }, { status: 400 })
-
- const options = {
- method: 'GET',
- headers: { accept: 'application/json', api_key: process.env.NEYNAR_API_KEY },
- next: {
- revalidate: 60,
- },
- } as any;
-
- try {
- const queryParams = new URLSearchParams({ fid, limit: '3' });
-
- const response = await fetch(`https://api.neynar.com/v2/farcaster/followers?${queryParams}`, options);
- const data = await response.json();
- const users = data?.users?.map((follower: any) => follower.user) ?? [];
-
- return Response.json({ users });
- } catch (error) {
- console.error(error);
- return Response.json({ message: 'getFollwers failed' }, { status: 500 });
- }
-}
diff --git a/app/api/neynar/getFollowing/route.ts b/app/api/neynar/getFollowing/route.ts
deleted file mode 100644
index f1f3ac99..00000000
--- a/app/api/neynar/getFollowing/route.ts
+++ /dev/null
@@ -1,53 +0,0 @@
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- const { searchParams } = new URL(req.url);
- const fid = searchParams.get('fid') as string;
- let first = true,
- cursor = null;
-
- const options = {
- method: 'GET',
- headers: { accept: 'application/json', api_key: process.env.NEYNAR_API_KEY },
- next: {
- revalidate: 60,
- },
- } as any;
-
- try {
- const params: any = { fid, limit: '100' };
- const users = [];
- while (first || cursor) {
- first = false;
- if (cursor) {
- params.cursor = cursor;
- }
- const queryParams = new URLSearchParams(params);
-
- const response = await fetch(
- `https://api.neynar.com/v2/farcaster/following?${queryParams}`,
- options,
- );
- const data = await response.json();
- if (data.users) {
- users.push(...data.users.map((user: any) => user.user));
- }
- cursor = data.next.cursor;
- }
-
- return new Response(JSON.stringify({ users }), {
- status: 200,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- } catch (error) {
- console.error(error);
- return new Response(JSON.stringify({ error: 'getFollowing Failed' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
-}
diff --git a/app/api/neynar/getProfile/route.ts b/app/api/neynar/getProfile/route.ts
deleted file mode 100644
index de5d0d98..00000000
--- a/app/api/neynar/getProfile/route.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import getUserByUsername from '@/lib/neynar/getNeynarUserByUsername';
-import { NextRequest, NextResponse } from 'next/server';
-
-const getResponse = async (req: NextRequest): Promise => {
- const url = req.nextUrl;
- const username = url.searchParams.get('username');
-
- if (!username) return NextResponse.json({ message: 'invalid param.' }, { status: 500 });
-
- try {
- const profile = await getUserByUsername(username);
- return NextResponse.json(profile, { status: 200 });
- } catch (error: any) {
- return NextResponse.json({ errors: 'Something went wrong' }, { status: 400 });
- }
-};
-
-export async function GET(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/neynar/getUser/route.ts b/app/api/neynar/getUser/route.ts
deleted file mode 100644
index c4540e42..00000000
--- a/app/api/neynar/getUser/route.ts
+++ /dev/null
@@ -1,46 +0,0 @@
-import { isEmpty } from 'lodash';
-import { NextRequest } from 'next/server';
-
-export async function GET(req: NextRequest): Promise {
- const fids = req.nextUrl.searchParams.get('fids') as string;
-
- if (isEmpty(fids)) {
- return new Response(JSON.stringify({ error: 'fids required' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
-
- const options = {
- method: 'GET',
- headers: { accept: 'application/json', api_key: process.env.NEYNAR_API_KEY },
- } as any;
-
- try {
- const queryParams = new URLSearchParams({
- fids,
- });
-
- const response = await fetch(
- `https://api.neynar.com/v2/farcaster/user/bulk?${queryParams}`,
- options,
- );
- const { users } = await response.json();
- return new Response(JSON.stringify({ users }), {
- status: 200,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- } catch (error) {
- console.error(error);
- return new Response(JSON.stringify({ error: 'getUser Failed' }), {
- status: 400,
- headers: {
- 'Content-Type': 'application/json',
- },
- });
- }
-}
diff --git a/app/api/og-image/cast/[username]/[hash]/[[...rank]]/route.tsx b/app/api/og-image/cast/[username]/[hash]/[[...rank]]/route.tsx
deleted file mode 100644
index 4d0c152c..00000000
--- a/app/api/og-image/cast/[username]/[hash]/[[...rank]]/route.tsx
+++ /dev/null
@@ -1,54 +0,0 @@
-import CastImageLayout from '@/components/Og/CastImageLayout';
-import { getDataForCastOg } from '@/lib/getDataForCastOg';
-import { ImageResponse } from '@vercel/og';
-import { NextRequest } from 'next/server';
-
-export const runtime = 'edge';
-
-export async function GET(
- req: NextRequest,
- { params }: { params: { username: string; hash: string; rank: string } },
-) {
- const { username, hash, rank } = params;
-
- const {
- encodedUsername,
- profilePfp,
- metadata,
- channelLabel,
- channelIcon,
- points,
- soraSemiBold,
- soraNormal,
- }: any = await getDataForCastOg(username, hash);
-
- return new ImageResponse(
- (
-
- ),
- {
- width: 1200,
- height: 630,
- fonts: [
- {
- name: 'Sora',
- data: await soraNormal,
- weight: 400,
- },
- {
- name: 'Sora',
- data: await soraSemiBold,
- weight: 600,
- },
- ],
- },
- );
-}
diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx
deleted file mode 100644
index 753e3f7c..00000000
--- a/app/api/og/route.tsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import Title from '@/components/Header/Title';
-
-export const runtime = 'edge';
-
-export async function GET() {
- const { ImageResponse } = await import('@vercel/og');
- return new ImageResponse(
- (
-
-
-
- ),
- );
-}
diff --git a/app/api/postMusicEmbed/route.ts b/app/api/postMusicEmbed/route.ts
deleted file mode 100644
index 1b4c57e1..00000000
--- a/app/api/postMusicEmbed/route.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import postMusicEmbed from '@/lib/neynar/postMusicEmbed';
-import sendBotCast from '@/lib/sonata/sendBotCast';
-import upsertCast from '@/lib/supabase/upsertCast';
-import { NextRequest } from 'next/server';
-
-const getResponse = async (req: NextRequest) => {
- const body = await req.json();
- const { signer_uuid, url } = body;
-
- const data = await postMusicEmbed(signer_uuid, url);
-
- if (!data?.success || !data?.cast?.hash) {
- return Response.json({ message: 'casting failed' }, { status: 400 });
- }
- console.log('new cast:', data);
-
- const cast: any = {
- timestamp: new Date().toISOString(),
- parent_url: '',
- root_parent_url: '',
- reactions: {
- likes_count: 0,
- },
- hash: data.cast.hash,
- embeds: [{ url }],
- author: data.cast.author,
- };
- const { success } = await upsertCast(cast);
-
- if (!success) return Response.json(
- { message: 'successfuly casted', data },
- { status: 200 }
- );
- sendBotCast(cast);
-
- return Response.json({
- message: 'successfuly casted and indexed',
- link: `/cast/${cast.author.username}/${cast.hash.substring(0, 8)}`
- }, { status: 307 });
-};
-
-export async function POST(req: NextRequest) {
- return getResponse(req);
-}
\ No newline at end of file
diff --git a/app/api/profile/route.ts b/app/api/profile/route.ts
new file mode 100644
index 00000000..458fa432
--- /dev/null
+++ b/app/api/profile/route.ts
@@ -0,0 +1,33 @@
+import { EVENT_ZORA_PROFILE } from '@/lib/consts';
+import getConnectedZoraProfile from '@/lib/getConnectedZoraProfile';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import verifySubscription from '@/lib/verifySubscription';
+import getZoraProfile from '@/lib/zora/getZoraProfile';
+import { NextRequest } from 'next/server';
+import { Address, isAddress, zeroAddress } from 'viem';
+
+export async function GET(req: NextRequest) {
+ try {
+ await trackEndpoint(EVENT_ZORA_PROFILE);
+
+ const address = req.nextUrl.searchParams.get('address');
+ if (!address) throw Error('address is required');
+
+ const zoraProfile = await getZoraProfile(address);
+ const isPro = await verifySubscription(isAddress(address) ? (address as Address) : zeroAddress);
+ const connectedZoraProfile = await getConnectedZoraProfile(zoraProfile.address);
+ return Response.json({
+ message: 'success',
+ zoraProfile,
+ isPro,
+ connectedZoraProfile,
+ });
+ } catch (e) {
+ const message = (e as { message?: string })?.message ?? 'failed';
+ return Response.json({ message }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const fetchCache = 'force-no-store';
+export const revalidate = 0;
diff --git a/app/api/songLink/fetchLink/route.ts b/app/api/songLink/fetchLink/route.ts
deleted file mode 100644
index 8ea3aff7..00000000
--- a/app/api/songLink/fetchLink/route.ts
+++ /dev/null
@@ -1,41 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-const getResponse = async (req: NextRequest): Promise => {
- const url = req.nextUrl;
- const trackUrl = url.searchParams.get('trackUrl');
- if (!trackUrl) return NextResponse.json({ errors: 'Missing trackUrl' }, { status: 422 });
- const endpoint = `https://api.song.link/v1-alpha.1/links?url=${trackUrl}&userCountry=US&songIfSingle=true`;
- const headers = {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- } as any;
-
- try {
- const response = await fetch(endpoint, {
- method: 'GET',
- headers: headers,
- });
-
- const data = await response.json();
-
- if (response.ok) {
- if (data.errors) {
- console.error('SongLink Errors:', data.errors);
- return NextResponse.json(data, { status: 400 });
- }
- return NextResponse.json(data, { status: 200 });
- } else {
- console.error(response.status, response.statusText);
- return NextResponse.json(data, { status: 400 });
- }
- } catch (error: any) {
- console.error(error?.message);
- return NextResponse.json({ errors: 'Something went wrong' }, { status: 400 });
- }
-};
-
-export async function GET(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/sound/fetchSoundGraphQL/route.ts b/app/api/sound/fetchSoundGraphQL/route.ts
deleted file mode 100644
index 1ef23a4b..00000000
--- a/app/api/sound/fetchSoundGraphQL/route.ts
+++ /dev/null
@@ -1,47 +0,0 @@
-import { NextRequest, NextResponse } from 'next/server';
-
-const getResponse = async (req: NextRequest): Promise => {
- const body = await req.json();
- const { query, variables } = body;
-
- const endpoint = 'https://api.sound.xyz/graphql';
- const headers = {
- 'Content-Type': 'application/json',
- Accept: 'application/json',
- 'X-Sound-Client-Key': process.env.SOUND_API_KEY,
- } as any;
-
- try {
- const response = await fetch(endpoint, {
- method: 'POST',
- headers: headers,
- body: JSON.stringify({
- query: query,
- variables: variables,
- }),
- });
-
- const data = await response.json();
-
- if (response.ok) {
- if (data.errors) {
- console.error('GraphQL Errors:', data.errors);
- return NextResponse.json(data, { status: 400 });
- }
-
- return NextResponse.json(data, { status: 200 });
- } else {
- console.error(response.status, response.statusText);
- return NextResponse.json(data, { status: 400 });
- }
- } catch (error: any) {
- console.error(error?.message);
- return NextResponse.json({ errors: 'Something went wrong' }, { status: 400 });
- }
-};
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/stack/leaderboard/getLeaderboardData.tsx b/app/api/stack/leaderboard/getLeaderboardData.tsx
new file mode 100644
index 00000000..e22e6466
--- /dev/null
+++ b/app/api/stack/leaderboard/getLeaderboardData.tsx
@@ -0,0 +1,30 @@
+import getAllEndpointWallets from '@/lib/privy/getAllEndpointWallets';
+import { stack } from '@/lib/stack/client';
+
+const getLeaderboardData = async () => {
+ const stackData = await stack.getLeaderboard();
+ const wallets = await getAllEndpointWallets();
+
+ const walletMap = wallets.reduce((map, wallet) => {
+ wallet.linked_accounts.forEach((account: any) => {
+ if (account.type === 'wallet') {
+ map[account.address.toLowerCase()] = wallet;
+ }
+ });
+ return map;
+ }, {});
+
+ const mergedLeaderboard = stackData.leaderboard.map((entry) => {
+ const walletDetails = walletMap[entry.address.toLowerCase()] || {};
+ return { ...entry, walletDetails };
+ });
+
+ return {
+ leaderboard: mergedLeaderboard,
+ metadata: stackData.metadata,
+ stats: stackData.stats,
+ pointSystem: (stackData as any)?.pointSystem,
+ };
+};
+
+export default getLeaderboardData;
diff --git a/app/api/stack/leaderboard/route.ts b/app/api/stack/leaderboard/route.ts
new file mode 100644
index 00000000..dea223e8
--- /dev/null
+++ b/app/api/stack/leaderboard/route.ts
@@ -0,0 +1,33 @@
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import getLeaderboardData from './getLeaderboardData';
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
+
+export async function GET() {
+ try {
+ await trackEndpoint('stack+leaderboard');
+ const data = await getLeaderboardData();
+ return new Response(
+ JSON.stringify({
+ message: 'success',
+ data,
+ }),
+ {
+ status: 200,
+ headers: {
+ 'Cache-Control': 'no-store, max-age=0, must-revalidate',
+ },
+ },
+ );
+ } catch (error) {
+ console.error('Error:', error);
+ return new Response(JSON.stringify({ message: 'failed' }), {
+ status: 400,
+ headers: {
+ 'Cache-Control': 'no-store, max-age=0, must-revalidate',
+ },
+ });
+ }
+}
diff --git a/app/api/tip/route.ts b/app/api/tip/route.ts
deleted file mode 100644
index 63b85a27..00000000
--- a/app/api/tip/route.ts
+++ /dev/null
@@ -1,103 +0,0 @@
-import getUser from '@/lib/neynar/getNeynarUser';
-import verifySignerUUID from '@/lib/neynar/verifySigner';
-import { stack } from '@/lib/stack/client';
-import { createClient } from '@supabase/supabase-js';
-import { isEmpty, isNil } from 'lodash';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY);
-
-const getResponse = async (req: NextRequest): Promise => {
- const body = await req.json();
- const { signer_uuid, tipAmount, postHash, recipientFid } = body;
-
- const { status, fid: tipperFid } = await verifySignerUUID(signer_uuid);
-
- if (!status) {
- return NextResponse.json(
- { message: `Invalid Signer UUID`, tipRemaining: 0, totalTipOnPost: 0 },
- { status: 400 },
- );
- }
-
- if (tipperFid === recipientFid) {
- return NextResponse.json(
- { message: `Can not tip yourself`, tipRemaining: 0, totalTipOnPost: 0 },
- { status: 400 },
- );
- }
-
- const { remaining_tip_allocation, total_tip_on_post, used_tip } = await callAllocateTip(
- `${tipperFid}`,
- tipAmount,
- postHash,
- `${recipientFid}`,
- );
-
- if (used_tip === 0) {
- return NextResponse.json(
- {
- message: `Already reached max NOTES tips`,
- usedTip: used_tip,
- tipRemaining: remaining_tip_allocation,
- totalTipOnPost: total_tip_on_post,
- },
- { status: 200 },
- );
- }
-
- const recipientUser = await getUser(recipientFid);
- const recipientWalletAddress = !isEmpty(recipientUser.verifications)
- ? recipientUser.verifications[0]
- : undefined;
-
- if (isNil(recipientWalletAddress)) {
- return NextResponse.json(
- { message: `Invalid recipient`, tipRemaining: 0, totalTipOnPost: 0 },
- { status: 400 },
- );
- }
-
- stack.track(`tip_from_${tipperFid}`, { account: recipientWalletAddress, points: used_tip });
-
- return NextResponse.json(
- {
- message: `Tipped ${used_tip} NOTES`,
- usedTip: used_tip,
- tipRemaining: remaining_tip_allocation,
- totalTipOnPost: total_tip_on_post,
- },
- { status: 200 },
- );
-};
-
-export async function POST(req: NextRequest): Promise {
- return getResponse(req);
-}
-
-async function callAllocateTip(
- fidInput: string,
- tipAmount: number,
- postHashInput: string,
- recipientFid: string,
-) {
- const { data, error } = await supabase.rpc('allocate_tip_with_fid_recipient', {
- fid_input: fidInput,
- tip_amount: tipAmount,
- post_hash_input: postHashInput,
- receiver_fid: recipientFid,
- });
-
- if (error) {
- console.error('Error calling function:', error);
- return null;
- }
-
- console.log('Function returned:', data);
- return data;
-}
-
-export const dynamic = 'force-dynamic';
diff --git a/app/api/tips/route.ts b/app/api/tips/route.ts
deleted file mode 100644
index e96b0b84..00000000
--- a/app/api/tips/route.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-import { getLatestTips } from '@/lib/getLatestTips';
-
-export async function GET(): Promise {
- return getLatestTips();
-}
diff --git a/app/api/user/getSongs/route.tsx b/app/api/user/getSongs/route.tsx
deleted file mode 100644
index fbc316f4..00000000
--- a/app/api/user/getSongs/route.tsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import { createClient } from '@supabase/supabase-js';
-import { NextRequest, NextResponse } from 'next/server';
-
-const SUPABASE_URL = process.env.SUPABASE_URL as string;
-const SUPABASE_KEY = process.env.SUPABASE_KEY as string;
-
-const supabase = createClient(SUPABASE_URL, SUPABASE_KEY, {
- auth: {
- autoRefreshToken: false,
- persistSession: false,
- detectSessionInUrl: false,
- },
-});
-
-const getResponse = async (profileFid: string): Promise => {
- const { data: songs } = await supabase
- .from('posts')
- .select('*')
- .eq('authorFid', profileFid)
- .order('likes', { ascending: false });
- return NextResponse.json({ message: 'success', songs }, { status: 200 });
-};
-
-export async function GET(req: NextRequest): Promise {
- const profileFid = req.nextUrl.searchParams.get('profile_fid');
- if (!profileFid) return NextResponse.json({ errors: 'invalid param.' }, { status: 400 });
-
- const response = await getResponse(profileFid).catch((error) => {
- console.error('Error in background task:', error);
- });
- return response as NextResponse;
-}
-
-export const dynamic = 'force-dynamic';
-export const fetchCache = 'force-no-store';
-export const revalidate = 0;
diff --git a/app/api/zora/collections/route.ts b/app/api/zora/collections/route.ts
new file mode 100644
index 00000000..ac936a0c
--- /dev/null
+++ b/app/api/zora/collections/route.ts
@@ -0,0 +1,52 @@
+import { NextRequest } from 'next/server';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import { EVENT_ZORA_COLLECTIONS, TOKEN_INDEXER_POINT_ID } from '@/lib/consts';
+import { createStackClient } from '@/lib/stack/client';
+import { TOKEN_EVENT_TYPE } from '@/types/token';
+import getChainId from '@/lib/getChainId';
+
+const stack = createStackClient(TOKEN_INDEXER_POINT_ID);
+
+export async function GET(request: NextRequest) {
+ try {
+ await trackEndpoint(EVENT_ZORA_COLLECTIONS);
+ const query: Record = { limit: 100 };
+ const creatorAddress = request.nextUrl.searchParams.get('creator');
+ if (creatorAddress) query.address = creatorAddress;
+
+ const tokens: TOKEN_EVENT_TYPE[] = (await stack.getEvents(query)) as any;
+
+ const aggregatedData = tokens.reduce((acc: any, curr: TOKEN_EVENT_TYPE) => {
+ const { collection } = curr.metadata;
+ const key = collection;
+
+ if (!acc[key]) {
+ acc[key] = { ...curr, count: 1 };
+ } else {
+ acc[key].count += 1;
+ acc[key].points += curr.points;
+ }
+
+ return acc;
+ }, {});
+
+ const collections = Object.values(aggregatedData).map((collection: any) => {
+ const chain = collection.metadata.uniqueId.split('-')[0];
+ const chainId = getChainId(chain);
+
+ return {
+ tokensCreated: collection.points,
+ address: collection.metadata.collection,
+ chainId,
+ };
+ });
+ return Response.json({ collections });
+ } catch (error) {
+ console.error('Error:', error);
+ return Response.json({ message: 'failed' }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
diff --git a/app/api/zora/feeRecipients/route.ts b/app/api/zora/feeRecipients/route.ts
new file mode 100644
index 00000000..ea940b81
--- /dev/null
+++ b/app/api/zora/feeRecipients/route.ts
@@ -0,0 +1,16 @@
+import { ZORA_FEE_RECIPIENTS } from '@/lib/consts';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+
+export async function GET() {
+ try {
+ await trackEndpoint('zora+feeRecipients');
+ return Response.json({ message: 'success', data: ZORA_FEE_RECIPIENTS });
+ } catch (error) {
+ console.error('Error:', error);
+ return Response.json({ message: 'failed' }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
diff --git a/app/api/zora/rewards/route.tsx b/app/api/zora/rewards/route.tsx
new file mode 100644
index 00000000..0b59f65d
--- /dev/null
+++ b/app/api/zora/rewards/route.tsx
@@ -0,0 +1,29 @@
+import { NextRequest } from 'next/server';
+import { Address } from 'viem';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import { EVENT_ZORA_REWARDS } from '@/lib/consts';
+import getRewardsPoints from '@/lib/stack/getRewardsPoints';
+import indexNewRewards from '@/lib/indexNewRewards';
+import formatBigIntValues from '@/lib/formatBigIntValues';
+
+export async function GET(request: NextRequest) {
+ try {
+ await trackEndpoint(EVENT_ZORA_REWARDS);
+ const address = new URL(request.url).searchParams.get('address') as Address;
+ const rewards = await getRewardsPoints(address);
+ const newLogs = await indexNewRewards(address);
+ return Response.json({
+ message: 'success',
+ address,
+ ...rewards,
+ newLogs: formatBigIntValues(newLogs),
+ });
+ } catch (error) {
+ console.error('Error:', error);
+ return Response.json({ message: 'failed' }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
diff --git a/app/api/zora/score/route.tsx b/app/api/zora/score/route.tsx
new file mode 100644
index 00000000..1f8de078
--- /dev/null
+++ b/app/api/zora/score/route.tsx
@@ -0,0 +1,41 @@
+import { NextRequest } from 'next/server';
+import { Address } from 'viem';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import getZoraProfileScore from '@/lib/zora/score/getZoraProfileScore';
+import { EVENT_ZORA_SCORE, TOKEN_INDEXER_POINT_ID } from '@/lib/consts';
+import getZoraScore from '@/lib/zora/score/getZoraScore';
+import { createStackClient } from '@/lib/stack/client';
+import { getCreateScore } from '@/lib/zora/score/getCreateScore';
+
+const stack = createStackClient(TOKEN_INDEXER_POINT_ID);
+
+export async function GET(request: NextRequest) {
+ try {
+ await trackEndpoint(EVENT_ZORA_SCORE);
+ const address = new URL(request.url).searchParams.get('address') as Address;
+ const profile = await getZoraProfileScore(address);
+ const query: Record = { limit: 100 };
+ if (address) {
+ query.address = address;
+ }
+ const events = await stack.getEvents(query);
+ const tokensCreated = events.filter((event) => event.address === address).length;
+ const createScore = getCreateScore(tokensCreated);
+ const score = getZoraScore({ profileScore: profile.score as number, createScore });
+
+ return Response.json({
+ message: 'success',
+ address,
+ score,
+ profile,
+ createScore,
+ });
+ } catch (error) {
+ console.error('Error:', error);
+ return Response.json({ message: 'failed', error }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
diff --git a/app/api/zora/tokens/route.ts b/app/api/zora/tokens/route.ts
new file mode 100644
index 00000000..837fbe24
--- /dev/null
+++ b/app/api/zora/tokens/route.ts
@@ -0,0 +1,26 @@
+import { EVENT_ZORA_TOKENS, TOKEN_INDEXER_POINT_ID } from '@/lib/consts';
+import { createStackClient } from '@/lib/stack/client';
+import trackEndpoint from '@/lib/stack/trackEndpoint';
+import { NextRequest } from 'next/server';
+
+const stack = createStackClient(TOKEN_INDEXER_POINT_ID);
+
+export async function GET(req: NextRequest) {
+ try {
+ await trackEndpoint(EVENT_ZORA_TOKENS);
+ const query: Record = { limit: 100 };
+ const creatorAddress = req.nextUrl.searchParams.get('creatorAddress');
+ if (creatorAddress) query.address = creatorAddress;
+
+ const tokens = await stack.getEvents(query);
+
+ return Response.json({ tokens });
+ } catch (error) {
+ console.error('Error:', error);
+ return Response.json({ message: 'failed' }, { status: 400 });
+ }
+}
+
+export const dynamic = 'force-dynamic';
+export const revalidate = 0;
+export const fetchCache = 'force-no-store';
diff --git a/app/cast/[username]/[hash]/layout.tsx b/app/cast/[username]/[hash]/layout.tsx
deleted file mode 100644
index 623ede9b..00000000
--- a/app/cast/[username]/[hash]/layout.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-'use client';
-
-import { ReactNode } from 'react';
-import Sidebar from '@/components/Sidebar';
-import { Sheet, SheetContent } from '@/components/ui/sheet';
-import { useUi } from '@/providers/UiProvider';
-import { Separator } from '@/components/ui/separator';
-import GlobalPlayer from '@/components/GlobalPlayer';
-import FeedProvider from '@/providers/FeedProvider';
-import { CaretLeftIcon } from '@radix-ui/react-icons';
-import ProfileProvider from '@/providers/ProfileProvider';
-
-export default function FeedLayout({ children }: { children: ReactNode }) {
- const { menuOpen, setMenuOpen } = useUi();
-
- return (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- );
-}
diff --git a/app/cast/[username]/[hash]/page.tsx b/app/cast/[username]/[hash]/page.tsx
deleted file mode 100644
index 144d2cf6..00000000
--- a/app/cast/[username]/[hash]/page.tsx
+++ /dev/null
@@ -1,72 +0,0 @@
-import { getFrameMetadata } from '@coinbase/onchainkit';
-import type { Metadata } from 'next';
-import { DEFAULT_FRAME, DESCRIPTION, TITLE, VERCEL_URL } from '@/lib/consts';
-import getCastHash from '@/lib/neynar/getCastHash';
-import { supabaseClient } from '@/lib/supabase/client';
-import Cast from '@/components/Cast';
-import getUserByUsername from '@/lib/neynar/getNeynarUserByUsername';
-import { getUserLeaderboardRanks } from '@/lib/getUserLeadboardRank';
-import { getHighestRank } from '@/lib/getHighestRank';
-
-const frameMetadata = { ...getFrameMetadata(DEFAULT_FRAME), 'of:accepts:xmtp': '2024-02-01' };
-
-const metadata: Metadata = {
- title: TITLE,
- description: DESCRIPTION,
- openGraph: {
- title: TITLE,
- description: DESCRIPTION,
- images: `${VERCEL_URL}/images/og.png`,
- },
- icons: [`${VERCEL_URL}/images/logo2.png`],
- other: {
- ...frameMetadata,
- },
-};
-
-export async function generateMetadata({ params }: any): Promise {
- const { username, hash } = params;
-
- try {
- const userProfile = await getUserByUsername(username);
- const verifications = userProfile?.verifications || [];
- const validRanks = await getUserLeaderboardRanks(verifications);
- const rank = getHighestRank(validRanks);
-
- const ogImageUrl = `/api/og-image/cast/${username}/${hash}/${Number(rank) > 0 ? rank : 0}`;
-
- return {
- title: TITLE,
- description: DESCRIPTION,
- openGraph: {
- title: TITLE,
- description: DESCRIPTION,
- images: VERCEL_URL + ogImageUrl,
- },
- icons: [`${VERCEL_URL}/images/logo2.png`],
- other: {
- ...frameMetadata,
- },
- };
- } catch (error) {
- console.error('Failed to generate metadata:', error);
- return metadata;
- }
-}
-
-export default async function CastHome({ params }: { params: { username: string; hash: string } }) {
- const { username, hash } = params;
- const fullHash = await getCastHash(`https://warpcast.com/${username}/${hash}`);
-
- const { data: cast } = await supabaseClient
- .from('posts')
- .select('*')
- .eq('post_hash', fullHash)
- .single();
-
- return (
-
-
-
- );
-}
diff --git a/app/layout.tsx b/app/layout.tsx
index 2159fd05..184cf308 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -1,9 +1,7 @@
-import Providers from '@/providers';
import { Sora } from 'next/font/google';
import '../styles/globals.css';
import { Analytics } from '@vercel/analytics/react';
import { ReactNode } from 'react';
-import { Toaster } from '@/components/ui/toaster';
import { headers } from 'next/headers';
export const viewport = {
@@ -29,11 +27,8 @@ export default function RootLayout({ children }: { children: ReactNode }) {
-
-
- {children}
-
-
+
+ {children}