Files
smom-dbis-138/docs/operations/integrations/CCIP_MESSAGE_FORMAT.md
defiQUG 1fb7266469 Add Oracle Aggregator and CCIP Integration
- Introduced Aggregator.sol for Chainlink-compatible oracle functionality, including round-based updates and access control.
- Added OracleWithCCIP.sol to extend Aggregator with CCIP cross-chain messaging capabilities.
- Created .gitmodules to include OpenZeppelin contracts as a submodule.
- Developed a comprehensive deployment guide in NEXT_STEPS_COMPLETE_GUIDE.md for Phase 2 and smart contract deployment.
- Implemented Vite configuration for the orchestration portal, supporting both Vue and React frameworks.
- Added server-side logic for the Multi-Cloud Orchestration Portal, including API endpoints for environment management and monitoring.
- Created scripts for resource import and usage validation across non-US regions.
- Added tests for CCIP error handling and integration to ensure robust functionality.
- Included various new files and directories for the orchestration portal and deployment scripts.
2025-12-12 14:57:48 -08:00

3.2 KiB

CCIP Message Format

Overview

This document describes the message format used for CCIP cross-chain oracle updates.

Message Structure

CCIP messages contain encoded oracle data in the following format:

struct OracleMessage {
    uint256 answer;      // Oracle price/answer (scaled by 10^8)
    uint256 roundId;     // Round ID for this update
    uint256 timestamp;   // Unix timestamp of the update
}

Encoding

Messages are encoded using ABI encoding:

bytes memory messageData = abi.encode(answer, roundId, timestamp);

Decoding

On the receiving chain, messages are decoded:

(uint256 answer, uint256 roundId, uint256 timestamp) = abi.decode(message.data, (uint256, uint256, uint256));

Example

Sending Message

uint256 price = 25000000000; // $250.00 (scaled by 10^8)
uint256 roundId = 12345;
uint256 timestamp = block.timestamp;

bytes memory messageData = abi.encode(price, roundId, timestamp);

CCIPSender sender = CCIPSender(senderAddress);
uint256 fee = sender.calculateFee(targetChainSelector, messageData);
sender.sendOracleUpdate{value: fee}(targetChainSelector, receiverAddress, messageData);

Receiving Message

function ccipReceive(
    IRouterClient.Any2EVMMessage calldata message
) external onlyRouter {
    (uint256 answer, uint256 roundId, uint256 timestamp) = abi.decode(
        message.data,
        (uint256, uint256, uint256)
    );
    
    // Update oracle
    updateOracle(answer, roundId, timestamp);
}

Data Types

Answer (uint256)

  • Oracle price/value
  • Scaled by 10^8 (8 decimal places)
  • Example: $250.00 = 25000000000

Round ID (uint256)

  • Sequential round identifier
  • Increments with each update
  • Used for ordering and deduplication

Timestamp (uint256)

  • Unix timestamp (seconds since epoch)
  • When the price was observed
  • Used for staleness checks

Message Size

Typical message size: ~96 bytes (3 * 32 bytes)

Maximum recommended size: 256 bytes

Validation

Before processing, validate:

  1. Message ID: Check for replay attacks
  2. Source Chain: Verify source chain selector
  3. Sender: Verify sender address is authorized
  4. Timestamp: Check timestamp is recent
  5. Round ID: Ensure round ID is sequential

Error Handling

Invalid Format

If message cannot be decoded:

try abi.decode(message.data, (uint256, uint256, uint256)) returns (uint256, uint256, uint256) {
    // Process message
} catch {
    // Log error and reject message
    emit InvalidMessageFormat(message.messageId);
    return;
}

Stale Data

Check timestamp is recent:

require(block.timestamp - timestamp < MAX_STALENESS, "Data too stale");

Invalid Round ID

Ensure round ID is sequential:

require(roundId > lastRoundId, "Invalid round ID");

Security Considerations

  1. Replay Protection: Track processed message IDs
  2. Source Validation: Verify source chain and sender
  3. Data Validation: Validate all fields before processing
  4. Access Control: Only authorized contracts can receive messages

References