Using Push model means you get data from on-chain contract storage, assuming someone uploaded the update beforehand. In UDF, we maintain a node for constantly publishing updates on assets that we support, but anyone is welcome to run their own instance of our node following this guide.
To utilize the update in your contract, you would read the value on asset of interest from PullOracle contract deployed on each chain we support, you can view addresses of this contract on this page.
To demonstrate an example, let's create an example consumer contract whose logic depends on 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. For the sake of example, it will read the latest stored price of NGL/USD asset and emit an event.
./contracts/PushConsumer.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.19;interface IPullOracle {/// @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 IPullOracle public pullOracle;constructor(address_pullOracle) { pullOracle =IPullOracle(_pullOracle); }eventPriceConsumed(bytes32,uint256,uint256);// @notice function that reads and uses update from PullOraclefunctionconsumePrice() public {// Read latest update from PullOracle contractuint256 latestPrice; uint256 latestTimestamp; (latestPrice, latestTimestamp) = pullOracle.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.
./ignition/modules/PushConsumer.ts
import { buildModule } from"@nomicfoundation/hardhat-ignition/modules";// PullOralce address on Eth Sepolia networkconstPullOracleAddress="0x0b2d8Ef1D9104c4Df5C89F00B645Ce8bAa56DeB5"constPushConsumerModule=buildModule("PushConsumerModule", (m) => {constconsumer=m.contract("PushConsumer", [PullOracleAddress]);return { consumer };});exportdefault PushConsumerModule;
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