Skip to main content

Validation

Validation ensures your widget 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: RelayerValidationError or InvalidMetadataError 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 widget',
  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 widget
} 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

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 widgets work reliably across all Relayer platforms and provides clear feedback during development. Always validate your metadata before deployment!