Skip to main content

Overview

The JSON precompile provides efficient JSON parsing capabilities directly on-chain, allowing smart contracts to extract specific values from JSON data without complex string manipulation. This is particularly useful for processing API responses, oracle data, and structured data from external sources. Contract Address: 0x0000000000000000000000000000000000001003

Key Features

  • Efficient Parsing: Gas-optimized JSON parsing on-chain
  • Type-Safe Extraction: Extract data as specific types (bytes, uint256, arrays)
  • Key-Based Access: Extract values using JSON key paths
  • Oracle Integration: Process structured data from external APIs

Available Functions

View Functions

Extract a value from JSON data as bytes.Parameters:
  • input (bytes): The JSON data as bytes
  • key (string): The JSON key path to extract
Returns: The extracted value as bytes
const jsonData = '{"price": "100.50", "symbol": "SEI"}';
const jsonBytes = ethers.toUtf8Bytes(jsonData);

const priceBytes = await jsonContract.extractAsBytes(
  jsonBytes,
  "price"
);

// Convert back to string
const price = ethers.toUtf8String(priceBytes);
console.log('Price:', price); // "100.50"
Extract an array of values from JSON data as bytes array.Parameters:
  • input (bytes): The JSON data as bytes
  • key (string): The JSON key path to extract (must point to an array)
Returns: Array of extracted values as bytes
const jsonData = '{"validators": ["seivaloper1...", "seivaloper2..."]}';
const jsonBytes = ethers.toUtf8Bytes(jsonData);

const validatorBytes = await jsonContract.extractAsBytesList(
  jsonBytes,
  "validators"
);

// Convert bytes array to string array
const validators = validatorBytes.map(bytes => ethers.toUtf8String(bytes));
console.log('Validators:', validators);
Extract a numeric value from JSON data as uint256.Parameters:
  • input (bytes): The JSON data as bytes
  • key (string): The JSON key path to extract (must be numeric)
Returns: The extracted value as uint256
const jsonData = '{"balance": "1000000000000000000", "decimals": 18}';
const jsonBytes = ethers.toUtf8Bytes(jsonData);

const balance = await jsonContract.extractAsUint256(
  jsonBytes,
  "balance"
);

const decimals = await jsonContract.extractAsUint256(
  jsonBytes,
  "decimals"
);

console.log('Balance:', formatEther(balance)); // "1.0"
console.log('Decimals:', decimals.toString()); // "18"

Usage Examples

import { createPublicClient, http, formatEther, toHex } from 'viem';
import { 
  seiTestnet,
  JSON_PRECOMPILE_ABI,
  JSON_PRECOMPILE_ADDRESS 
} from '@sei-js/precompiles/viem';

const publicClient = createPublicClient({
  chain: seiTestnet,
  transport: http()
});

// Helper to convert string to bytes
function stringToBytes(str: string): `0x${string}` {
  return toHex(new TextEncoder().encode(str));
}

// Helper to convert bytes to string
function bytesToString(bytes: `0x${string}`): string {
  const uint8Array = new Uint8Array(
    bytes.slice(2).match(/.{1,2}/g)?.map(byte => parseInt(byte, 16)) || []
  );
  return new TextDecoder().decode(uint8Array);
}

// Extract string value from JSON
async function extractStringValue(jsonData: string, key: string): Promise<string> {
  const jsonBytes = stringToBytes(jsonData);
  
  const result = await publicClient.readContract({
    address: JSON_PRECOMPILE_ADDRESS,
    abi: JSON_PRECOMPILE_ABI,
    functionName: 'extractAsBytes',
    args: [jsonBytes, key]
  });
  
  return bytesToString(result);
}

// Extract numeric value from JSON
async function extractNumericValue(jsonData: string, key: string): Promise<bigint> {
  const jsonBytes = stringToBytes(jsonData);
  
  return await publicClient.readContract({
    address: JSON_PRECOMPILE_ADDRESS,
    abi: JSON_PRECOMPILE_ABI,
    functionName: 'extractAsUint256',
    args: [jsonBytes, key]
  });
}

// Extract array from JSON
async function extractArrayValue(jsonData: string, key: string): Promise<string[]> {
  const jsonBytes = stringToBytes(jsonData);
  
  const result = await publicClient.readContract({
    address: JSON_PRECOMPILE_ADDRESS,
    abi: JSON_PRECOMPILE_ABI,
    functionName: 'extractAsBytesList',
    args: [jsonBytes, key]
  });
  
  return result.map(bytes => bytesToString(bytes));
}

// Oracle data processor
async function processOracleData(oracleResponse: string) {
  // Example oracle response
  const jsonData = `{
    "prices": {
      "SEI": {
        "usd": "0.45",
        "change_24h": "5.2"
      },
      "ATOM": {
        "usd": "12.30",
        "change_24h": "-2.1"
      }
    },
    "timestamp": "1640995200",
    "source": "coingecko"
  }`;
  
  // Extract individual values
  const seiPrice = await extractStringValue(jsonData, "prices.SEI.usd");
  const atomPrice = await extractStringValue(jsonData, "prices.ATOM.usd");
  const timestamp = await extractNumericValue(jsonData, "timestamp");
  const source = await extractStringValue(jsonData, "source");
  
  return {
    prices: {
      SEI: parseFloat(seiPrice),
      ATOM: parseFloat(atomPrice)
    },
    timestamp: Number(timestamp),
    source
  };
}

// DeFi price aggregator
async function aggregatePriceData(priceFeeds: string[]) {
  const results = [];
  
  for (const feed of priceFeeds) {
    try {
      // Each feed is a JSON string with price data
      const symbol = await extractStringValue(feed, "symbol");
      const price = await extractStringValue(feed, "price");
      const volume = await extractNumericValue(feed, "volume");
      const lastUpdate = await extractNumericValue(feed, "last_update");
      
      results.push({
        symbol,
        price: parseFloat(price),
        volume: Number(volume),
        lastUpdate: Number(lastUpdate)
      });
    } catch (error) {
      console.error('Failed to process price feed:', error);
    }
  }
  
  return results;
}

// Multi-chain data processor
async function processMultiChainData(chainDataJson: string) {
  // Extract chain information
  const chainName = await extractStringValue(chainDataJson, "chain_name");
  const blockHeight = await extractNumericValue(chainDataJson, "block_height");
  const validators = await extractArrayValue(chainDataJson, "validators");
  const totalSupply = await extractNumericValue(chainDataJson, "total_supply");
  
  return {
    chainName,
    blockHeight: Number(blockHeight),
    validators,
    totalSupply: formatEther(totalSupply)
  };
}

// Configuration parser
async function parseProtocolConfig(configJson: string) {
  const protocolName = await extractStringValue(configJson, "protocol_name");
  const version = await extractStringValue(configJson, "version");
  const maxSupply = await extractNumericValue(configJson, "max_supply");
  const features = await extractArrayValue(configJson, "features");
  const feeRate = await extractNumericValue(configJson, "fee_rate");
  
  return {
    protocolName,
    version,
    maxSupply: formatEther(maxSupply),
    features,
    feeRate: Number(feeRate)
  };
}

// Example usage
const oracleData = await processOracleData('oracle_response');
console.log('SEI Price:', oracleData.prices.SEI);

const chainData = `{
  "chain_name": "Sei",
  "block_height": "1000000",
  "validators": ["seivaloper1...", "seivaloper2..."],
  "total_supply": "1000000000000000000000000000"
}`;

const processedData = await processMultiChainData(chainData);
console.log('Chain data:', processedData);

JSON Key Path Syntax

Simple Keys

// For JSON: {"name": "Sei", "price": "0.45"}
const name = await extractStringValue(jsonData, "name");
const price = await extractStringValue(jsonData, "price");

Nested Objects

// For JSON: {"token": {"name": "Sei", "decimals": 18}}
const tokenName = await extractStringValue(jsonData, "token.name");
const decimals = await extractNumericValue(jsonData, "token.decimals");

Array Access

// For JSON: {"prices": ["0.45", "0.46", "0.44"]}
const allPrices = await extractArrayValue(jsonData, "prices");

// For JSON: {"validators": [{"name": "val1"}, {"name": "val2"}]}
const validatorNames = await extractArrayValue(jsonData, "validators.name");

Common Use Cases

Oracle Data Processing

  • Price Feeds: Extract price data from oracle responses
  • Market Data: Process trading volume, market cap, and other metrics
  • External APIs: Parse responses from external data sources

DeFi Protocol Integration

  • Token Metadata: Extract token information from registry responses
  • Pool Data: Process liquidity pool information
  • Yield Data: Extract APY and reward information

Cross-Chain Data

  • Chain State: Process blockchain state information
  • Validator Data: Extract validator sets and staking information
  • Transaction Data: Parse complex transaction metadata

Data Processing Patterns

Type-Safe Extraction

// Define data schema
interface TokenData {
  name: string;
  symbol: string;
  decimals: number;
  totalSupply: bigint;
  holders: string[];
}

async function extractTokenData(jsonData: string): Promise<TokenData> {
  return {
    name: await extractStringValue(jsonData, "name"),
    symbol: await extractStringValue(jsonData, "symbol"),
    decimals: Number(await extractNumericValue(jsonData, "decimals")),
    totalSupply: await extractNumericValue(jsonData, "total_supply"),
    holders: await extractArrayValue(jsonData, "holders")
  };
}

Batch Processing

// Process multiple JSON objects efficiently
async function batchProcessJsonData(jsonDataArray: string[], keyPath: string) {
  const extractPromises = jsonDataArray.map(jsonData =>
    extractStringValue(jsonData, keyPath)
  );
  
  return await Promise.all(extractPromises);
}

Error-Resilient Parsing

async function safeExtractValue(
  jsonData: string,
  key: string,
  defaultValue: any = null
): Promise<any> {
  try {
    // Try string extraction first
    return await extractStringValue(jsonData, key);
  } catch {
    try {
      // Try numeric extraction
      return await extractNumericValue(jsonData, key);
    } catch {
      try {
        // Try array extraction
        return await extractArrayValue(jsonData, key);
      } catch {
        return defaultValue;
      }
    }
  }
}

Performance Optimization

Efficient Data Extraction

// Cache frequently accessed data
class JSONCache {
  private cache = new Map<string, any>();
  
  async getCachedValue(jsonData: string, key: string, extractor: Function) {
    const cacheKey = `${jsonData.slice(0, 100)}-${key}`;
    
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }
    
    const value = await extractor(jsonData, key);
    this.cache.set(cacheKey, value);
    
    return value;
  }
}

Batch Key Extraction

// Extract multiple keys in parallel
async function extractMultipleKeys(
  jsonData: string,
  keys: Array<{ key: string; type: 'string' | 'number' | 'array' }>
) {
  const extractPromises = keys.map(async ({ key, type }) => {
    switch (type) {
      case 'string':
        return { key, value: await extractStringValue(jsonData, key) };
      case 'number':
        return { key, value: await extractNumericValue(jsonData, key) };
      case 'array':
        return { key, value: await extractArrayValue(jsonData, key) };
      default:
        throw new Error(`Unknown type: ${type}`);
    }
  });
  
  const results = await Promise.all(extractPromises);
  
  return results.reduce((acc, { key, value }) => {
    acc[key] = value;
    return acc;
  }, {} as Record<string, any>);
}

Error Handling

Common errors when using the JSON precompile:
  • Invalid JSON: Malformed JSON data
  • Key not found: Specified key doesn’t exist in JSON
  • Type mismatch: Trying to extract wrong data type
  • Invalid key path: Malformed key path syntax
async function safeJsonExtraction(
  jsonData: string,
  key: string,
  expectedType: 'string' | 'number' | 'array'
) {
  try {
    // Validate JSON format first
    JSON.parse(jsonData); // This will throw if invalid
    
    switch (expectedType) {
      case 'string':
        return await extractStringValue(jsonData, key);
      case 'number':
        return await extractNumericValue(jsonData, key);
      case 'array':
        return await extractArrayValue(jsonData, key);
      default:
        throw new Error(`Unsupported type: ${expectedType}`);
    }
  } catch (error) {
    if (error.message.includes('JSON')) {
      throw new Error('Invalid JSON format');
    } else if (error.message.includes('key not found')) {
      throw new Error(`Key "${key}" not found in JSON data`);
    } else if (error.message.includes('type mismatch')) {
      throw new Error(`Type mismatch: expected ${expectedType} for key "${key}"`);
    } else {
      throw error;
    }
  }
}

Security Considerations

Input Validation

  • Always validate JSON data before processing
  • Sanitize key paths to prevent injection attacks
  • Limit JSON data size to prevent DoS attacks

Data Integrity

  • Verify data sources and authenticity
  • Implement checksums for critical data
  • Use multiple data sources for validation
// Security validation helper
function validateJsonInput(jsonData: string, maxSize: number = 10000): boolean {
  // Check size limit
  if (jsonData.length > maxSize) {
    throw new Error('JSON data exceeds size limit');
  }
  
  // Validate JSON format
  try {
    JSON.parse(jsonData);
  } catch {
    throw new Error('Invalid JSON format');
  }
  
  // Check for potential security issues
  if (jsonData.includes('<script>') || jsonData.includes('javascript:')) {
    throw new Error('Potentially malicious content detected');
  }
  
  return true;
}
  • Oracle: Use with oracle data for price feeds
  • IBC: Process cross-chain transaction data
  • Bank: Parse token metadata and balance information