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.
- 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.
- 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.
Prerequisites
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
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 "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);
}
}
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
:
let config = ethers.utils.defaultAbiCoder.encode(
['address'],
['0x36ebea3941907c438ca8ca2b1065deef21ccdaed'], // oracleAddress
);
await lzEndpoint.setConfig(
0, // default library version
10132, // dstChainId
6, // CONFIG_TYPE_ORACLE
0x00000000000000000000000036ebea3941907c438ca8ca2b1065deef21ccdaed, // config
);
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:
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.
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.
Troubleshooting
Encountering a FAILED
message status on LayerZero Scan? This likely points to a misconfiguration of the oracle address on either one or both contracts.
- 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.