Skip to Content

JSON Precompile Usage

Address: 0x0000000000000000000000000000000000001003

The Sei JSON precompile allows EVM applications to efficiently parse and query JSON data directly within smart contracts. This enables complex data handling capabilities that are not natively available in Solidity, making it easier to work with structured data from external sources or APIs.

What is a precompile? A precompile is a special smart contract deployed at a fixed address by the Sei protocol itself, that exposes custom native chain logic to EVM-based applications. It acts like a regular contract from the EVM’s perspective, but executes privileged, low-level logic efficiently.

How Does the JSON Precompile Work?

The JSON precompile at address 0x0000000000000000000000000000000000001003 exposes functions like extractAsBytes(), extractAsBytesList(), and extractAsUint256().

  • Direct Integration: EVM contracts and dApps can parse JSON data like any other smart contract method.
  • Native Execution: JSON parsing is executed at the native level for maximum efficiency.
  • Seamless Bridge: No need for complex workarounds or external libraries for JSON handling.

Use Cases

  • Oracle Integration: Parse complex oracle responses containing multiple data points.
  • DeFi Applications: Process structured price feeds and market data.
  • Gaming: Handle complex game state and player data stored in JSON format.
  • Cross-Chain Communication: Parse messages and data from other chains.
  • NFT Metadata: Extract and manipulate NFT metadata stored in JSON format.

Functions

The JSON precompile exposes the following functions:

Query Functions

/// Extracts data as bytes from the input using the specified key. /// @param input The input data. /// @param key The key to extract. /// @return The extracted data as bytes. function extractAsBytes( bytes memory input, string memory key ) external view returns (bytes memory response); /// Extracts data as a list of bytes from the input using the specified key. /// @param input The input data. /// @param key The key to extract. /// @return The extracted data as bytes collection. function extractAsBytesList( bytes memory input, string memory key ) external view returns (bytes[] memory response); /// Extracts data as a uint256 from the input using the specified key. /// @param input The input data. /// @param key The key to extract. /// @return The extracted uint256. function extractAsUint256( bytes memory input, string memory key ) external view returns (uint256 response);

Using the Precompile

Setup

Prerequisites

Before getting started, ensure you have:

  • Node.js (v16 or higher)
  • npm or yarn package manager
  • MetaMask or compatible EVM wallet configured for Sei Mainnet
  • SEI tokens for gas fees

Install Dependencies

Install the required packages for interacting with Sei precompiles:

# Install ethers.js for smart contract interactions npm install ethers # Install Sei EVM bindings for precompile addresses and ABIs npm install @sei-js/precompiles@^2.1.2 # Install dotenv for managing private keys (recommended for security) npm install dotenv

Import Precompile Components

// Import JSON precompile address and ABI // View the entire ABI here: https://github.com/sei-protocol/sei-chain/tree/main/precompiles/json import { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } from '@sei-js/precompiles'; import { ethers } from 'ethers';
Precompile Address: The JSON precompile is deployed at 0x0000000000000000000000000000000000001003

Contract Initialization

Set up your provider, signer, and contract instance:

// Using MetaMask as the signer and provider (browser environment) const provider = new ethers.BrowserProvider(window.ethereum); await provider.send('eth_requestAccounts', []); const signer = await provider.getSigner(); // Or for Node.js environment const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); // Create a contract instance for the JSON precompile const jsonPrecompile = new ethers.Contract(JSON_PRECOMPILE_ADDRESS, JSON_PRECOMPILE_ABI, signer);

Data Type Handling

The JSON precompile has specific limitations for different data types:

Supported Data Types

Function

Supports

Limitations

extractAsBytes

Strings, Objects, Arrays

Returns raw bytes - needs conversion

extractAsUint256

Integers only

No decimals, booleans, or negative numbers

extractAsBytesList

Arrays of strings/objects

Each element returned as bytes

Data Type Conversion Strategies

// For decimal numbers - store as integers with known precision const priceData = { price: 250 }; // Represents 2.50 with 2 decimal places const price = await jsonPrecompile.extractAsUint256(inputData, 'price'); const actualPrice = parseFloat(price.toString()) / 100; // Convert back to 2.50 // For boolean values - use 0/1 integers const statusData = { isActive: 1 }; // 1 = true, 0 = false const isActiveInt = await jsonPrecompile.extractAsUint256(inputData, 'isActive'); const isActive = isActiveInt === 1n; // For complex objects - extract as bytes and parse const userData = { user: { name: 'Alice', balance: 1000 } }; const userBytes = await jsonPrecompile.extractAsBytes(inputData, 'user'); const userString = ethers.toUtf8String(userBytes); const userObject = JSON.parse(userString);

Error Handling Utilities

Create comprehensive error handling for all extraction functions:

// Safe extraction for bytes data async function safeExtractBytes(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: string = ''): Promise<string> { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsBytes(inputData, key); return ethers.toUtf8String(result); } catch (error: any) { console.log(`Key "${key}" not found or invalid, using default value`); return defaultValue; } } // Safe extraction for uint256 data async function safeExtractUint256(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: bigint = 0n): Promise<bigint> { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsUint256(inputData, key); return result; } catch (error: any) { console.log(`Key "${key}" not found or not a valid integer, using default value`); return defaultValue; } } // Safe extraction for array data async function safeExtractBytesList(jsonPrecompile: ethers.Contract, data: any, key: string, defaultValue: string[] = []): Promise<string[]> { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsBytesList(inputData, key); return result.map((bytes: any) => ethers.toUtf8String(bytes)); } catch (error: any) { console.log(`Key "${key}" not found or not a valid array, using default value`); return defaultValue; } }

Step-by-Step Guide: Using the JSON Precompile

Extract String Data

// JSON data with string values const data = { name: 'Sei Token', symbol: 'SEI' }; const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); // Extract string using safe method const name = await safeExtractBytes(jsonPrecompile, data, 'name'); console.log('Name:', name); // Output: "Sei Token" // Or direct extraction with error handling try { const symbolBytes = await jsonPrecompile.extractAsBytes(inputData, 'symbol'); const symbol = ethers.toUtf8String(symbolBytes); console.log('Symbol:', symbol); // Output: "SEI" } catch (error) { console.error('Failed to extract symbol:', error.message); }

Extract Numeric Data

// JSON data with numbers (integers only) const data = { price: 42, volume: 1000000 }; const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); // Extract integer using safe method const price = await safeExtractUint256(jsonPrecompile, data, 'price'); console.log('Price:', price.toString()); // Output: "42" // Handle decimal numbers by using integer representation const decimalData = { rate: 275 }; // Represents 2.75 with 2 decimal places const rate = await safeExtractUint256(jsonPrecompile, decimalData, 'rate'); const actualRate = Number(rate) / 100; // Convert to 2.75 console.log('Rate:', actualRate);

Extract Array Data

// JSON data with array const data = { tokens: ['SEI', 'USDC', 'USDT'] }; const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); // Extract array using safe method const tokens = await safeExtractBytesList(jsonPrecompile, data, 'tokens'); console.log('Tokens:', tokens); // Output: ["SEI", "USDC", "USDT"] // Handle mixed arrays by extracting as bytes and parsing const mixedData = { values: [42, 'test', 100] }; const valuesBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(mixedData)), 'values'); const valuesString = ethers.toUtf8String(valuesBytes); const valuesArray = JSON.parse(valuesString); console.log('Mixed values:', valuesArray); // Output: [42, "test", 100]

Extract Nested Data

⚠️
Important: The JSON precompile does not support dot notation for nested objects. Instead, extract the parent object and parse it manually.
// For nested data, extract the parent object first const data = { user: { wallet: { balance: 1000, currency: 'SEI' } } }; // Method 1: Extract parent object and parse manually (recommended) const userBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(data)), 'user'); const userData = JSON.parse(ethers.toUtf8String(userBytes)); const balance = userData.wallet.balance; const currency = userData.wallet.currency; console.log('Balance:', balance); // Output: 1000 console.log('Currency:', currency); // Output: "SEI" // Method 2: Extract deeper nested object const walletBytes = await jsonPrecompile.extractAsBytes( ethers.toUtf8Bytes(JSON.stringify(data)), 'wallet' // Direct key, not dot notation ); const walletData = JSON.parse(ethers.toUtf8String(walletBytes)); console.log('Wallet data:', walletData); // Output: { balance: 1000, currency: "SEI" }

Complete Integration Example

Create a comprehensive JSON parsing application for mainnet:

json-precompile-mainnet.js
// json-precompile-mainnet.js const { ethers } = require('ethers'); const { JSON_PRECOMPILE_ABI, JSON_PRECOMPILE_ADDRESS } = require('@sei-js/precompiles'); require('dotenv').config(); // Safe extraction utilities async function safeExtractBytes(jsonPrecompile, data, key, defaultValue = '') { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsBytes(inputData, key); return ethers.toUtf8String(result); } catch (error) { console.log(` ⚠️ Key "${key}" not found, using default: "${defaultValue}"`); return defaultValue; } } async function safeExtractUint256(jsonPrecompile, data, key, defaultValue = 0n) { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsUint256(inputData, key); return result; } catch (error) { console.log(` ⚠️ Key "${key}" not found or invalid integer, using default: ${defaultValue}`); return defaultValue; } } async function safeExtractBytesList(jsonPrecompile, data, key, defaultValue = []) { try { const inputData = ethers.toUtf8Bytes(JSON.stringify(data)); const result = await jsonPrecompile.extractAsBytesList(inputData, key); return result.map((bytes) => ethers.toUtf8String(bytes)); } catch (error) { console.log(` ⚠️ Key "${key}" not found or invalid array, using default: [${defaultValue.join(', ')}]`); return defaultValue; } } async function main() { try { // Connect to Sei Mainnet console.log('🌐 Connecting to Sei Mainnet...'); const provider = new ethers.JsonRpcProvider('https://evm-rpc.sei-apis.com'); // Use private key from environment variables (secure approach) const privateKey = process.env.PRIVATE_KEY; if (!privateKey) { throw new Error('Please set PRIVATE_KEY in .env file'); } const wallet = new ethers.Wallet(privateKey, provider); console.log(`πŸ‘€ Wallet address: ${wallet.address}`); // Check balance const balance = await provider.getBalance(wallet.address); const balanceEther = ethers.formatEther(balance); console.log(`πŸ’° Wallet balance: ${balanceEther} SEI\n`); // Initialize JSON precompile contract const jsonPrecompile = new ethers.Contract(JSON_PRECOMPILE_ADDRESS, JSON_PRECOMPILE_ABI, wallet); console.log('πŸ“„ === Sei Mainnet JSON Precompile Demo ===\n'); // Complex sample data - simulating a real-world oracle response const oracleData = { market: { prices: { SEI: 275, // 2.75 USD (stored as integer with 2 decimal places) BTC: 5000000, // 50,000.00 USD ETH: 300000 // 3,000.00 USD }, volume: { daily: 1500000000, // $15M daily volume weekly: 8500000000 // $85M weekly volume } }, metadata: { timestamp: '2024-12-27T12:00:00Z', source: 'MainnetPriceOracle', version: '2.1.0', isLive: 1 // 1 = true, 0 = false }, assets: ['SEI', 'BTC', 'ETH', 'USDC'], validators: { active: 125, total: 200 } }; console.log('πŸ“‹ Input JSON Data:'); console.log(JSON.stringify(oracleData, null, 2)); console.log(); // 1. Extract string data with error handling console.log('1️⃣ Extracting String Data...'); // Extract metadata object first, then parse individual fields const metadataBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'metadata'); const metadata = JSON.parse(ethers.toUtf8String(metadataBytes)); console.log(` πŸ“Š Source: ${metadata.source}`); console.log(` πŸ”’ Version: ${metadata.version}`); console.log(` ⏰ Timestamp: ${metadata.timestamp}`); console.log(` 🟒 Is Live: ${metadata.isLive === 1 ? 'Yes' : 'No'}`); // Test missing key const missingKey = await safeExtractBytes(jsonPrecompile, oracleData, 'nonexistent', 'N/A'); console.log(` ❓ Missing Key: ${missingKey}`); // 2. Extract numeric data with proper object parsing console.log('\n2️⃣ Extracting Numeric Data...'); // Extract market object first const marketBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'market'); const market = JSON.parse(ethers.toUtf8String(marketBytes)); // Extract validators object const validatorsBytes = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(oracleData)), 'validators'); const validators = JSON.parse(ethers.toUtf8String(validatorsBytes)); // Convert integers back to decimals const seiPrice = market.prices.SEI / 100; // 2 decimal places const btcPrice = market.prices.BTC / 100; // 2 decimal places const isLive = metadata.isLive === 1; // Convert to boolean console.log(` πŸ’° SEI Price: ${seiPrice} (raw: ${market.prices.SEI})`); console.log(` β‚Ώ BTC Price: ${btcPrice.toLocaleString()} (raw: ${market.prices.BTC})`); console.log(` πŸ’Ή Daily Volume: ${market.volume.daily.toLocaleString()}`); console.log(` βœ… Active Validators: ${validators.active}`); console.log(` πŸ”΄ Is Live: ${isLive} (raw: ${metadata.isLive})`); // 3. Extract array data console.log('\n3️⃣ Extracting Array Data...'); const assets = await safeExtractBytesList(jsonPrecompile, oracleData, 'assets'); const invalidArray = await safeExtractBytesList(jsonPrecompile, oracleData, 'invalid.array', ['DEFAULT']); console.log(` πŸͺ™ Assets: [${assets.join(', ')}]`); console.log(` ❌ Invalid Array: [${invalidArray.join(', ')}]`); // 4. Extract complex nested objects console.log('\n4️⃣ Extracting Complex Nested Data...'); console.log(' πŸ“ˆ Market Data:'); console.log(` Prices: SEI=${market.prices.SEI / 100}, BTC=${market.prices.BTC / 100}, ETH=${market.prices.ETH / 100}`); console.log(` Volume: Daily=${market.volume.daily.toLocaleString()}, Weekly=${market.volume.weekly.toLocaleString()}`); console.log(` πŸ‘₯ Validator Data:`); console.log(` Active: ${validators.active}, Total: ${validators.total}`); // 5. Gas optimization demonstration console.log('\n5️⃣ Gas Usage Analysis...'); const gasEstimates = { small: 50000, // Simple key extraction medium: 80000, // Nested object extraction large: 120000 // Complex array/object processing }; console.log(' β›½ Recommended Gas Limits:'); console.log(` Simple extraction: ${gasEstimates.small.toLocaleString()} gas`); console.log(` Nested extraction: ${gasEstimates.medium.toLocaleString()} gas`); console.log(` Complex processing: ${gasEstimates.large.toLocaleString()} gas`); // 6. Performance test with corrected key extraction console.log('\n6️⃣ Performance & Error Testing...'); const testCases = [ { key: 'metadata', expected: 'object', description: 'metadata object' }, { key: 'market', expected: 'object', description: 'market data' }, { key: 'assets', expected: 'array', description: 'assets array' }, { key: 'nonexistent', expected: 'error', description: 'missing key' } ]; for (const testCase of testCases) { try { const start = Date.now(); const result = await safeExtractBytes(jsonPrecompile, oracleData, testCase.key); const duration = Date.now() - start; const preview = result.length > 50 ? result.substring(0, 47) + '...' : result; console.log(` βœ… ${testCase.description}: "${preview}" (${duration}ms)`); } catch (error) { console.log(` ❌ ${testCase.description}: ${error.message}`); } } console.log('\nπŸŽ‰ === JSON Precompile Demo Completed Successfully! ==='); } catch (error) { console.error('\n❌ Error:', error.message); console.log('\nπŸ”§ Troubleshooting tips:'); console.log('1. Ensure PRIVATE_KEY is set in .env file'); console.log('2. Verify sufficient SEI balance for gas fees'); console.log('3. Check network connectivity to Sei mainnet'); console.log('4. Validate JSON structure before processing'); process.exit(1); } } // Run the demo main().catch(console.error);

Running the Mainnet Example

  1. Create a new directory and initialize npm:
mkdir sei-json-mainnet cd sei-json-mainnet npm init -y
  1. Install dependencies:
npm install ethers @sei-js/precompiles@^2.1.2 dotenv
  1. Create a .env file:
PRIVATE_KEY=your_private_key_here
⚠️
Security Note: Never commit private keys to version control! The .env file should be added to your .gitignore.
  1. Create the demo file: Copy the complete integration example above into json-precompile-mainnet.js

  2. Ensure you have SEI tokens:

    • SEI for gas fees (small amounts needed for view functions)
  3. Run the script:

node json-precompile-mainnet.js

Expected Output

🌐 Connecting to Sei Mainnet... πŸ‘€ Wallet address: 0x742d35Cc6634C0532925a3b844Bc6789e065f3B πŸ’° Wallet balance: 15.5 SEI πŸ“„ === Sei Mainnet JSON Precompile Demo === πŸ“‹ Input JSON Data: { "market": { "prices": { "SEI": 275, "BTC": 5000000, "ETH": 300000 }, "volume": { "daily": 1500000000, "weekly": 8500000000 } }, "metadata": { "timestamp": "2024-12-27T12:00:00Z", "source": "MainnetPriceOracle", "version": "2.1.0", "isLive": 1 }, "assets": ["SEI", "BTC", "ETH", "USDC"], "validators": { "active": 125, "total": 200 } } 1️⃣ Extracting String Data... πŸ“Š Source: MainnetPriceOracle πŸ”’ Version: 2.1.0 ⏰ Timestamp: 2024-12-27T12:00:00Z ⚠️ Key "nonexistent.key" not found, using default: "N/A" ❓ Missing Key: N/A 2️⃣ Extracting Numeric Data... πŸ’° SEI Price: $2.75 (raw: 275) β‚Ώ BTC Price: $50,000 (raw: 5000000) πŸ’Ή Daily Volume: $1,500,000,000 βœ… Active Validators: 125 πŸ”΄ Is Live: true (raw: 1) 3️⃣ Extracting Array Data... πŸͺ™ Assets: [SEI, BTC, ETH, USDC] ⚠️ Key "invalid.array" not found or invalid array, using default: [DEFAULT] ❌ Invalid Array: [DEFAULT] 4️⃣ Extracting Complex Nested Data... πŸ“ˆ Market Data: Prices: SEI=$2.75, BTC=$50000, ETH=$3000 Volume: Daily=$1,500,000,000, Weekly=$8,500,000,000 5️⃣ Gas Usage Analysis... β›½ Recommended Gas Limits: Simple extraction: 50,000 gas Nested extraction: 80,000 gas Complex processing: 120,000 gas 6️⃣ Performance & Error Testing... βœ… metadata.source: "MainnetPriceOracle" (15ms) βœ… market.prices.SEI: "275" (12ms) βœ… assets: "[\"SEI\",\"BTC\",\"ETH\",\"USDC\"]" (18ms) ⚠️ Key "nonexistent.deeply.nested.key" not found, using default: "" βœ… nonexistent.deeply.nested.key: "" (8ms) πŸŽ‰ === JSON Precompile Demo Completed Successfully! ===

Advanced Usage Examples

Oracle Price Feed Integration

// Advanced oracle integration with comprehensive error handling class SeiPriceOracle { private jsonPrecompile: ethers.Contract; constructor(jsonPrecompile: ethers.Contract) { this.jsonPrecompile = jsonPrecompile; } async parsePriceFeed(oracleResponse: any): Promise<{ prices: Map<string, number>; timestamp: string; isValid: boolean; }> { try { // Extract timestamp const timestamp = await safeExtractBytes(this.jsonPrecompile, oracleResponse, 'metadata.timestamp'); // Extract validation flag const isValidRaw = await safeExtractUint256(this.jsonPrecompile, oracleResponse, 'metadata.isValid'); const isValid = isValidRaw === 1n; // Extract price data const prices = new Map<string, number>(); const assets = await safeExtractBytesList(this.jsonPrecompile, oracleResponse, 'assets'); for (const asset of assets) { const priceRaw = await safeExtractUint256(this.jsonPrecompile, oracleResponse, `prices.${asset}`); // Convert from integer with 6 decimal places prices.set(asset, Number(priceRaw) / 1000000); } return { prices, timestamp, isValid }; } catch (error) { console.error('Failed to parse oracle response:', error); return { prices: new Map(), timestamp: '', isValid: false }; } } }

Troubleshooting

Common Issues and Solutions

Key Not Found

// Comprehensive error handling for missing keys async function handleMissingKeys(jsonPrecompile: ethers.Contract, data: any) { const keys = ['required.key', 'optional.key', 'deeply.nested.key']; for (const key of keys) { try { const result = await jsonPrecompile.extractAsBytes(ethers.toUtf8Bytes(JSON.stringify(data)), key); console.log(`βœ… ${key}: ${ethers.toUtf8String(result)}`); } catch (error: any) { if (error.message.includes('key not found')) { console.log(`⚠️ Key "${key}" not found in JSON structure`); } else { console.log(`❌ Error extracting "${key}": ${error.message}`); } } } }

Error Code Reference

Error

Cause

Solution

key not found

Specified key doesn’t exist in JSON

Verify key path and JSON structure

invalid JSON format

Malformed JSON input

Use JSON.stringify() to ensure valid format

execution reverted

Gas limit too low

Increase gas limit based on data complexity

invalid integer

Non-numeric value for extractAsUint256

Ensure the value is a valid integer

out of gas

Insufficient gas for large JSON

Use calculateGasLimit() for dynamic estimation

Key Considerations and Tricks

  • Integers Only: extractAsUint256 only handles integers - no decimals, booleans, or negative numbers
  • Decimal Handling: Store decimal numbers as integers with known precision (e.g., 275 for 2.75 with 2 decimal places)
  • Boolean Values: Use 0/1 integers to represent false/true
  • Key Paths: Extract parent objects first, then parse manually - dot notation may not be supported
  • Arrays: Must use extractAsBytesList() for array data
  • Gas Costs: Large JSON objects require higher gas limits
  • Encoding: Always use UTF-8 encoding with ethers.toUtf8Bytes()
  • Error Handling: Always implement fallback values for production applications
Need Help? If you encounter issues not covered here, check the Sei Discord  or GitHub repository  for community support.
Last updated on