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).
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.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.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.
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 "https://github.com/LayerZero-Labs/solidity-examples/blob/e43908440cefdcbc93cd8e0ea863326c4bd904eb/contracts/lzApp/NonblockingLzApp.sol";
/// @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);
}
}
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
8 6, // CONFIG_TYPE_ORACLE
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:

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.
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.
Last modified 9d ago