This frame enables swaps and announcements. It is built and customized for $NATIVE, but feel free to fork and use for your own projects (MIT-license). For swaps, the fee is currently set to 25, split between Native and @nonomnouns with a 1% sponsorship fee to Splits.
Initial developer: @nonomnouns. Built as response to bounty opened by Derek and inspired by Dan Romero's original cast here.
- Swap ETH to NATIVE tokens on Base chain
- Real-time price updates
- Transaction status tracking
- Affiliate fee integration
The frame supports various types of notifications following the Farcaster Frames v2 Specification:
The notifications handler supports these webhook events:
- frame_added: When user adds the frame
type EventFrameAddedPayload = {
event: "frame_added";
notificationDetails?: {
url: string; // Notification endpoint URL
token: string; // Auth token for sending notifications
};
};
- frame_removed: When user removes the frame
{
event: "frame_removed";
}
- notifications_enabled/disabled: When user toggles notifications
type EventNotificationsEnabledPayload = {
event: "notifications_enabled";
notificationDetails: {
url: string;
token: string;
};
};
When sending notifications, the server will respond with:
successTokens
: Successfully sent notificationsinvalidTokens
: Tokens that should not be used againrateLimitedTokens
: Tokens that exceeded rate limit
To bypass rate limits for important notifications, use:
curl -X POST '[YOUR_APP_URL]/api/announcements' \
-H "Content-Type: application/json" \
-H "X-Skip-Rate-Limit: true" \
-d '{
"fid": [USER_FID]
}'
You can manage announcements using the API endpoints:
- Create a new announcement (POST to Supabase):
curl -X POST '[SUPABASE_URL]/rest/v1/announcements' \
-H "apikey: [YOUR_KEY]" \
-H "Authorization: Bearer [YOUR_KEY]" \
-H "Content-Type: application/json" \
-d '{
"title": "π New Feature Alert!",
"text": "Check out our latest updates!",
"created_at": "2024-02-09T12:00:00Z"
}'
- Send notifications to users:
curl -X POST '[YOUR_APP_URL]/api/announcements' \
-H "Content-Type: application/json" \
-H "X-Skip-Rate-Limit: true" \
-d '{
"fid": [USER_FID]
}'
- Get all announcements:
curl -X GET '[YOUR_APP_URL]/api/announcements' \
-H "Content-Type: application/json"
You can use this Python script to manage notifications and announcements:
<<<<<<< HEAD
=======
````python
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
import requests
import time
import json
# Configuration
SUPABASE_URL = "https://mock-supabase-url.example.com" # Mock Supabase URL
SUPABASE_KEY = "mock-supabase-key-1234567890" # Mock Supabase API key
APP_URL = "https://mock-app-url.example.com" # Mock application URL
def get_all_fids():
"""Fetch all FIDs from Supabase"""
headers = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}",
"Content-Type": "application/json"
}
<<<<<<< HEAD
=======
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
response = requests.get(
f"{SUPABASE_URL}/rest/v1/notification_tokens?select=fid",
headers=headers
)
<<<<<<< HEAD
=======
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
if response.status_code == 200:
return [item['fid'] for item in response.json()]
else:
print(f"Error getting FIDs: {response.status_code}")
return []
<<<<<<< HEAD
=======
def send_announcement_to_user(fid):
"""Send an announcement to a single user"""
headers = {
"Content-Type": "application/json",
"X-Skip-Rate-Limit": "true"
}
data = {
"fid": fid
}
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
def send_announcement_to_user(fid):
"""Send an announcement to a single user"""
headers = {
"Content-Type": "application/json",
"X-Skip-Rate-Limit": "true"
}
data = {
"fid": fid
}
response = requests.post(
f"{APP_URL}/api/announcements",
headers=headers,
json=data
)
<<<<<<< HEAD
return response.status_code == 200
=======
return response.status_code == 200
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
def create_announcement(title, text):
"""Create a new announcement in Supabase"""
headers = {
"apikey": SUPABASE_KEY,
"Authorization": f"Bearer {SUPABASE_KEY}",
"Content-Type": "application/json",
"Prefer": "return=minimal"
}
data = {
"title": title,
"text": text,
"created_at": time.strftime('%Y-%m-%dT%H:%M:%SZ')
}
response = requests.post(
f"{SUPABASE_URL}/rest/v1/announcements",
headers=headers,
json=data
)
return response.status_code == 201
<<<<<<< HEAD
def main():
# 1. Create a new announcement
announcement_title = "π New Features Released!"
announcement_text = "We've just released exciting new features for Native Swap. Check them out!"
print("Creating new announcement...")
if create_announcement(announcement_title, announcement_text):
print("Announcement created successfully!")
else:
print("Failed to create announcement")
return
# 2. Fetch all FIDs
print("\nGetting all FIDs...")
fids = get_all_fids()
print(f"Found {len(fids)} users")
# 3. Send to all users
success_count = 0
fail_count = 0
print("\nSending notifications to users...")
for i, fid in enumerate(fids, 1):
print(f"Processing {i}/{len(fids)}: FID {fid}", end=" ")
if send_announcement_to_user(fid):
print("β
")
success_count += 1
else:
print("β")
fail_count += 1
# Small delay between requests
time.sleep(0.5)
# 4. Print summary
print("\nNotification Summary:")
print(f"Total Users: {len(fids)}")
print(f"Successful: {success_count}")
print(f"Failed: {fail_count}")
=======
data = {
"title": title,
"text": text,
"created_at": time.strftime('%Y-%m-%dT%H:%M:%SZ')
}
response = requests.post(
f"{SUPABASE_URL}/rest/v1/announcements",
headers=headers,
json=data
)
return response.status_code == 201
def main():
# 1. Create a new announcement
announcement_title = "π New Features Released!"
announcement_text = "We've just released exciting new features for Native Swap. Check them out!"
print("Creating new announcement...")
if create_announcement(announcement_title, announcement_text):
print("Announcement created successfully!")
else:
print("Failed to create announcement")
return
# 2. Fetch all FIDs
print("\nGetting all FIDs...")
fids = get_all_fids()
print(f"Found {len(fids)} users")
# 3. Send to all users
success_count = 0
fail_count = 0
print("\nSending notifications to users...")
for i, fid in enumerate(fids, 1):
print(f"Processing {i}/{len(fids)}: FID {fid}", end=" ")
if send_announcement_to_user(fid):
print("β
")
success_count += 1
else:
print("β")
fail_count += 1
# Small delay between requests
time.sleep(0.5)
# 4. Print summary
print("\nNotification Summary:")
print(f"Total Users: {len(fids)}")
print(f"Successful: {success_count}")
print(f"Failed: {fail_count}")
>>>>>>> 34c2f6354692aa38432b1a0eb2c7f522a4fce419
if __name__ == "__main__":
main()
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
For local development and testing with Farcaster Frames, you'll need to expose your local server using ngrok:
- Install ngrok:
# Using npm
npm install ngrok -g
# Using chocolatey (Windows)
choco install ngrok
# Using homebrew (macOS)
brew install ngrok
<<<<<<< HEAD For local development and testing with Farcaster Frames, you'll need to expose your local server using ngrok:
- Install ngrok:
# Using npm
npm install ngrok -g
# Using chocolatey (Windows)
choco install ngrok
# Using homebrew (macOS)
brew install ngrok
- Start your local server (default port 3000):
34c2f6354692aa38432b1a0eb2c7f522a4fce419
npm run dev
34c2f6354692aa38432b1a0eb2c7f522a4fce419
ngrok http 3000
-
Copy the HTTPS URL provided by ngrok (e.g.,
https://your-ngrok-url.ngrok.io
)
34c2f6354692aa38432b1a0eb2c7f522a4fce419
NEXT_PUBLIC_URL=https://your-ngrok-url.ngrok.io
34c2f6354692aa38432b1a0eb2c7f522a4fce419
- Go to Warpcast Frame Development
- Paste your ngrok URL into the frame URL field
- Click "Test frame" to preview and debug your frame
Note: The ngrok URL changes each time you restart ngrok. For persistent URLs, consider upgrading to a paid ngrok plan or using a production deployment.
You can access your local development server at http://localhost:3000 and the ngrok-exposed version at your ngrok URL.
- Next.js 14 with App Router
- Supabase for data storage
- Upstash Redis for caching
- Farcaster Frame SDK
- 0x Protocol for swaps
- Alchemy for token data
- Neynar for Farcaster data
Create a .env
file with the following variables:
NEXT_PUBLIC_URL=your_app_url
UPSTASH_REDIS_REST_URL=your_redis_url
UPSTASH_REDIS_REST_TOKEN=your_redis_token
NEYNAR_API_KEY=your_neynar_key
ZEROX_API_KEY=your_0x_key
NEXT_PUBLIC_ALCHEMY_KEY=your_alchemy_key
NEXT_PUBLIC_SUPABASE_URL=your_supabase_url
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_supabase_key
To set up the database tables in Supabase, follow these steps:
- Open your Supabase project dashboard
- Go to SQL Editor
- Create a new query
- Copy and paste the following SQL:
-- Announcements table
create table announcements (
id bigint generated by default as identity primary key,
title text not null,
text text not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null
);
-- Notification tokens table
create table notification_tokens (
fid bigint not null,
token text not null,
url text not null,
created_at timestamp with time zone default timezone('utc'::text, now()) not null,
primary key (fid, token)
);
-- Optional: Add some test data
insert into announcements (title, text) values
('π Welcome to Native Swap!', 'We are excited to launch our new swap platform.'),
('π’ New Features', 'Check out our latest updates and improvements.');
This setup creates:
announcements
table for storing platform announcementsnotification_tokens
table for managing user notification preferences- Sample announcement data
To learn more about Next.js, take a look at the following resources:
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.