This project demonstrates how to implement a "Dynamic Action" (Mini App) using the Sherry SDK within a Next.js application. It shows how to expose metadata for an action and handle its execution, creating interactive blockchain applications that can be embedded in different platforms.
- Dynamic Action Metadata: Exposes a
GETendpoint serving metadata required by Sherry SDK to define a dynamic action - Action Execution: Implements a
POSTendpoint that handles action execution, receiving parameters and returning serialized transactions - CORS Support: Complete CORS handling for cross-origin requests, including
OPTIONSpreflight requests - Blockchain Integration: Uses
viemfor chain details (Avalanche Fuji) andwagmifor transaction serialization - Parameter Validation: Robust parameter handling and validation
- TypeScript Support: Full TypeScript implementation with proper types
- Node.js (version 18.x or higher recommended)
- npm, yarn, or pnpm
- Basic understanding of Next.js and blockchain concepts
# Clone this repository or create a new Next.js project
npx create-next-app@latest sherry-example --typescript --eslint --tailwind --src-dir --app --import-alias "@/*"
cd sherry-examplenpm install @sherrylinks/sdk viem wagmi
# or
yarn add @sherrylinks/sdk viem wagmi
# or
pnpm add @sherrylinks/sdk viem wagmiTo avoid build errors, you can disable ESLint in next.config.js:
/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
}
module.exports = nextConfigStart the development server:
npm run dev
# or
yarn dev
# or
pnpm devThe application will be available at http://localhost:3000. The API endpoint will be accessible at http://localhost:3000/api/example.
Purpose: Returns metadata for the dynamic action, used by platforms to display and interact with your mini app.
Response Example:
{
"url": "https://sherry.social",
"icon": "https://avatars.githubusercontent.com/u/117962315",
"title": "Timestamped Message",
"baseUrl": "http://localhost:3000",
"description": "Store a message with an optimized timestamp calculated by our algorithm",
"actions": [
{
"type": "dynamic",
"label": "Store Message",
"description": "Store your message with a custom timestamp calculated for optimal storage",
"chains": { "source": "fuji" },
"path": "/api/example",
"params": [
{
"name": "message",
"label": "Your Message Hermano!",
"type": "text",
"required": true,
"description": "Enter the message you want to store on the blockchain"
}
]
}
]
}Important: Note that
baseUrlis automatically derived from the request headers. During development, it will usehttp://localhost:3000, but when deployed, it will use your production domain. This happens automatically through:const host = req.headers.get('host') || 'localhost:3000'; const protocol = req.headers.get('x-forwarded-proto') || 'http'; const serverUrl = `${protocol}://${host}`;
Purpose: Executes the dynamic action, processes parameters, and returns a serialized transaction.
Query Parameters:
message(string, required): The message to process
Example Request:
POST http://localhost:3000/api/example?message=HelloSherry
Success Response:
{
"serializedTransaction": "0x...",
"chainId": "avalancheFuji"
}Error Response (400 - Missing Parameter):
{
"error": "Message parameter is required"
}Purpose: Handles CORS preflight requests for cross-origin compatibility.
Response: 204 No Content with appropriate CORS headers.
You have several options to test your dynamic action:
- Visit https://app.sherry.social/home
- Enter your endpoint URL:
- During development:
http://localhost:3000/api/example(only works if your local server is publicly accessible) - After deployment: Use your actual deployed URL (e.g.,
https://your-app-name.vercel.app/api/example)
- During development:
- The platform will automatically render your mini app
Important: The localhost URL is only provided as a reference. For actual integration with the Sherry platform, you must deploy your application and use the public URL. The Sherry platform cannot access your localhost unless you're using a tunneling service.
- Go to https://app.sherry.social/debugger
- Test using one of three methods:
- URL: Paste your GET endpoint URL (localhost during development, deployed URL in production)
- JSON: Copy and paste the metadata JSON
- TypeScript: Paste your TypeScript code directly
Note: The debugger is in active development and may contain bugs. You can report issues directly from the debugger interface.
-
Start Development Server:
npm run dev
-
Verify GET Endpoint:
- Navigate to
http://localhost:3000/api/examplelocally - After deployment, use your deployed URL (e.g.,
https://your-app-name.vercel.app/api/example) - Confirm you see the metadata JSON response
- Navigate to
-
Test in Debugger:
- Use appropriate URL based on environment (local or deployed)
- Verify input fields render correctly
- Test form completion and submission
-
Verify Execution:
- Fill in required parameters
- Click the action button
- Confirm you receive a serialized transaction
βββ app/
β βββ api/
β β βββ example/
β β βββ route.ts # Main API endpoint
β βββ globals.css # Global styles
β βββ layout.tsx # Root layout
β βββ page.tsx # Home page
βββ README.md # This file
βββ package.json # Dependencies
createMetadata: Validates and structures metadata objectsMetadata,ValidatedMetadata,ExecutionResponse: TypeScript types for proper data handlingExecutionResponseis specifically used to type the transaction response, ensuring the proper format:const resp: ExecutionResponse = { serializedTransaction: serialized, chainId: avalancheFuji.name, }
- Parameter validation and processing
- viem: Provides chain configurations (Avalanche Fuji)
- wagmi: Handles transaction serialization
- The
serializefunction from wagmi is used to convert a transaction object into a serialized format:import { serialize } from 'wagmi' const tx = { to: '0x5ee75a1B1648C023e885E58bD3735Ae273f2cc52', value: BigInt(1000000), chainId: avalancheFuji.id, } const serialized = serialize(tx)
- The
- Support for multiple blockchain networks
Complete CORS setup allowing cross-origin requests from any domain, essential for platform integration.
To add new input fields, extend the params array in your metadata:
params: [
// ... existing params
{
name: "newParam",
label: "New Parameter",
type: "number",
required: false,
description: "Description of the new parameter"
}
]Update the chain configuration:
import { mainnet } from "viem/chains"; // or any other chain
const tx = {
to: '0x...',
value: BigInt(1000000),
chainId: mainnet.id, // Update chain ID
}
const response: ExecutionResponse = {
serializedTransaction: serialized,
chainId: mainnet.name, // Update chain name
}Extend the POST handler with your custom logic:
export async function POST(req: NextRequest) {
try {
const { searchParams } = new URL(req.url);
const message = searchParams.get("message");
// Add your custom business logic here
const processedData = await processMessage(message);
// Create transaction based on processed data
const tx = createCustomTransaction(processedData);
// ... rest of the handler
} catch (error) {
// Handle errors
}
}CORS Errors:
- Ensure all endpoints include proper CORS headers
- Verify OPTIONS method is implemented
Metadata Validation Failures:
- Check all required fields are present
- Verify data types match expected formats
- Use
createMetadata()for validation
Parameter Issues:
- Confirm parameter names match between metadata and POST handler
- Verify required parameters are marked correctly
Transaction Serialization:
- Ensure values are in correct format (BigInt for wei amounts)
- Verify chainId is valid for target network
- Check destination address format
- Check Browser Console: Look for JavaScript errors when testing
- Verify JSON Response: Ensure GET endpoint returns valid JSON
- Test Parameters: Use browser developer tools to inspect network requests
- Console Logging: Add console.log statements to trace execution flow
Contributions are welcome! Please feel free to submit issues, feature requests, or pull requests.
This project is open source and available under the MIT License.