LogoLogo
  • Entangle
    • Overview
    • Security Audits
  • Universal Interoperability Protocol
    • Overview
    • Architecture
      • Scalability and Network Stability
        • L2 Utility Blockchains
        • Transmitter Groups
      • Security and Consensus Mechanism
      • Finality
      • Execution Latency
      • Compatibility and Interoperability
    • Developer Guides
      • Getting Started
      • Solidity
        • Simple Abstract Messenger Example
        • Deploying Your Custom EVM Protocol
        • Bridging Tokens with UIP
        • Become an EVM Transmitter
      • Solana
        • Simple Abstract Messenger Example
        • Deploying Your Custom Solana Protocol
        • Become a Solana Transmitter
      • Calculate Cross-Chain Transaction Cost
      • Customizable Message Transmission Options
      • How to Debug Sent Messages
      • SDK Setup
      • Revenue Sharing for Transmitters
      • How to Become a Super Transmitter
    • Endpoints
  • Universal Data Feeds
    • Overview
    • Architecture
      • Data Delivery Methods
        • Pull Model
        • Push Model
      • Oracle Contract & User Interaction
    • Developer Guides
      • Custom Data Feeds
      • Fetch Data via Pull Model (PAYG)
        • EVM Smart Contracts
        • Solana Smart Contracts
      • Fetch Data via Pull Model (Subscriptions)
        • EVM Smart Contracts
        • Solana Smart Contracts
      • Fetch Data via Push Model
        • EVM Smart Contracts
        • Solana Smart Contracts
    • User Guides
      • Accessing Feeds
      • Subscribe to a Data Feed
      • Check Subscription
      • Manage Subscription
      • Renew Subscription
    • Data Endpoints
  • Universal Token Standard
    • Overview
    • Architecture
      • Fee Components
    • Developer Guides
      • Manual Deploy
        • Token Deployment Types
        • Create a Custom Token
        • Factory Blueprint Deployment
        • Examples
          • Initial Setup
          • UTS Connector
            • Mint & Burn Connector Scheme
            • Lock & Unlock Connector Scheme
            • Connector Over Native Currency
          • UTS Token
            • Simple Token
            • Token with Messages
      • Bridge SDK
      • Token Verification
      • Fees Calculation & Gas Estimation Logic
      • Estimations
    • User Guides
      • Launch Universal Token
      • Create a Liquidity Pool
      • Expand Existing Token
      • Transfer Liquidity to Connector
      • Bridging
    • Contract Addresses
  • Entangle Interoperable Blockchain
    • Overview
    • Architecture
    • Developer Guides
      • Set up a Validator Node
      • Delegating to Validators
      • Undelegating from Validators
      • Supported Accounts
  • More
    • Media Kit
    • FAQ
    • Report an Issue
    • Become a Partner
Powered by GitBook
On this page
  • Step 1: Initialize new hardhat project
  • Step 2: Write a contract that verifies an update in the transaction
  • Step 3: Deploy PullVerifierSub
  • Step 4: Create a Pull Subscribtion
  • Step 5: Execute transaction

Was this helpful?

Export as PDF
  1. Universal Data Feeds
  2. Developer Guides
  3. Fetch Data via Pull Model (Subscriptions)

EVM Smart Contracts

In the Pull model, consumers include verification data about the most recent update directly in their transaction. This data is fetched from the oracle and verified within the same transaction before being used.

This scheme comes with the following properties:

  1. Consumers always use the latest update in their transaction, so the price used in the transaction is as close to real time as protocol update latency.

  2. The cost for maintaining update relevance on-chain relies on end-consumers. This allows the oracle protocol to have much larger set of assets.

This guide will help developers who want to use UDF Pull model (with subscription) within their contracts. It uses Hardhat for contract compilation, although similar methods apply with other frameworks.

Step 1: Initialize new hardhat project

In the first step, we are establishing a basic solidity development setup.

yarn init -y
# yarn initialization flow
yarn add --dev hardhat
yarn hardhat init
# Hardhat initialization flow(select TypeScript project)

Step 2: Write a contract that verifies an update in the transaction

Now create a new PullVerifierSub contract, which utilizes the pullMarketplace.verifyAsSubscriber function to verify the update information provided through calldata. The update contains oracle votes along with their signatures.

This contract should already be in subscription so it can have access for verification. The subscription can be purchased from the marketplace and the consumer contract needs to be added as an authorized address for the subscription.

Once the PullVerifierSub contract verifies the update against these inputs, it stores them on-chain, to later be accessed. If the verification fails (e.g., due to mismatched data, invalid signatures, or other inconsistencies) the contract reverts the entire transaction, ensuring that only valid data is accepted.

./contractrs/PullVerifierSub.sol
// SPDX-License-Identifier: BSL1.1
pragma solidity ^0.8.20;

interface IPullMarketplace  {
        struct PriceUpdate {
        uint256 price;
        uint256 timestamp;
    }
    function verifyAsSubscriber(
        bytes calldata updateData,
        bytes32 feedKey
    )
        external
        returns (PriceUpdate memory);
}
contract PullVerifierSub {
    IPullMarketplace public pullMarketplace;
    uint public latestPrice;
    uint public latestTimestamp;
    constructor(address _pullMarketplace){
        pullMarketplace = IPullMarketplace(_pullMarketplace);
    }
    // @notice function that verifies feed as subscriber from provided update through UDFOracle and stores update data
    function verify(bytes calldata updateData, bytes32 feedKey) external{
        IPullMarketplace.PriceUpdate memory priceUpdate = pullMarketplace.verifyAsSubscriber(updateData,feedKey);
        latestPrice = priceUpdate.price;
        latestTimestamp = priceUpdate.timestamp;
    }
}

Next, create an ignition script to help with deployment of the new PullVerifierSub contract.

./ignition/modules/PullVerifierSub.ts
import { buildModule } from "@nomicfoundation/hardhat-ignition/modules";

// PullMarketplace address on Eth Sepolia network
const PullMarketplaceAddress = "0xC6670092056e7bf56c3149FDE52A187b3e9f2e63";

const PullVerifierSubModule = buildModule("PullVerifierSubModule", (m) => {
	const consumer = m.contract("PullVerifierSub", [PullMarketplaceAddress ]);

	return { consumer };
});

export default PullVerifierSubModule;

Step 3: Deploy PullVerifierSub

Now let's modify the hardhat configuration file to add the eth_sepolia network.

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

const config: HardhatUserConfig = {
  solidity: "0.8.28",
  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 can deploy the PullVerifierSub contract by running the command below.

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

Deploying [ PullVerifierSubModule ]

Batch #1
  Executed PullVerifierSubModule#PullVerifierSub

[ PullVerifierSubModule] successfully deployed 🚀

Deployed Addresses

PullVerifierSubModule#PullVerifierSub - 0x7c0ad1Bb6c6dD48CA70C190B400aD56DeF61F43C

Step 4: Create a Pull Subscribtion

Now we should add the deployed PullVerifierSub contract as an allowed address to our subscription, so that it can verify Pull updates.

Step 5: Execute transaction

First, add the UDF SDK library. We will use it to fetch the update using the REST API and encode it for on-chain usage.

yarn add --dev @entangle-labs/udf-sdk

To validate that we have our price, we will write an example backend script that send transaction to execute PullVerifierSub.verifyAsSubscriber. Let's create separate directory scripts and our new script verifyAsSub.ts to do just that.

mkdir scripts && touch ./scripts/verifyAsSub.ts

Change the PullVerifierAddress to the address you got from deployment and execute the script.

./scripts/verifyAsSub.ts
import {
  PullVerifierSub,
} from "../typechain-types";
import { ethers } from "hardhat";
import { UdfSdk } from '@entangle-labs/udf-sdk';

// PullVerifierSub address we got from the deployment
const PullVerifierAddress = "0x7c0ad1Bb6c6dD48CA70C190B400aD56DeF61F43C";

async function main() {  
  const pullVerifier = await ethers.getContractAt(
    "PullVerifierSub",
    PullVerifierAddress 
  ) as PullVerifierSub;

  // Fetch the update data from finalized-data-snap
  const sdk = new UdfSdk();
  const updateData = await sdk.getCallData(["BTC/USD"]);
  const feedKey = ethers.encodeBytes32String("BTC/USD");
  let tx = await pullVerifier.verify(updateData,feedKey);
  await tx.wait();

  console.log("sent PullVerifierSub.verify tx", tx.hash);
  let price = await pullVerifier.latestPrice();
  let timestamp = await pullVerifier.latestTimestamp();
  console.log("price:", price );
  console.log("timestamp:", timestamp );
}

main()
  .then(() => process.exit(0))
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

Now let's run the script using the command below to ensure it's working.

yarn hardhat run ./scripts/verifyAsSub.ts --network eth_sepolia
Expected Results
sent PullVerifierSub.verify tx 0x7d202c67c1d3f04606743a406979eb4dd4a435be37eb0490c502895c28ecc0ec
price: 84087090417798546633122n
timestamp: 1743492361n

You can use cast to examine the transaction call trace and logs. The emitted event shows the latest update that we verified. Note that gas usage may vary depending on the result and the version of the library in use.

cast run 0x7d202c67c1d3f04606743a406979eb4dd4a435be37eb0490c502895c28ecc0ec -r https://ethereum-sepolia-rpc.publicnode.com
Expected Results
Executing previous transactions from the block.
Traces:
  [99231] PullVerifierSub::verify(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000)
	├─ [82339] ERC1967Proxy::fallback(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000)
	│   ├─ [77350] PullMarketplace::verifyAsSubscriber(0x55444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b, 0x4254432f55534400000000000000000000000000000000000000000000000000) [delegatecall]
	│   │   ├─ [60369] 0xc0931aEE1064BD5245fEe76A2d740eab8436621e::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b0000000000000000000000000000000000000000000000) [staticcall]
	│   │   │   ├─ [55380] 0xB2F863B68d85b198DDe2fE7da1D8baFdCFf199c0::40366475(00000000000000000000000000000000000000000000000000000000000000404254432f5553440000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001a955444646014254432f55534400000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000011ce4ebc2338797454e80000000000000000000000000000000000000000000000000000000067eba567c157813327ab23012c0c51d803a60fdeb2db3460e19840849b99b5e4996ee90853a0d1ad5e5be5304cb76990ffd1e04680f7f2f85680ac62f529b5614031062e1c0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567cd0d22ac7bc0820ebcbc11b087c059c2d36b207d020a3987b21b56b11c2ca0982796fc953c1ebf14434ee0dffc225d87e12ca4506f07f0971e4fcd72bcbab2241c0000000000000000000000000000000000000000000011ce6ed6df09a64f08420000000000000000000000000000000000000000000000000000000067eba567c82350a2e16b70134ec057193550fd49b18e793604b1e4743fc01c2cae01f9b7631cdfa9033e2c414cf29451f916c482316673fd8be956a1d5ea5dc1791115c01b0000000000000000000000000000000000000000000000) [delegatecall]
	│   │   │   │   ├─ [3000] PRECOMPILES::ecrecover(0x0a68e8365be00d0cd98a1171d18af4e3e1566fd09a59d61fe23743c2dbe50c12, 28, 87450987175619729345224437904190378668378471047311994984055329008011973683464, 37826109101770063310129024824192616189507182989479589014137121493233374922286) [staticcall]
	│   │   │   │   │   └─ ← [Return] 0x0000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f
	│   │   │   │   ├─ [8142] ERC1967Proxy::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [staticcall]
	│   │   │   │   │   ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e310000000000000000000000000000000000000000000000000000000000000000000000006733d110a59dc160b5c8093066a7f5103885196f) [delegatecall]
	│   │   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   ├─ [3000] PRECOMPILES::ecrecover(0x14e205399af2fdf39cd5ba0d6d33d50ffbb0007a622f772edc16663e7de9cd57, 28, 92747342280930951330331743016924826142985862405571157083665267571049381011608, 17906971417906977260169118529732908076796855624976136117544900421598137987620) [staticcall]
	│   │   │   │   │   └─ ← [Return] 0x000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a
	│   │   │   │   ├─ [3642] ERC1967Proxy::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [staticcall]
	│   │   │   │   │   ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e31000000000000000000000000000000000000000000000000000000000000000000000000be524616e96bb4b62cce8034ab6bea8f2505b55a) [delegatecall]
	│   │   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   ├─ [3000] PRECOMPILES::ecrecover(0xca613c8b1a20618b010d94a62a700c6e09917c2a8778a82c881f2be28d25a2b3, 27, 90524965894879087420297465183903263448359497350110207873450031945407891896759, 44829987373565001565186606953511927342046667061064660196284399196770407617984) [staticcall]
	│   │   │   │   │   └─ ← [Return] 0x00000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a
	│   │   │   │   ├─ [3642] ERC1967Proxy::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [staticcall]
	│   │   │   │   │   ├─ [3246] 0xA7a8eAAA131dc4D2f70636F5F8796e640B119926::55b5190b(7564662d76312e3100000000000000000000000000000000000000000000000000000000000000000000000093b502d3eb45b9eae948f8fca01e64d9c0ba538a) [delegatecall]
	│   │   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000000000000000000000001
	│   │   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567
	│   │   │   └─ ← [Return] 0x0000000000000000000000000000000000000000000011ce5ecc567d2b3ab1a20000000000000000000000000000000000000000000000000000000067eba567
	│   │   ├─ emit VerifyAsSubscriberCalled(verifier: PullVerifierSub: [0x9Cd59BEbE8daBbb4a79739619a99DF890498305e], feedKey: 0x4254432f55534400000000000000000000000000000000000000000000000000)
	│   │   └─ ← [Return] PriceUpdate({ price: 84087090417798546633122 [8.408e22], timestamp: 1743496551 [1.743e9] })
	│   └─ ← [Return] PriceUpdate({ price: 84087090417798546633122 [8.408e22], timestamp: 1743496551 [1.743e9] })
	└─ ← [Stop]


Transaction successfully executed.
Gas used: 125591
PreviousFetch Data via Pull Model (Subscriptions)NextSolana Smart Contracts

Last updated 1 month ago

Was this helpful?

To deploy your contract on testnet, you'll need ETH eth_sepolia testnet tokens. You can obtain funds from public faucets (, , , , ).

To do that, go to the BTC/USD and select Pull Mode. Then add the address of the deployed PullVerifierSub contract under "Which addresses can read the contract?" and click Subscribe.

You may read more about subscribing to feeds at .

Option 1
Option 2
Option 3
Option 4
Option 5
pair page
Subscribe to a Data Feed