Metadata
Metadata Validation
Validation ensures your mini-app is correctly structured and prevents runtime errors.
Validation Functions
The SDK exports two main functions for validation:
createMetadata(metadata: Metadata): ValidatedMetadata
Takes a Metadata
object, validates it completely, and returns a processed ValidatedMetadata
object.
- Validates: Structure, action types, parameters, chains, ABI compatibility
- Processes: Infers parameter types from ABI, adds validation metadata
- Throws:
SherryValidationError
orInvalidMetadataError
if validation fails - Returns:
ValidatedMetadata
with additional processed information
import { createMetadata, Metadata } from '@sherrylinks/sdk';
const metadata: Metadata = {
url: 'https://myapp.example',
icon: 'https://example.com/icon.png',
title: 'My Trigger',
description: 'An awesome Web3 mini-app',
actions: [
{
type: 'blockchain',
label: 'Mint NFT',
address: '0x...',
abi: nftAbi,
functionName: 'safeMint',
chains: { source: 43114 },
params: [
{
name: 'to',
label: 'Recipient',
type: 'address',
required: true,
},
],
},
],
};
try {
const validatedMetadata = createMetadata(metadata);
console.log('✅ Metadata is valid and processed!');
// Use validatedMetadata for your mini-app
} catch (error) {
console.error('❌ Validation failed:', error.message);
// Handle validation errors
}
validateMetadata(metadata: Metadata): ValidationResult
Validates metadata without throwing errors, returning a detailed result object.
interface ValidationResult =
| { isValid: true; type: 'ValidatedMetadata'; data: ValidatedMetadata }
| { isValid: false; type: 'ValidationError'; errors: ValidationErrorInfo[] };
interface ValidationErrorInfo {
path: string; // Path to the error (e.g., "actions[0].params[1].name")
message: string; // Descriptive error message
}
import { validateMetadata } from '@sherrylinks/sdk';
const result = validateMetadata(metadata);
if (result.isValid) {
console.log('✅ Metadata is valid:', result.data);
// Use result.data
} else {
console.error('❌ Validation errors found:');
result.errors.forEach(error => {
console.error(`- ${error.path}: ${error.message}`);
});
}
What Gets Validated?
Metadata Structure
✅ Required fields: url, icon, title, description, actions
✅ Field types: string validation for text fields
✅ Actions array: non-empty, max 4 actions
✅ BaseUrl format: valid URL if provided
Action Validation
✅ Action types: 'blockchain', 'transfer', 'http', 'dynamic', 'flow'
✅ Required properties per action type
✅ Chain configuration validity
✅ Cross-action consistency
BlockchainAction Validation
✅ Contract address: valid Ethereum address format
✅ ABI structure: valid array of ABI objects
✅ Function existence: functionName exists in ABI
✅ Parameter compatibility: params match ABI inputs
✅ Parameter order: matches ABI function signature
✅ Amount field: only for payable functions
✅ Type compatibility: parameter types match ABI types
TransferAction Validation
✅ Recipient address: valid if provided directly
✅ Amount: positive number if provided directly
✅ Chain support: valid source and destination chains
✅ Configuration objects: recipient and amountConfig structure
✅ Selection options: valid options for select/radio types
HttpAction Validation
✅ Path URL: valid URL format
✅ Parameter structure: name, label, type validation
✅ Option arrays: non-empty for select/radio parameters
✅ Type-specific validation: email format, URL format, etc.
DynamicAction Validation
✅ Path validation: absolute URL or relative with baseUrl
✅ BaseUrl dependency: relative paths require baseUrl in metadata
✅ Parameter structure: same as other action types
✅ Chain configuration: valid source chain
ActionFlow Validation
✅ Initial action: initialActionId exists in actions array
✅ Unique IDs: no duplicate action IDs within flow
✅ Reference integrity: all nextActionId references exist
✅ Graph connectivity: all actions reachable from initial action
✅ Nested action validation: each action validates according to its type
✅ Completion actions: no nextActions allowed
✅ Decision options: valid nextActionId for each option
Parameter Validation
✅ Required fields: name, label, type
✅ Type compatibility: UI types vs ABI types for blockchain actions
✅ Constraint validation: min/max, minLength/maxLength
✅ Pattern validation: regex patterns are valid
✅ Option validation: non-empty arrays for select/radio
✅ Value compatibility: default/fixed values match expected types
✅ Selection consistency: values exist in options for select/radio
Chain Validation
✅ Supported chains: 43113, 43114, 44787, 42220, 1, 11155111
✅ Source chain: always required
✅ Destination chain: optional, valid if provided
✅ Cross-chain logic: proper source/destination combination
Validation Examples
Valid Metadata
const validMetadata: Metadata = {
url: 'https://myapp.example/api/mint',
icon: 'https://myapp.example/icon.png',
title: 'NFT Minter',
description: 'Mint your unique NFT',
actions: [
{
type: 'blockchain',
label: 'Mint NFT',
address: '0x5ee75a1B1648C023e885E58bD3735Ae273f2cc52',
abi: [
{
name: 'safeMint',
type: 'function',
stateMutability: 'payable',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'tokenURI', type: 'string' },
],
outputs: [{ name: 'tokenId', type: 'uint256' }],
},
],
functionName: 'safeMint',
chains: { source: 43114 },
amount: 0.1,
params: [
{
name: 'to',
label: 'Recipient Address',
type: 'address',
required: true,
},
{
name: 'tokenURI',
label: 'Token URI',
type: 'string',
required: true,
value: 'ipfs://QmHash',
fixed: true,
},
],
},
],
};
// This will validate successfully
const validated = createMetadata(validMetadata);
Common Validation Errors
Missing Required Fields
const invalidMetadata = {
// Missing required fields
title: 'My App',
actions: [],
};
// Error: "Metadata missing required 'url' field"
// Error: "Metadata missing required 'icon' field"
// Error: "Metadata missing required 'description' field"
// Error: "Metadata must include at least one action"
Parameter Mismatch
const invalidAction = {
type: 'blockchain',
label: 'Transfer',
address: '0x...',
abi: [
{
name: 'transfer',
inputs: [
{ name: 'to', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
},
],
functionName: 'transfer',
chains: { source: 43114 },
params: [
// Wrong order! Should be 'to' first, then 'amount'
{
name: 'amount',
label: 'Amount',
type: 'uint256',
},
{
name: 'to',
label: 'Recipient',
type: 'address',
},
],
};
// Error: "Parameter name mismatch at index 0. Expected 'to', received 'amount'"
Invalid Chain Configuration
const invalidChain = {
type: 'transfer',
label: 'Send Tokens',
chains: { source: 99999 }, // Invalid chain
amount: 0.1,
};
// Error: "Invalid source chain: 99999"
Missing BaseUrl for Dynamic Action
const invalidDynamic: Metadata = {
url: 'https://myapp.example',
icon: 'https://example.com/icon.png',
title: 'Dynamic App',
description: 'App with dynamic action',
// Missing baseUrl!
actions: [
{
type: 'dynamic',
label: 'Dynamic Action',
path: '/api/dynamic', // Relative path but no baseUrl
chains: { source: 43114 },
},
],
};
// Error: "Dynamic action has a relative path '/api/dynamic' but no baseUrl is provided"
Error Handling Best Practices
1. Always Validate During Development
import { createMetadata } from '@sherrylinks/sdk';
// Validate early in development
const metadata = createMetadata(myMetadata);
2. Use Try-Catch for Error Handling
try {
const validated = createMetadata(metadata);
return NextResponse.json(validated);
} catch (error) {
console.error('Validation error:', error);
return NextResponse.json({ error: 'Invalid metadata configuration' }, { status: 500 });
}
3. Provide Detailed Error Information in Development
if (process.env.NODE_ENV === 'development') {
try {
const validated = createMetadata(metadata);
return NextResponse.json(validated);
} catch (error) {
return NextResponse.json(
{
error: 'Validation failed',
details: error.message,
validationErrors: error.validationErrors || [],
},
{ status: 500 },
);
}
}
4. Use validateMetadata for Detailed Debugging
const result = validateMetadata(metadata);
if (!result.isValid) {
console.log('Validation errors:');
result.errors.forEach(error => {
console.log(`❌ ${error.path}: ${error.message}`);
});
}
Integration with Development Tools
Sherry Debugger
The Sherry Debugger provides visual validation:
- URL Mode: Test your deployed endpoint
- JSON Mode: Paste metadata JSON directly
- TypeScript Mode: Validate your metadata code
IDE Integration
// Use TypeScript for compile-time validation
import { Metadata } from '@sherrylinks/sdk';
const metadata: Metadata = {
// TypeScript will catch missing required fields
url: 'https://myapp.example',
icon: 'https://example.com/icon.png',
title: 'My App',
description: 'Description',
actions: [
// TypeScript will validate action structure
],
};
Unit Testing
import { validateMetadata, createMetadata } from '@sherrylinks/sdk';
describe('Metadata Validation', () => {
test('valid metadata should pass validation', () => {
const result = validateMetadata(validMetadata);
expect(result.isValid).toBe(true);
});
test('invalid metadata should fail validation', () => {
const result = validateMetadata(invalidMetadata);
expect(result.isValid).toBe(false);
expect(result.errors.length).toBeGreaterThan(0);
});
test('createMetadata should throw on invalid input', () => {
expect(() => createMetadata(invalidMetadata)).toThrow();
});
});
Proper validation ensures your mini-apps work reliably across all Sherry platforms and provides clear feedback during development. Always validate your metadata before deployment!