---
title: "Part 2: Deploy & Interact with Pyth App on EVM Testnets"
description: >-
  A step-by-step guide to deploying Pyth applications on the EVM, retrieving
  real-time price feeds via Hermes, and integrating them into a TypeScript
  project.
full: false
index: false
---

# Deploy your Pyth App

In this part of the tutorial, we will learn to deploy the `MyFirstPythContract.sol` contract and fetch the latest price from Pyth network.

This part of the tutorial will conver the following:

- Deploy the contract on OP Sepolia testnet.
- Interact with the contract from the command line.
- Update and fetch the price from the contract using [hermes-client](https://github.com/pyth-network/pyth-crosschain/tree/main/apps/hermes/client/js).

<InfoBox variant="info">
  <div>
    This tutorial is continuation of the [Part 1: Create a Contract](./part-1).
    If you haven't completed that part yet, please do so before continuing.
  </div>
</InfoBox>

## Deploy the contract

Now, let's deploy the contract to a test network.
We will use Optimism Sepolia, but any EVM network will work.
We will also use the Foundry command line tools -- specifically [`cast`](https://book.getfoundry.sh/reference/cast/) -- to perform many of the necessary operations.

First, create a new wallet:

```bash copy
cast wallet new
```

This command will generate a new Ethereum keypair, producing output similar to the following.
Note that the address and private key will be different hexadecimal values.

```
Successfully created new keypair.
Address:     0xB806824fdA4b2b6631e9B87a86d42C9dfd04D129
Private key: 0x0d510c72fd2279155c717eb433ae598a83cfb34b09c2ada86bc424b481082023
```

We will export the values from the command above as environment variables to simplify the commands below.
We will also export the RPC URL of the network.
Run the following commands in your shell substituting the address and private key in the indicated places:

```bash copy
export ADDRESS=<address from above>
export PRIVATE_KEY=<your private key from above>
export RPC_URL="https://sepolia.optimism.io"
```

Next, use the [Superchain Faucet](https://app.optimism.io/faucet?utm_source=docs) to claim some test Sepolia ETH.
Paste the address from the command above into the faucet to get your ETH.
You can verify that the ETH has arrived in your wallet by running the command `cast balance $ADDRESS -r $RPC_URL -e`

The final step before deploying is to get the arguments for the contract's constructor:
the [Pyth contract address](https://docs.pyth.network/price-feeds/contract-addresses/evm#testnets) for Optimism Sepolia
and the [price feed id](https://docs.pyth.network/price-feeds/price-feed-ids) for ETH/USD.
We will also export these values as environment variables for convenience:

```bash copy
export PYTH_OP_SEPOLIA_ADDRESS=0x0708325268dF9F66270F1401206434524814508b
export ETH_USD_ID=0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace
```

Finally, let's deploy the contracts.
Run the following command:

```bash copy
forge create src/MyFirstPythContract.sol:MyFirstPythContract \
--private-key $PRIVATE_KEY \
--rpc-url $RPC_URL \
--constructor-args $PYTH_OP_SEPOLIA_ADDRESS $ETH_USD_ID
```

This command should produce output along the following lines:

```
[⠑] Compiling...
No files changed, compilation skipped
Deployer: 0xB806824fdA4b2b6631e9B87a86d42C9dfd04D129
Deployed to: 0xcb952c0D1E19E4cA5660Cc2F5E0284690bDd2fE9
Transaction hash: 0x3b933816760d6a0070503ab251de0a21618e7634ca7c6c49aea75ff640d470db
```

The contract is deployed!
Let's save the contract address as another environment variable for later use:

```bash copy
export DEPLOYMENT_ADDRESS=<deployed to address from above>
```

## Interact from command line

Let's try out the deployed contract.
In order to do so, we will need to get a price update to pass to the `updateAndMint` function.
We can fetch this update from Hermes:

```bash copy
curl -s "https://hermes.pyth.network/v2/updates/price/latest?&ids[]=$ETH_USD_ID" | jq -r ".binary.data[0]" > price_update.txt
```

This command will fetch a fresh price update from Hermes and save it in the file `price_update.txt`.
The update is a binary payload represented as a hexadecimal string.

We can check the content of the price update by running `cat price_update.txt`

```
 
```

As expected, it's a long hexadecimal string.

Let's use the price update to call the `updateAndMint` function.
We can do this using `cast` by running the following command:

```bash copy
cast send \
  --private-key $PRIVATE_KEY \
  --rpc-url $RPC_URL \
  -j \
  --value 0.0005ether \
  $DEPLOYMENT_ADDRESS \
  "updateAndMint(bytes[])" \
  [0x`cat price_update.txt`]
```

Note that this command uses the environment variables defined in the sections above.
It passes `0.0005` ETH to the `updateAndMint` function to cover the minting fee.
It also passes the price update we got from Hermes, wrapped in an array to match the function signature.
The command above should produce output along the following lines:

```
{"transactionHash":"0x789e9deffc57276efed218e979200a0144606ab77d8378ed55e6c062d4286cb1","transactionIndex":"0x1","blockHash":"0x09b49149595de2c5b683149c03afa36c1a77786b97ddd07ecc69b5b7f926cd0d","blockNumber":"0x76a204","from":"0xc47bf8ac4ded9f6fd4b4f22b11f2eef21bd97734","to":"0xedaa586727c51f0b29f2db7c68dab06499f44614","cumulativeGasUsed":"0x41f43","gasUsed":"0x35a0a","contractAddress":null,"logs":[{"address":"0x0708325268df9f66270f1401206434524814508b","topics":["0xd06a6b7f4918494b3719217d1802786c1f5112a6c1d88fe2cfec00b4584f6aec","0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace"],"data":"0x0000000000000000000000000000000000000000000000000000000065c41fa400000000000000000000000000000000000000000000000000000038b9e86b65000000000000000000000000000000000000000000000000000000000b8318d5","blockHash":"0x09b49149595de2c5b683149c03afa36c1a77786b97ddd07ecc69b5b7f926cd0d","blockNumber":"0x76a204","transactionHash":"0x789e9deffc57276efed218e979200a0144606ab77d8378ed55e6c062d4286cb1","transactionIndex":"0x1","logIndex":"0x0","removed":false}],"status":"0x1","logsBloom":" ","type":"0x2","effectiveGasPrice":"0xb2d05efc","l1Fee":"0x4711ac0c0471","l1FeeScalar":"0.684","l1GasPrice":"0x13355e9e2","l1GasUsed":"0x568c"}
```

You can see the transaction using the [Optimism Sepolia Explorer](https://sepolia-optimism.etherscan.io/) by pasting the `transactionHash` from your result into the search box.
If you get an error in this step, don't worry -- these are easily fixed.
There are two possible errors when running this command.
The first error looks like this:

```
Error:
(code: 3, message: execution reverted, data: Some(String("0x025dbdd4")))
```

The error code `0x025dbdd4` represents the `InsufficientFee` error.
This means that `0.0005` ETH was not sufficient to cover the minting fee.
Try increasing the fee to fix the problem.

The other error looks like this:

```
Error:
(code: 3, message: execution reverted, data: Some(String("0x19abf40e")))
```

The error code `0x19abf40e` represents the `StalePrice` error that we saw before.
This means that the `price_update.txt` was too old to be used by the contract.
Simply re-run the `curl` command above to get a newer price update, then retry sending the transaction.

## Interact from Typescript

As a final task, let's interact with our deployed contract from Typescript.
First create a new directory called `app` that is adjacent to `contracts` and create a new `npm` project:

```bash copy
cd ..
mkdir app
cd app
npm init -y
npm install --save typescript ts-node viem @pythnetwork/hermes-client
```

Then open `src/mintNft.ts` and paste in the following content:

```typescript copy mintNft.ts
import { createWalletClient, http, parseEther } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { optimismSepolia } from "viem/chains";
import { EvmPriceServiceConnection } from "@pythnetwork/hermes-client";
import { getContract } from "viem";

export const abi = [
  {
    type: "constructor",
    inputs: [
      {
        name: "_pyth",
        type: "address",
        internalType: "address",
      },
      {
        name: "_ethUsdPriceId",
        type: "bytes32",
        internalType: "bytes32",
      },
    ],
    stateMutability: "nonpayable",
  },
  {
    type: "function",
    name: "mint",
    inputs: [],
    outputs: [],
    stateMutability: "payable",
  },
  {
    type: "function",
    name: "updateAndMint",
    inputs: [
      {
        name: "pythPriceUpdate",
        type: "bytes[]",
        internalType: "bytes[]",
      },
    ],
    outputs: [],
    stateMutability: "payable",
  },
  {
    type: "error",
    name: "InsufficientFee",
    inputs: [],
  },
] as const;

async function run() {
  const account = privateKeyToAccount(process.env["PRIVATE_KEY"] as any);
  const client = createWalletClient({
    account,
    chain: optimismSepolia,
    transport: http(),
  });

  const contract = getContract({
    address: process.env["DEPLOYMENT_ADDRESS"] as any,
    abi: abi,
    client,
  });

  const connection = new HermesClient("https://hermes.pyth.network");
  const priceIds = [process.env["ETH_USD_ID"] as string];
  const priceFeedUpdateData = await connection.getLatestPriceUpdates(priceIds);
  console.log("Retrieved Pyth price update:");
  console.log(priceFeedUpdateData);

  const hash = await contract.write.updateAndMint(
    [priceFeedUpdateData as any],
    { value: parseEther("0.0005") },
  );
  console.log("Transaction hash:");
  console.log(hash);
}

run();
```

Notice: This `abi` variable which represents the interface of our contract and can be autogenerated by running `forge inspect MyFirstPythContract abi` in the `contracts` directory.

The `run` function does two different things:

- First, it instantiates an interface to our contract using the [`viem`](https://viem.sh/) library.
- Second, it uses `HermesClient` from [`@pythnetwork/hermes-client`](<(https://github.com/pyth-network/pyth-crosschain/tree/main/apps/hermes/client/js)>) to retrieve

Run this script using:

```bash copy
npx ts-node src/mintNft.ts
```

```
Retrieved Pyth price update:
[
  ' '
]
Transaction hash:
0xe304f461a9a0a8e47e1e8dd394e0d3f95dcadd9a2b72bb4963693bdc71ff8405
```

It works!
Again, you can look up the transaction hash in the Optimism Sepolia explorer to check that the transaction landed.

Congratulations! You've built your first app using Pyth price feeds.

## Conclusion

In this tutorial, we created a Solidity contract that updates and reads Pyth prices, tested the contract locally,
then deployed the contract and interacted with it both via the command line and `hermes-client` sdk.

## Next Steps

Check out the [EVM Contract References](https://api-reference.pyth.network/price-feeds/evm/getPriceNoOlderThan) for detailed information on the Pyth EVM contract API.
For an end-to-end example using Pyth price feed, check out the [Pyth Oracle AMM Example](https://github.com/pyth-network/pyth-examples/tree/main/price_feeds/evm).
