Configuring Custom Oracle

Learn how to seamlessly set up and integrate a new Oracle for your User Application (UA).
This tutorial provides a step-by-step guide on setting a new Oracle for your User Application (UA).

Understanding Oracle Configuration

In LayerZero, Oracle configurations help enable smooth messaging across chain pathways. A chain pathway represents a connected route that utilizes both the Oracle and Relayer to facilitate message routing between blockchains.
  1. 1.
    Consistent Oracle Configuration: It's essential to ensure that the same Oracle provider is present on both the source and destination chains. This uniformity guarantees that messages can be reliably sent and received in both directions on the pathway.
  2. 2.
    Payment and Delivery Logic: If you're paying Oracle A on the source chain, you'd expect Oracle A to also handle the delivery on the destination chain. Hence, if Oracle A is available on both chains, it can be used in both directions. On the other hand, if Oracle A is only present on one chain, you'd need to opt for an alternative that's supported on both chain directions.
Remember, the objective is to ensure that the Oracle setup supports the chain pathways, as they are the conduits for message routing. This is vital for efficient, error-free cross-chain communication.


You should have an LZApp to start with that's already working with default settings. While we use OmniCounter in this tutorial, any app that inherits LZApp.sol (including the OFT and ONFT standards) can be used.
In order to set a new Oracle, all a user will need to do is call the setConfig function on Chain A and Chain B.
Below is a simple example for how to set your Oracle, using the Ethereum Goerli and Optimism Goerli Testnets.

Example: Setting an Oracle using Ethereum Goerli and Optimism Goerli Testnets

1. Deploying OmniCounter

After deploying OmniCounter on both Goerli and OP-Goerli, ensure that:
  • You've correctly called setTrustedRemote.
  • The incrementCounter function works by default on both contracts.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
pragma abicoder v2;
import "";
/// @title A LayerZero example sending a cross chain message from a source chain to a destination chain to increment a counter
contract OmniCounter is NonblockingLzApp {
bytes public constant PAYLOAD = "\x01\x02\x03\x04";
uint public counter;
constructor(address _lzEndpoint) NonblockingLzApp(_lzEndpoint) {}
function _nonblockingLzReceive(uint16, bytes memory, uint64, bytes memory) internal override {
counter += 1;
function estimateFee(uint16 _dstChainId, bool _useZro, bytes calldata _adapterParams) public view returns (uint nativeFee, uint zroFee) {
return lzEndpoint.estimateFees(_dstChainId, address(this), PAYLOAD, _useZro, _adapterParams);
function incrementCounter(uint16 _dstChainId) public payable {
_lzSend(_dstChainId, PAYLOAD, payable(msg.sender), address(0x0), bytes(""), msg.value);

2. Setting a New Oracle

To modify your UA contracts, you'll need to invoke the setConfig function. This can be done directly from a verified block explorer or using scripting tools. In this tutorial, we'll demonstrate using Remix.
Here's how to set the Oracle for the Goerli OmniCounter using the Goerli TSS Oracle address, 0x36ebea3941907c438ca8ca2b1065deef21ccdaed:
1 let config = ethers.utils.defaultAbiCoder.encode(
2 ["address"],
3 ["0x36ebea3941907c438ca8ca2b1065deef21ccdaed"] // oracleAddress
4 )
5 await lzEndpoint.setConfig(
6 0, // default library version
7 10132, // dstChainId
9 0x00000000000000000000000036ebea3941907c438ca8ca2b1065deef21ccdaed // config
10 )
This process should be repeated on both the source and destination contracts. Ensure you adjust the _dstChainId and oracleAddress based on the contract's location. For instance, on OP Goerli, use the OP Goerli TSS Oracle Address and set the destination chain to 10121 for Goerli ETH.
In Remix, passing these arguments will show the following:

3. Checking Oracle Configuration

To ensure your Oracle setup is correctly configured:
Navigate to the Block Explorer: Go to your chain's Endpoint Address on the designated block explorer.
Access the Contract Details: Click on "Read Contract". Here, you should see an option labeled defaultReceiveLibraryAddress. Select it to navigate to LayerZero's UltraLightNode.
Query the UltraLightNode Contract:
  • getConfig: This returns the current configuration of your UA Contract.
  • defaultAppConfig: This gives the default configuration based on the latest library version. To use this, you'll need to provide the _dstChainId parameter.
View the Oracle Parameter
For the defaultAppConfig, simply pass the _dstChainId and observe the returned oracle parameter.
For the getConfig, pass the _dstChainId, your UA Contract Address, and set the constant CONFIG_TYPE_ORACLE to 6.
Compare Oracle Addresses:
At the time of writing this tutorial, TSS is the default testnet Oracle. Therefore, if you haven't made any changes, both getConfig and defaultAppConfig should return identical Oracle addresses.
However, if you've opted for a different Oracle from the current default, the two queries should return different Oracle addresses.
Understanding Query Results: You might notice a difference in how the queries present the Oracle:
  • defaultAppConfig: This query returns the Oracle as an address.
  • getConfig: In contrast, this displays the Oracle as a bytes value.
However, don't be alarmed by this variation. If the only discrepancy between the two results is the presence of '0' padding, then both queries are referencing the same Oracle.

4. Testing Message Delivery

Validate your Oracle setup by calling incrementCounter. The protocol should now reflect your custom Oracle configuration and be capable to send messages in both directions.
Congratulations on your successful configuration! 🥳
A successful oracle configuration will not impact message delivery.


Encountering a FAILED message status on LayerZero Scan? This likely points to a misconfiguration of the oracle address on either one or both contracts.
A failed oracle configuration will impact message delivery.
  • Ensure you're using the local oracle address (i.e., the same chain as your UA) when invoking setConfig.
  • Double-check the dstChainId you're passing.
For further customization, refer to the UA Custom Configuration documentation.