Files
smom-dbis-138/docs/operations/integrations/CCIP_MESSAGE_FORMAT.md

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