Fetch Data via Push model

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.

$ mkdir push-consumer && cd push-consumer
$ yarn init
# Yarn initialization
$ yarn add --dev hardhat
$ yarn hardhat init
# Hardhat initialization

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: UNLICENSED
pragma solidity ^0.8.19;

interface IPullOracle {
    /// @notice mapping of dataKey to the latest update
    function latestUpdate(bytes32 dataKey) external view returns (uint256,uint256);
}

contract PushConsumer {
    // @notice DataKey for NGL/USD asset
    bytes32 constant public NGL_DATAKEY = 0x4e474c2f55534400000000000000000000000000000000000000000000000000;

    // @notice PullOracle address on specific chain
    IPullOracle public pullOracle;

    constructor(address _pullOracle) {
        pullOracle = IPullOracle(_pullOracle);
    }

    event PriceConsumed(bytes32,uint256,uint256);

    // @notice function that reads and uses update from PullOracle
    function consumePrice() public {

        // Read latest update from PullOracle contract
        uint256 latestPrice; uint256 latestTimestamp;
        (latestPrice, latestTimestamp) = pullOracle.latestUpdate(NGL_DATAKEY);

        // Emit an event with the latest update
        emit PriceConsumed(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 network
const PullOracleAddress = "0x0b2d8Ef1D9104c4Df5C89F00B645Ce8bAa56DeB5"

const PushConsumerModule = buildModule("PushConsumerModule", (m) => {
  const consumer = m.contract("PushConsumer", [PullOracleAddress]);

  return { consumer };
});

export default 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:

$ anvil --fork-url https://ethereum-sepolia-rpc.publicnode.com

Copy one of private keys from stdout to use it for deployment.


Step #3: Deploy PushConsumer

Now let's modify hardhat.config.ts to add eth_sepolia network.

./hardhat.config.ts
import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";

const config: HardhatUserConfig = {
  solidity: "0.8.27",
  networks: {
    eth_sepolia: {
        chainId: 11155111,
        // or http://127.0.0.1:8545, if you forked it locally
        url: "https://ethereum-sepolia-rpc.publicnode.com",
        accounts: [ "0x" ], // TODO: Set deployer private key
    },
  },
};

export default config;

Now we're ready to deploy PushConsumer contract.

$ yarn hardhat ignition deploy ./ignition/modules/PushConsumer.ts --network eth_sepolia
 Confirm deploy to network eth_sepolia (11155111)? … yes
Compiled 1 Solidity file successfully (evm target: paris).
Hardhat Ignition 🚀

Deploying [ PushConsumerModule ]

Batch #1
  Executed PushConsumerModule#PushConsumer

[ PushConsumerModule ] successfully deployed 🚀

Deployed Addresses

PushConsumerModule#PushConsumer - 0x3f724844973A8e5F669499D366943A676F6EF7CE

Step #4: Execute PushConsumer.consumePrice transaction

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.

$ mkdir scripts && touch ./scripts/consumePrice.ts
./scripts/consumePrice.ts
import {
  PushConsumer,
} from "../typechain-types";
import { ethers } from "hardhat";

// PushConsumer address we got from the deployment
const PushConsumerAddress = "0x3f724844973A8e5F669499D366943A676F6EF7CE";

async function main() {  
  const pushConsumer = await ethers.getContractAt(
    "PushConsumer",
    PushConsumerAddress
  ) as PushConsumer;

  let tx = await pushConsumer.consumePrice();
  await tx.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

$ yarn hardhat run ./scripts/consumePrice.ts --network eth_sepolia
sent PushConsumer.consumePrice tx 0x69b5f309ac88d8511f3b393abcda76bb08b287c50d7c38cf88031cd9751ea9f2

You can examine transaction closely using cast to verify that event was emitted with expected values.

$ cast run 0x69b5f309ac88d8511f3b393abcda76bb08b287c50d7c38cf88031cd9751ea9f2 -r http://127.0.0.1:8545
Executing previous transactions from the block.
Traces:
  [17132] 0x3f724844973A8e5F669499D366943A676F6EF7CE::cd00fbb8()
    ├─ [9559] 0x0b2d8Ef1D9104c4Df5C89F00B645Ce8bAa56DeB5::f0f0fce9(4e474c2f55534400000000000000000000000000000000000000000000000000) [staticcall]
    │   ├─ [4663] 0x6d88F714E488bFB551022eC77933dC0469527b46::f0f0fce9(4e474c2f55534400000000000000000000000000000000000000000000000000) [delegatecall]
    │   │   └─ ← 0x00000000000000000000000000000000000000000000000000e182a86074a39f0000000000000000000000000000000000000000000000000000000066eae7ab
    │   └─ ← 0x00000000000000000000000000000000000000000000000000e182a86074a39f0000000000000000000000000000000000000000000000000000000066eae7ab
    ├─  emit topic 0: 0xca844ada88fd18cb2bf8845797fa4b85183b1fbc60f9cd9b06db90df5b4e074b
    │           data: 0x4e474c2f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e182a86074a39f0000000000000000000000000000000000000000000000000000000066eae7ab
    └─  ()


Transaction successfully executed.
Gas used: 38184

Last updated