Skip to content

diadata-org/Spectra-interoperability

Repository files navigation

Table of Contents

Spectra

Diadata utilizes Hyperlane to transfer or provide data to destination chains. Currently, the supported testnets are sepolia.

This repository contains the Spectra smart contract, Hyperlane configuration, and setup instructions.

Components

1. OracleTrigger Smart Contract

The OracleTrigger smart contract is deployed on the Lasernet chain. It retrieves asset prices from the Metadata Smart Contract and propagates them to the destination chain using Spectra.

2. OracleUpdateRecipient Smart Contract

The OracleUpdateRecipient smart contract is deployed on the destination chain. It receives and processes oracle price updates from Lasernet using either a push-based or request-based mechanism.

3. OracleService

The OracleService operates at defined intervals to fetch updated values from the Lasernet. It then performs transactions with the OracleTrigger, providing the chain ID and symbol. This process facilitates bridging the data to the destination chain via Hyperlane

Spectra Flow

Spectra Flow

Push Based Oracle

The Push-Based Oracle system enables contracts to receive real-time updates based on predefined criteria such as fixed intervals, specific price deviations, or a combination of both. This design provides flexibility and efficiency for decentralized applications needing accurate and timely data.

Usage

Method 1: Separate Oracle Contract

In this method, any contract that needs data can read directly from the Oracle contract. The Oracle maintains updates as a mapping, where each key maps to a Data struct containing the latest timestamp and value.

The updates mapping is a key-value store where:

  • Key: A unique identifier, typically a string, representing the asset or data type (e.g., DIA/USD, BTC/USD).
  • Value: A Data struct containing:
    • key: The identifier of the data entry (redundant for reference but useful for integrity checks).
    • timestamp: The timestamp of the latest update.
    • value: The most recent value associated with the key.

Example

contract PriceConsumer {
    Oracle public oracle;
    
    constructor(address _oracle) {
        oracle = Oracle(_oracle);
    }
    
    function getLatestPrice(string memory _key) public view returns (uint128) {
        return oracle.updates(_key).value;
    }
}

Example Oracle contract

Method 2: Direct Delivery to Contracts

In this method, updates are pushed to the receiving contract via a callback mechanism. The receiving contract must implement a specific interface and define a callback function to handle the incoming data.

  • Your contract must implement IMessageRecipient and IInterchainSecurityModule.
  • The Oracle invokes the handle callback function in the receiving contract to deliver updates. The data payload is decoded and can be stored or processed as needed.
function handle(
    uint32 _origin,
    bytes32 _sender,
    bytes calldata _data
) external payable virtual override {
    (string memory key, uint128 timestamp, uint128 value) = abi.decode(
        _data,
        (string, uint128, uint128)
    );
    // Store or process the received data
    updates[key] = Data({key: key, timestamp: timestamp, value: value});
    emit ReceivedMessage(key, timestamp, value);
}

Access Oracle

  • To access oracle, use the updates(pair_name) function. Here’s how you can interact with it:
    • Use the full pair name (e.g., DIA/USD) as the pair_name
    • Query the contract using Etherscan's "Read" section.

Dia Oracle Etherscan

  • The response contains the following data fields:

    • Key: The identifier of the asset pair (e.g., DIA/USD).
    • Timestamp: The time of the latest price update.
    • Value: The most recent price of the asset.

Request Based Oracle

Request Based Oracle enables the creation of requests for asset prices on a source blockchain. These requests are sent through a mailbox on the current chain and ultimately delivered to the DIA chain, which retrieves and delivers the required price data.

How It Works

1. Request Creation

A request can be made from the source chain for an asset symbol whose price is required. These requests pass through the chain's mailbox, and the respective mailbox addresses are as follows:

MailBox

Reciepient

ISM Address

2. Request Body Format

The request body is formatted as follows in JavaScript:

const key =  "WBTC/USD"; // Assuming key is an address or a bytes32 value

const requestBody = abiCoder.encode(
    ["string"],  // Types of the parameters
    [key]        // Values to encode
);

The request body is formatted as follows in solidity:

    bytes memory requestBody = abi.encode("WBTC/USD");

Message Delivery Process

Once a request is created, it is transmitted to the Hyperlane mailbox. The message is then relayed to the OracleRequestRecipient contract , where the price data is fetched from the Oracle Metadata Contract.

Response and Callback

Upon receiving the request, the OracleRequestRecipient Smart Contract on Lasernet initiates a transaction to deliver the message back to the end contract on the source chain. The recipient contract on the source chain must implement the IMessageRecipient interface, which includes a handle function that will receive the price quotation.

Reciepient

Example Request Based Oracle

Request Oracle Flow

Request Flow

 function handle(
        uint32 _origin,
        bytes32 _sender,
        bytes calldata _data
    ) external payable virtual override {
        (string memory  key, uint128 timestamp, uint128 value) = abi.decode(
            _data,
            (string, uint128, uint128)
        );
        receivedData = Data({key: key, timestamp: timestamp, value: value});

        updates[key] = receivedData;
        emit ReceivedMessage(key,timestamp,value);
        lastSender = _sender;
        lastData = _data;
    }

Example of Requesting Price from Lasernet

To create a contract for a price request from the lasernet, you need to implement two key interfaces: IMessageRecipient and ISpecifiesInterchainSecurityModule.

IMessageRecipient

This interface allows the contract to receive messages back from the DIA chain, likely for handling the price quote response.

ISpecifiesInterchainSecurityModule

This interface enables setting the ISM (Interchain Security Module) contract address, which plays a crucial role in securing the interchain communication process.

Once the price request is sent to the mailbox, it will return a response message to the handle function of IMessageRecipient.

Here’s an example contract implementation:

// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.0;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

import {IMessageRecipient} from "../interfaces/IMessageRecipient.sol";
import {IInterchainSecurityModule, ISpecifiesInterchainSecurityModule} from "../interfaces/IInterchainSecurityModule.sol";
import {IMailbox} from "../interfaces/IMailbox.sol";
import {IPostDispatchHook} from "../interfaces/hooks/IPostDispatchHook.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";


using TypeCasts for address;

contract RequestBasedOracleExample is
    Ownable,
    IMessageRecipient,
    ISpecifiesInterchainSecurityModule
{
    IInterchainSecurityModule public interchainSecurityModule;
    bytes32 public lastSender;
    bytes public lastData;

    address public lastCaller;
    string public lastCallMessage;

    struct Data {
        string key;
        uint128 timestamp;
        uint128 value;
    }
    Data public receivedData;

    mapping(string => Data) public updates;


    event ReceivedMessage(
        string key,
        uint128 timestamp,
        uint128 value
    );


        function request(
        IMailbox _mailbox,
        address reciever,
        uint32 _destinationDomain,
        bytes calldata _messageBody

    ) external payable returns (bytes32 messageId) {
        // bytes memory messageBody = abi.encode("aa", 111111, 11);

        return _mailbox.dispatch{value: msg.value}(
            _destinationDomain,
            reciever.addressToBytes32(),
            _messageBody,
            bytes(""),
            IPostDispatchHook(0x0000000000000000000000000000000000000000)
        );
    }


    event ReceivedCall(address indexed caller, uint256 amount, string message);

    function handle(
        uint32 _origin,
        bytes32 _sender,
        bytes calldata _data
    ) external payable virtual override {
        (string memory  key, uint128 timestamp, uint128 value) = abi.decode(
            _data,
            (string, uint128, uint128)
        );
        receivedData = Data({key: key, timestamp: timestamp, value: value});

        updates[key] = receivedData;
        emit ReceivedMessage(key,timestamp,value);
        lastSender = _sender;
        lastData = _data;
    }
 

    function setInterchainSecurityModule(address _ism) external onlyOwner {
        interchainSecurityModule = IInterchainSecurityModule(_ism);
    }
}

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published