# 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.

```bash
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.&#x20;

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.

{% code title="./contractrs/PullVerifierSub.sol" %}

```solidity
// 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;
    }
}

```

{% endcode %}

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

{% code title="./ignition/modules/PullVerifierSub.ts" %}

```typescript
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;
```

{% endcode %}

## Step 3: Deploy PullVerifierSub

{% hint style="info" %}
To deploy your contract on testnet, you'll need ETH eth\_sepolia testnet tokens. You can obtain funds from public faucets ([Option 1](https://faucet.polygon.technology/), [Option 2](https://sepolia-faucet.pk910.de/), [Option 3](https://faucet.sepolia.mantle.xyz/), [Option 4](https://www.alchemy.com/faucets), [Option 5](https://cloud.google.com/application/web3/faucet/ethereum/sepolia)).&#x20;
{% endhint %}

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

{% code title="./hardhat.config.ts" %}

```typescript
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;
```

{% endcode %}

Now we can deploy the `PullVerifierSub` contract by running the command below.

```bash
yarn hardhat ignition deploy ./ignition/modules/PullVerifierSub.ts --network eth_sepolia
```

<details>

<summary>Expected Result</summary>

<pre><code><strong>✔ Confirm deploy to network eth_sepolia (11155111)? … yes
</strong>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

</code></pre>

</details>

## 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.&#x20;

To do that, go to the BTC/USD [pair page](https://udf.entangle.fi/pair/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 [Subscribe to a Data Feed](https://docs.entangle.fi/universal-data-feeds/user-guides/subscribe-to-a-data-feed).

<figure><img src="https://4040807501-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F5AajewgFWO9EkufRORqL%2Fuploads%2FiPue3h7wF5vknf271VcR%2Fimage.png?alt=media&#x26;token=89e5cc75-688f-44d5-8b31-bf4791c2ee30" alt=""><figcaption></figcaption></figure>

## 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.

<pre class="language-bash"><code class="lang-bash"><strong>yarn add --dev @entangle-labs/udf-sdk
</strong></code></pre>

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.

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

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

{% code title="./scripts/verifyAsSub.ts" %}

```typescript
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);
  });
```

{% endcode %}

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

```bash
yarn hardhat run ./scripts/verifyAsSub.ts --network eth_sepolia
```

<details>

<summary>Expected Results</summary>

<pre><code><strong>sent PullVerifierSub.verify tx 0x7d202c67c1d3f04606743a406979eb4dd4a435be37eb0490c502895c28ecc0ec
</strong>price: 84087090417798546633122n
timestamp: 1743492361n
</code></pre>

</details>

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
```

<details>

<summary>Expected Results</summary>

```
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

```

</details>
