L2 Services

Registering and Using Oracle Network Services on L2 Chains

Registry Contract

This contract allows you to manage new chain and Orakl Network services.

L2 Chain Management

Please contact Orakl Network after you propose a new chain. We will support you to set things up.

  1. Propose New Chain and Pay proposeFee

function proposeNewChain(
    uint256 _chainID,
    string memory _jsonRpc,
    address _endpoint
) external payable {
    if (msg.value < proposeFee) {
        revert NotEnoughFee();
    }
    if (chainRegistry[_chainID].owner != address(0)) {
        revert ChainExisted();
    }
    pendingProposal[_chainID].jsonRpc = _jsonRpc;
    pendingProposal[_chainID].endpoint = _endpoint;
    pendingProposal[_chainID].owner = msg.sender;
    emit ChainProposed(msg.sender, _chainID);
}
  1. Edit Chain Info

function editChainInfo(
    uint256 _chainID,
    string memory _jsonRpc,
    address _endpoint
) external payable onlyConfirmedChainOwner(_chainID) {
    if (msg.value < proposeFee) {
        revert NotEnoughFee();
    }

    chainRegistry[_chainID].jsonRpc = _jsonRpc;
    chainRegistry[_chainID].endpoint = _endpoint;
    emit ChainEdited(_jsonRpc, _endpoint);
}

L2 Data Feeds Management

  1. Add Data Feed

function addAggregator(
    uint256 chainID,
    address l1Aggregator,
    address l2Aggregator
) external onlyConfirmedChainOwner(chainID) {
    AggregatorPair memory newAggregatorPair = AggregatorPair({
        aggregatorID: aggregatorCount[chainID]++,
        l1Aggregator: l1Aggregator,
        l2Aggregator: l2Aggregator
    });
    aggregators[chainID].push(newAggregatorPair);
    emit AggregatorAdded(chainID, newAggregatorPair.aggregatorID);
}
  1. Remove Data Feed

function removeAggregator(
    uint256 chainID,
    uint256 aggregatorID
) external onlyConfirmedChainOwner(chainID) {
    AggregatorPair[] storage aggregatorInfo = aggregators[chainID];
    for (uint256 i = 0; i < aggregatorInfo.length; i++) {
        if (aggregatorInfo[i].aggregatorID == aggregatorID) {
            // Move the last item to the current index to be removed
            aggregatorInfo[i] = aggregatorInfo[aggregatorInfo.length - 1];

            // Remove the last item from the list
            aggregatorInfo.pop();

            emit AggregatorRemoved(chainID, aggregatorID);
            break; // Exit the loop once item is found and removed
        }
    }
}

Account and Payment Functions

Before you can make a request from L2 chain, you need to create and deposit to your account.

  1. Create Account

function createAccount(uint256 _chainId) external onlyConfirmedChain(_chainId) {
    Account storage newAccount = accounts[nextAccountId];
    newAccount.accId = nextAccountId;
    newAccount.chainId = _chainId;
    newAccount.owner = msg.sender;
    newAccount.balance = 0;
    emit AccountCreated(nextAccountId, _chainId, msg.sender);
    nextAccountId++;
}
  1. Deposit Funds Into the Account

function deposit(uint256 _accId) public payable {
    accounts[_accId].balance += msg.value;
    emit BalanceIncreased(_accId, msg.value);
}
  1. Add Consumer Into Account

function addConsumer(
    uint256 _accId,
    address _consumerAddress
) external onlyAccountOwner(_accId) {
    Account storage account = accounts[_accId];
    require(account.consumerCount < MAX_CONSUMER, "Max consumers reached");

    account.consumers.push(_consumerAddress);
    account.consumerCount++;

    emit ConsumerAdded(_accId, _consumerAddress);
}
  1. Remove Consumer From Account

function removeConsumer(
    uint256 _accId,
    address _consumerAddress
) external onlyAccountOwner(_accId) {
    require(_accId > 0 && _accId < nextAccountId, "Account does not exist");
    Account storage account = accounts[_accId];

    for (uint8 i = 0; i < account.consumerCount; i++) {
        address[] storage consumers = account.consumers;
        if (consumers[i] == _consumerAddress) {
            account.consumerCount--;
            consumers[i] = consumers[account.consumerCount];
            consumers.pop();
            emit ConsumerRemoved(_accId, _consumerAddress);
            return;
        }
    }
}

The consumer address to add or remove to this account is on L2 chain, not mainnet.

Endpoint Contracts

L2Endpoint

You need to deploy this contract on your chain; it acts as a coordinator for VRF and Request-Response.

  1. Add L2 Data Feed

function addAggregator(address _newAggregator) external onlyOwner {
    if (sAggregators[_newAggregator]) revert InvalidAggregator(_newAggregator);
    sAggregators[_newAggregator] = true;
    sAggregatorCount += 1;
    emit AggregatorAdded(_newAggregator);
}
  1. Remove L2 Data Feed

function removeAggregator(address _aggregator) external onlyOwner {
    if (!sAggregators[_aggregator]) revert InvalidAggregator(_aggregator);
    delete sAggregators[_aggregator];
    sAggregatorCount -= 1;
    emit AggregatorRemoved(_aggregator);
}
  1. Add L2 Data Feed Reporter

function addSubmitter(address _newSubmitter) external onlyOwner {
    if (sSubmitters[_newSubmitter]) revert InvalidSubmitter(_newSubmitter);
    sSubmitters[_newSubmitter] = true;
    sSubmitterCount += 1;
    emit SubmitterAdded(_newSubmitter);
}
  1. Add L2 Data Feed Reporter

function removeSubmitter(address _submitter) external onlyOwner {
        if (!sSubmitters[_submitter]) revert InvalidSubmitter(_submitter);
        delete sSubmitters[_submitter];
        sSubmitterCount -= 1;
        emit SubmitterRemoved(_submitter);
    }
  1. Request Random Words From L2

function requestRandomWords(
    bytes32 keyHash,
    uint64 accId,
    uint32 callbackGasLimit,
    uint32 numWords
) external nonReentrant returns (uint256) {
    sNonce++;
    (uint256 requestId, uint256 preSeed) = computeRequestId(keyHash, msg.sender, accId, sNonce);
    sRequestDetail[requestId] = RequestInfo({
        owner: msg.sender,
        callbackGasLimit: callbackGasLimit
    });
    emit RandomWordsRequested(
        keyHash,
        requestId,
        preSeed,
        accId,
        callbackGasLimit,
        numWords,
        msg.sender
    );

    return requestId;
}
  1. Request Data From L2

function requestData(
    Orakl.Request memory req,
    uint32 callbackGasLimit,
    uint64 accId,
    uint8 numSubmission
) external nonReentrant returns (uint256) {
    sNonce++;
    uint256 requestId = computeRequestId(msg.sender, accId, sNonce);
    sRequestDetail[requestId] = RequestInfo({
        owner: msg.sender,
        callbackGasLimit: callbackGasLimit
    });
    emit DataRequested(
        requestId,
        req.id,
        accId,
        callbackGasLimit,
        msg.sender,
        numSubmission,
        req
    );

    return requestId;
}

How to Integrate Data Feed, VRF and Request-Response on L2

Data Feed

The use of Aggregator and AggregatorProxy smart contracts on L2 is the same as on the mainnet. Please refer to our Data Feed mock consumer contract.

VRF

Refer to L2 VRF mock consumer contract.

  1. Inherit from the VRFConsumerBase Contract

contract L2VRFConsumerMock is VRFConsumerBase
  1. Specify l2Endpoint in the Constructor

constructor(address l2Endpoint) VRFConsumerBase(l2Endpoint) {
    sOwner = msg.sender;
    L2ENDPOINT = IL2Endpoint(l2Endpoint);
}
  1. Implement the requestRandomWords Function

 function requestRandomWords(
    bytes32 keyHash,
    uint64 accId,
    uint32 callbackGasLimit,
    uint32 numWords
) public onlyOwner returns (uint256 requestId) {
    requestId = L2ENDPOINT.requestRandomWords(keyHash, accId, callbackGasLimit, numWords);
}
  1. Implement the fulfillRandomWords Function

function fulfillRandomWords(
    uint256 /* requestId */,
    uint256[] memory randomWords
) internal override {
    sRandomWord = (randomWords[0] % 50) + 1;
}

Request-Response

Refer to L2 Request-Response mock consumer contract.

  1. Inherit from the Base Contract for Request Data Types

contract L2RequestResponseConsumerMock is
    RequestResponseConsumerFulfillUint128,
    RequestResponseConsumerFulfillInt256,
    RequestResponseConsumerFulfillBool,
    RequestResponseConsumerFulfillString,
    RequestResponseConsumerFulfillBytes32,
    RequestResponseConsumerFulfillBytes
  1. Specify l2Endpoint in the Constructor

constructor(address l2Endpoint) RequestResponseConsumerBase(l2Endpoint) {
    sOwner = msg.sender;
    L2ENDPOINT = IL2Endpoint(l2Endpoint);
}
  1. Implement requestData* Function

//request for uint128
function requestDataUint128(
    uint64 accId,
    uint32 callbackGasLimit,
    uint8 numSubmission
) public onlyOwner returns (uint256 requestId) {
    bytes32 jobId = keccak256(abi.encodePacked("uint128"));
    Orakl.Request memory req = buildRequest(jobId);
    //change here for your expected data
    req.add(
        "get",
        "https://min-api.cryptocompare.com/data/pricemultifull?fsyms=KLAY&tsyms=USD"
    );
    req.add("path", "RAW,KLAY,USD,PRICE");
    req.add("pow10", "8");
    requestId = L2ENDPOINT.requestData(req, callbackGasLimit, accId, numSubmission);
}
  1. Implement the fulfillDataRequest Function

function fulfillDataRequest(uint256 /*requestId*/, uint128 response) internal override {
    sResponseUint128 = response;
}

Last updated