Git: d00d391 (2026-01-29) RPC: checking... Block: ... Load: ...
Blockchain Stores Open Data Open Code Open Prompts Simulator AI Speaking

FloodBoy Development

Complete FloodBoy Webapp Specification

Create a Webapp using Viem.js to display sensor data from smart contracts on JIBCHAIN L1.

Connection Details:
- Chain ID: 8899
- RPC URL: https://rpc-l1.jibchain.net
- Block Explorer: https://exp.jibchain.net

Contract Addresses:
- Factory Contract: 0x63bB41b79b5aAc6e98C7b35Dcb0fE941b85Ba5Bb
- FloodBoy001 Store: 0xCd3Ec17ddFDa24f8F97131fa0FDf20e7cbd1A8Bb
- FloodBoy016 Store: 0x0994Bc66b2863f8D58C8185b1ed6147895632812
- Universal Signer: 0xcB0e58b011924e049ce4b4D62298Edf43dFF0BDd (authorized for all stores)

Final UI Design (Latest Sensor Data Card):

Header Section:
- Title: "Latest Sensor Data"
- Store Nickname: Display nickname from factory contract (e.g., "FloodBoy001", "FloodBoy016")
- Store Description: Display description from factory contract (e.g., "Northern Thailand Flood Monitor", "FloodBoy016")
- Current Block: "Current Block: 5944625" (display current block number)
- Last Updated timestamp: "Last Updated: 7:25:08 PM"
- Store address (truncated): "0xCd3Ec17d...d1A8Bb" or "0x0994Bc66...632812" with external link icon

Chart Section (Full Width):
- Display a full-width line chart with toggle controls
- Toggle between "Water Depth" and "Battery Voltage" views
- Chart titles: "Water Depth Over Time" / "Battery Voltage Over Time"
- Y-axis: Water depth in meters (scaled from x10000) OR Battery voltage in volts (scaled from x100)
- X-axis: Timestamp (last 24 hours or available data range)
- Chart colors: Blue (#3B82F6) for water depth, Green (#10B981) for voltage
- Full container width with proper responsive scaling
- Show data points and connect with smooth lines
- Apply data smoothing for better chart visualization (moving averages, interpolation)
- Include hover tooltips showing exact values and timestamps
- Toggle buttons above chart: [Water Depth] [Battery Voltage]
- Active toggle button highlighted with matching chart color
- Responsive design that works on mobile devices
- If no historical data available, show "No historical data available" message

Data Table:
Columns: Metric | Current | Min | Max
Rows (CORRECT FORMAT - use proper capitalization and sample counts):
- Battery Voltage: [voltage] V | [min] V | [max] V
- Installation Height: [height] m | [height] m | [height] m  
- Water Depth ([X] samples): [depth] m | [min] m | [max] m

Real Examples (FloodBoy001 vs FloodBoy016):
FloodBoy001: Water Depth (9 samples): 0.4454 m | 0.4450 m | 0.4460 m
FloodBoy016: Water Depth (3 samples): 0.5433 m | 0.5400 m | 0.5490 m

WRONG FORMAT (DO NOT USE):
❌ battery voltage: [voltage] V (lowercase field name)
❌ installation height: 302.000 m (incorrect unit conversion from x10000 - should be ~3.02 m)
❌ water depth: 44.540 m (wrong conversion from x10000 - should be ~0.44-0.54 m)
❌ water depth count: [X] count (should be "Water Depth ([X] samples)" format instead)

Footer:
- Last Updated: 7/22/2025, 7:25:08 PM
- Store Owner: 0x943E41e4cc22f971284ae957A380D3DbeA1Dc481 (truncated with link)
- Deployed Block: #5944625 (with block explorer link)
- Sensor Count: 1 authorized sensor

Data Processing Requirements:
Unit Scaling:
- x100 → divide by 100 (3 decimal places for voltage) - Example: 1291 → 12.910 V
- x1000 → divide by 1000 (3 decimal places) 
- x10000 → divide by 10000 (2-4 decimal places for meters) - Examples: 30200 → 3.02 m, 2700 → 0.27 m
- Extract base unit from unit string (e.g., "V" from "V x100", "m" from "m x10000")
- Format timestamps in human-readable format (MM/dd/yyyy, h:mm:ss AM/PM)
- Use appropriate decimal precision: voltage (3 decimals), meters (2 decimals for readability)

Visual Design:
- Clean white card with rounded corners and subtle shadow
- Store nickname prominently displayed as main heading
- Store description as subtitle below nickname
- Alternating row colors (white/light gray)
- Green status indicator for current block
- Truncated addresses with external link icons
- Sample count badges where applicable
- Responsive table layout
- Store metadata section with owner and deployment information

Features:
- Display store nickname and description from factory contract
- Full-width chart with toggle between Water Depth and Battery Voltage views
- Toggle buttons with active state highlighting (matching chart colors)
- Loading indicators during data fetching
- Error handling with user-friendly messages
- Always use the universal signer (0xcB0e58b011924e049ce4b4D62298Edf43dFF0BDd) for data retrieval
- Show "No data" if no records exist
- Responsive chart scaling for all screen sizes
- Store metadata integration (nickname, description, owner, deployed block)

Historical Data Requirements:
- Use RecordStored events to build both water depth and battery voltage timelines
- Handle RPC limits with pagination (max 2000 blocks per request)
- Cache event data to reduce blockchain calls
- Dynamically find water depth AND battery voltage field indexes using getAllFields()
- Process both data types: water depth (x10000 scaling) and battery voltage (x100 scaling)
- Sort events by timestamp for proper chart ordering
- Support toggle between datasets without refetching data
- Include error handling for missing or corrupted event data
- Show loading state while fetching historical events
- Display "No historical data" message if events array is empty

Required ABIs for Webapp

Factory Contract ABI (Key Functions):
[
  {
    "name": "getStoreInfo",
    "inputs": [{"name": "store", "type": "address"}],
    "outputs": [
      {"name": "nickname", "type": "string"},
      {"name": "owner", "type": "address"},
      {"name": "authorizedSensorCount", "type": "uint256"},
      {"name": "deployedBlock", "type": "uint128"},
      {"name": "description", "type": "string"}
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

CatLabSecureSensorStore ABI (Key Functions):
[
  {
    "name": "getAllFields",
    "outputs": [{
      "components": [
        {"name": "name", "type": "string"},
        {"name": "unit", "type": "string"},
        {"name": "dtype", "type": "string"}
      ],
      "type": "tuple[]"
    }],
    "stateMutability": "view",
    "type": "function"
  },
  {
    "name": "getLatestRecord",
    "inputs": [{"name": "sensor", "type": "address"}],
    "outputs": [
      {"name": "", "type": "uint256"},
      {"name": "", "type": "int256[]"}
    ],
    "stateMutability": "view",
    "type": "function"
  }
]

Note: Full ABIs are provided in /abis/ directory. 
Include complete ABIs in your implementation.

Implementation Example

import { createPublicClient, http } from 'viem';
import FactoryABI from '@/abis/CatLabFactory.json';
import StoreABI from '@/abis/CatLabSecureSensorStore.abi.json';

// JIBCHAIN L1 Configuration
const jibchain = {
  id: 8899,
  name: 'JIBCHAIN L1',
  rpcUrls: {
    default: { http: ['https://rpc-l1.jibchain.net'] }
  }
};

const client = createPublicClient({
  chain: jibchain,
  transport: http()
});

// Constants
const FACTORY_ADDRESS = '0x63bB41b79b5aAc6e98C7b35Dcb0fE941b85Ba5Bb';
const FLOODBOY001_STORE = '0xCd3Ec17ddFDa24f8F97131fa0FDf20e7cbd1A8Bb';
const FLOODBOY016_STORE = '0x0994Bc66b2863f8D58C8185b1ed6147895632812';
const UNIVERSAL_SIGNER = '0xcB0e58b011924e049ce4b4D62298Edf43dFF0BDd';

// Step 1: Get store information from factory contract
const [nickname, owner, sensorCount, deployedBlock, description] = await client.readContract({
  address: FACTORY_ADDRESS,
  abi: FactoryABI,
  functionName: 'getStoreInfo',
  args: [FLOODBOY016_STORE] // Using FloodBoy016 as example
});

// Display store metadata in UI
console.log('Store Info:', { nickname, description, owner, sensorCount, deployedBlock });

// Step 2: Get field configurations
const fields = await client.readContract({
  address: FLOODBOY016_STORE,
  abi: StoreABI,
  functionName: 'getAllFields'
});

// Step 3: Get latest sensor data (using universal signer)
const [timestamp, values] = await client.readContract({
  address: FLOODBOY016_STORE,
  abi: StoreABI,
  functionName: 'getLatestRecord',
  args: [UNIVERSAL_SIGNER]
});

// Step 4: Get historical data using event logs
// RecordStored event signature from CatLabSecureSensorStore:
// event RecordStored(address indexed sensor, uint256 timestamp, int256[] values)

// Method 1: Get recent events (recommended for charts)
const currentBlockNumber = await client.getBlockNumber();
const fromBlock = currentBlockNumber - BigInt(28800); // ~24 hours (assuming 3sec blocks)

const historicalEvents = await client.getContractEvents({
  address: FLOODBOY016_STORE,
  abi: StoreABI,
  eventName: 'RecordStored',
  fromBlock: fromBlock,
  toBlock: 'latest',
  args: {
    sensor: UNIVERSAL_SIGNER // Filter by sensor address
  }
});

// Process chart data for toggle functionality
const waterDepthIndex = fields.findIndex(field => 
  field.name.toLowerCase().includes('water_depth') && !field.name.includes('min') && !field.name.includes('max')
);
const batteryVoltageIndex = fields.findIndex(field => 
  field.name.toLowerCase().includes('battery_voltage') && !field.name.includes('min') && !field.name.includes('max')
);

const chartData = historicalEvents.map(event => ({
  timestamp: Number(event.args.timestamp) * 1000, // Convert to milliseconds
  waterDepth: waterDepthIndex >= 0 ? Number(event.args.values[waterDepthIndex]) / 10000 : null,
  batteryVoltage: batteryVoltageIndex >= 0 ? Number(event.args.values[batteryVoltageIndex]) / 100 : null,
  blockNumber: Number(event.blockNumber)
})).sort((a, b) => a.timestamp - b.timestamp);

// Chart toggle state management
const [activeChart, setActiveChart] = useState('waterDepth'); // 'waterDepth' or 'batteryVoltage'

// Data processing function with CORRECT x10000 conversion - MOST CRITICAL FUNCTION
function processValue(value, unit) {
  const baseUnit = unit.replace(/ x\d+/, '');
  if (unit.includes('x100')) return (Number(value) / 100).toFixed(3) + ' ' + baseUnit; // Voltage: 3 decimals
  if (unit.includes('x1000')) return (Number(value) / 1000).toFixed(3) + ' ' + baseUnit;
  
  // ⚠️ CRITICAL: x10000 MUST divide by 10000, NOT 100!
  if (unit.includes('x10000')) {
    // CORRECT: divide by 10000
    return (Number(value) / 10000).toFixed(4) + ' ' + baseUnit;
    // WRONG examples that AI often generates:
    // return (Number(value) / 100).toFixed(3) + ' ' + baseUnit; // ❌ Wrong divisor!
    // return Number(value).toFixed(3) + ' ' + baseUnit; // ❌ No division!
  }
  
  return value + ' ' + unit;
}

// Field name formatting - CRITICAL FORMATTING RULES
function formatFieldName(fieldName) {
  // Convert snake_case to Title Case
  return fieldName
    .split('_')
    .map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
    .join(' ');
}

// Examples of CORRECT field name formatting:
// "battery_voltage" → "Battery Voltage" (NOT "battery voltage")
// "installation_height" → "Installation Height" (NOT "installation height") 
// "water_depth" → "Water Depth" (NOT "water depth")
// "water_depth_count" → append sample count as "Water Depth ([X] samples)" (NOT "Water Depth Count: [X] count")
// Note: [X] is dynamic - FloodBoy001: 9 samples, FloodBoy016: 3 samples, etc.

// Examples of correct processing:
// Battery voltage: 1291 (x100) → 12.91 V (showing as 12.910 V with 3 decimals)
// Installation height: 30200 (x10000) → 3.02 m
// Water depth: 2700 (x10000) → 0.27 m

Network Info:
Chain ID: 8899 (JIBCHAIN L1)
RPC URL: https://rpc-l1.jibchain.net
Block Explorer: https://exp.jibchain.net
FloodBoy016 Store: 0x0994Bc66b2863f8D58C8185b1ed6147895632812

** CRITICAL UNIT CONVERSION - MOST COMMON ERROR **
⚠️ x10000 fields MUST divide by 10000, NOT display raw values!

Unit Conversion Rules:
- x100 → divide by 100 (example: 1384 → 13.840 V) ✅
- x1000 → divide by 1000 
- x10000 → divide by 10000 ⚠️ CRITICAL:
  - Raw value 30200 → 3.0200 m ✅ (divide by 10000)
  - Raw value 4383 → 0.4383 m ✅ (divide by 10000)
  - NOT 302.000 m ❌ (wrong: displaying raw/100 instead of raw/10000)
  - NOT 43.830 m ❌ (wrong: displaying raw/100 instead of raw/10000)

Field Name Formatting:
- ALWAYS use Title Case: "Battery Voltage", "Installation Height", "Water Depth"
- NEVER use lowercase: "battery voltage", "installation height", "water depth"
- For count fields: Use "Water Depth ([X] samples)" format, NOT "Water Depth Count: [X] count"
- Sample count X is dynamic per store (FloodBoy001: 9, FloodBoy016: 3, etc.)

Common Mistakes to Avoid:
❌ "installation height: 302.000 m" (WRONG: lowercase + x10000 displayed as raw/100)
✅ "Installation Height: 3.0200 m" (CORRECT: Title Case + raw÷10000)

❌ "Water Depth: 43.830 m" (WRONG: x10000 displayed as raw/100) 
✅ "Water Depth: 0.4383 m" (CORRECT: raw 4383÷10000)

❌ "water depth count: [X] count" (wrong: separate count field)
✅ "Water Depth ([X] samples): [0.44-0.54] m" (correct: embedded sample count)

Generic Pattern for All Stores:
- Sample count varies by store: FloodBoy001 (9 samples), FloodBoy016 (3 samples)
- Water depth values vary: ~0.44-0.46m (001) vs ~0.54-0.55m (016)
- Installation height varies: 3.0200m (001) vs 3.0000m (016)
- Always use dynamic sample count from actual data

Data Smoothing Requirements:
- Apply smoothing algorithms when rendering charts to reduce noise and improve readability
- Use moving averages (window size: 3-5 data points) for real-time data visualization
- Implement interpolation for gaps in historical data
- Provide option to toggle between raw data and smoothed data views
- Maintain original data precision while smoothing display values

Real-World Sensor Data Examples:
The webapp should handle data volumes from various production FloodBoy sensors:

Sensor Data Records Overview (varies by store):
- FloodBoy001: 7,379 total → 1,569 grouped (30-min intervals)
- FloodBoy016: 3,907 total → 917 grouped (30-min intervals)
- Data Grouping Options: 30 minutes, 1 hour, 6 hours, 24 hours
- Static data display (no real-time updates needed)

Sample Sensor Output Formats:

FloodBoy001 (0xCd3Ec17d...d1A8Bb):
| Metric | Current | Min | Max |
|--------|---------|-----|-----|
| Battery Voltage | 13.780 V | 13.540 V | 14.130 V |
| Installation Height | 3.0200 m | 3.0200 m | 3.0200 m |
| Water Depth (9 samples) | 0.4454 m | 0.4450 m | 0.4460 m |

FloodBoy016 (0x0994Bc66...632812):
| Metric | Current | Min | Max |
|--------|---------|-----|-----|
| Battery Voltage | 14.140 V | 14.140 V | 14.150 V |
| Installation Height | 3.0000 m | 3.0000 m | 3.0000 m |
| Water Depth (3 samples) | 0.5433 m | 0.5400 m | 0.5490 m |

UI Controls Required:
- 📊 Data Table view toggle
- 📈 Charts view toggle  
- ⛶ Maximize/fullscreen option
- Show/hide fields selector
- Data grouping interval selector (30 min default)
- Simple static data display (no refresh controls needed)

Chart Data Processing:
- Handle variable dataset sizes efficiently (FloodBoy001: 7K+ records, FloodBoy016: 4K+ records)
- Group data by time intervals to reduce chart complexity (30min intervals typical)
- Apply smoothing to grouped data for cleaner visualization
- Show dynamic sample counts for grouped metrics (varies by store: "9 samples", "3 samples", etc.)
- Maintain responsive performance across all store sizes
- Adapt to different water depth ranges (001: ~0.44m, 016: ~0.54m) and sample frequencies

For direct blockchain access commands (cast/curl), see the dedicated Open Data page: /opendata

Network Info

Chain ID: 8899 (JIBCHAIN L1)

RPC URL: https://rpc-l1.jibchain.net

Block Explorer: https://exp.jibchain.net

FloodBoy001 Store: 0xCd3Ec17ddFDa24f8F97131fa0FDf20e7cbd1A8Bb

Git: d00d391 (2026-01-29)
Factory (JIBCHAIN): 0x63bB...a5Bb