Using the Push model means retrieving data from on-chain contract storage. This requires the data to exist in the on-chain contract storage, uploaded by a publisher or node operator beforehand. Entangle maintains a node that continuously publishes updates for supported assets, but anyone is free to run their own node instance to ensure redundancy or customize their setup.
To utilize an update in your contract, you can read the value of the asset of interest from the UDFOracle contract deployed on each supported chain. For demonstration purposes, let's create a consumer contract whose logic depends on the NGL/USD price.
Step #1: Initialize hardhat project
First of all, let's create new project and initialize hardhat in it.
Step #2: Create PushConsumer contract and hardhat ignition script
Next, let's create our consumer contract. The consumer contract, referred to as the PushConsumer, will retrieve data stored in UDFOracle, which assumes the availability of verified updates pushed on-chain beforehand.
For this example, the contract will read the latest stored price of the NGL/USD asset and emit an event. To achieve this, we'll use the latestUpdate function, a free data retrieval method. For convenience, this function returns both the price and the timestamp in a single call. Providing both values ensures that users can verify the freshness of the data, which is critical for applications where price accuracy and timing are essential for decision-making.
./contracts/PushConsumer.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.28;interface IUDFOracle {/// @notice mapping of dataKey to the latest updatefunctionlatestUpdate(bytes32 dataKey) externalviewreturns (uint256,uint256);}contract PushConsumer {// @notice DataKey for NGL/USD assetbytes32constantpublic NGL_DATAKEY =0x4e474c2f55534400000000000000000000000000000000000000000000000000;// @notice PullOracle address on specific chain IUDFOracle public udfOracle;constructor(address_udfOracle) { udfOracle =IUDFOracle(_udfOracle); }eventPriceConsumed(bytes32 feedKey, uint256 price, uint256 timestamp);// @notice function that reads and uses update from PullOraclefunctionconsumePrice() public {// Read latest update from PullOracle contractuint256 latestPrice; uint256 latestTimestamp; (latestPrice, latestTimestamp) = udfOracle.latestUpdate(NGL_DATAKEY);// Emit an event with the latest updateemitPriceConsumed(NGL_DATAKEY, latestPrice, latestTimestamp); }}
Next, create a ignition script to help with deployment of new PushConsumer contract.
Optional: Spin up local development node forked from eth_sepolia.
To deploy your contract on testnet, you'll need ETH eth_sepolia testnet tokens. In case you don't have ETH on eth_sepolia, you can spin up a local development fork of eth_sepolia by using anvil node like this:
To validate that we have our price, we will write an example backend script that send transaction to execute PushConsumer.consumePrice. Let's create a separate directory scripts and our new script consumePrice.ts to do just that.
$mkdirscripts&&touch./scripts/consumePrice.ts
./scripts/consumePrice.ts
import { PushConsumer,} from"../typechain-types";import { ethers } from"hardhat";// PushConsumer address we got from the deploymentconstPushConsumerAddress="0x3f724844973A8e5F669499D366943A676F6EF7CE";asyncfunctionmain() { constpushConsumer=awaitethers.getContractAt("PushConsumer", PushConsumerAddress ) asPushConsumer;let tx =awaitpushConsumer.consumePrice();awaittx.wait();console.log("sent PushConsumer.consumePrice tx",tx.hash);}main().then(() =>process.exit(0)).catch(error => {console.error(error);process.exit(1); });
Execute this script to send transaction to the blockchain