# Build your first FTSOv2 app

> Use FTSOv2 in your Foundry or Hardhat project.

> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.

Source: https://dev.flare.network/ftso/guides/build-first-app

This guide is for developers who want to build an FTSOv2 application using either [Foundry](https://getfoundry.sh/) or [Hardhat](https://hardhat.org).

In this guide, you will learn how to:

-   Create a contract to read the price of FLR/USD from FTSOv2 using [flare-periphery-contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts).
    
-   Compile your contract using Foundry [forge](https://getfoundry.sh/).
    
-   Deploy your contract to Flare Testnet Coston2, and interact with it using Foundry [cast](https://getfoundry.sh/).
    

In this guide, you will learn how to:

-   Create a contract to read the price of FLR/USD from FTSOv2 using [flare-periphery-contracts](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts).
    
-   Compile your contract using [Hardhat](https://hardhat.org) and run tests.
    
-   Deploy your contract to Flare Testnet Coston2.
    

## Prerequisites[​](#prerequisites "Direct link to Prerequisites")

Ensure you have the following tools installed:

-   [Foundry](https://getfoundry.sh/)
-   [Node.js](https://nodejs.org/en/download/)

Ensure you have the following tools installed:

-   [npm](https://nodejs.org/en/learn/getting-started/an-introduction-to-the-npm-package-manager) or [yarn](https://yarnpkg.com)
-   [Node.js](https://nodejs.org/en/download/)

## Clone the template[​](#clone-the-template "Direct link to Clone the template")

1.  Clone the [flare-foundry-starter](https://github.com/flare-foundation/flare-foundry-starter) and navigate into the project directory:
    
    ```
    git clone https://github.com/flare-foundation/flare-foundry-starter.gitcd flare-foundry-starter
    ```
    
2.  Install the project dependencies:
    
    ```
    make install
    ```
    
    The `make install` target runs `forge soldeer install` and additionally fetches any npm-only dependencies (`ftso-adapters`, `pyth-sdk-solidity`, `lz-address-book`) that the starter's adapters/OFT examples need.
    
3.  The starter ships with `remappings.txt` ready for the current periphery release.
    

If you're adding the starter's setup to an existing project, copy these remappings:

remappings.txt

```
@openzeppelin-contracts/=dependencies/@openzeppelin-contracts-5.2.0-rc.1/flare-periphery/=dependencies/flare-periphery-0.1.37/@flarenetwork/flare-periphery-contracts/=dependencies/flare-periphery-0.1.37/src/forge-std/=dependencies/forge-std-1.9.5/src/surl/=dependencies/surl-0.0.0/src/surl/=dependencies/surl-0.0.0/lz-address-book/=dependencies/lz-address-book/src/@layerzerolabs/lz-evm-protocol-v2/=dependencies/lz-address-book/lib/LayerZero-v2/packages/layerzero-v2/evm/protocol/@layerzerolabs/lz-evm-messagelib-v2/=dependencies/lz-address-book/lib/LayerZero-v2/packages/layerzero-v2/evm/messagelib/@layerzerolabs/oft-evm/=dependencies/lz-address-book/lib/devtools/packages/oft-evm/@layerzerolabs/oapp-evm/=dependencies/lz-address-book/lib/devtools/packages/oapp-evm/solidity-bytes-utils/=dependencies/lz-address-book/lib/solidity-bytes-utils/solady/=dependencies/solady-0.1.26/ftso-adapters/=dependencies/ftso-adapters-0.0.1/@pythnetwork/pyth-sdk-solidity/=dependencies/pyth-sdk-solidity-2.2.0/
```

1.  Clone the [flare-hardhat-starter](https://github.com/flare-foundation/flare-hardhat-starter) and install dependencies:
    
    ```
    git clone https://github.com/flare-foundation/flare-hardhat-starter.gitcd flare-hardhat-starter
    ```
    
2.  Install the project dependencies:
    
    ```
    yarn install
    ```
    
3.  Copy the environment file and set your private key:
    
    ```
    cp .env.example .env
    ```
    

danger

-   Never share your private keys.
-   Never put your private keys in source code.
-   Never commit private keys to a Git repository.

## Create and compile a contract[​](#create-and-compile-a-contract "Direct link to Create and compile a contract")

1.  Create a contract file `src/FtsoV2FeedConsumer.sol`, and add the following code to it:
    
    src/FtsoV2FeedConsumer.sol
    
    ```
    // SPDX-License-Identifier: MITpragma solidity >=0.8.0 <0.9.0;import {TestFtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/TestFtsoV2Interface.sol";import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";import {IFeeCalculator} from "@flarenetwork/flare-periphery-contracts/coston2/IFeeCalculator.sol";contract FtsoV2Consumer {    bytes21 public constant flrUsdId =        0x01464c522f55534400000000000000000000000000; // "FLR/USD"    // Feed IDs, see https://dev.flare.network/ftso/feeds for full list    bytes21[] public feedIds = [        bytes21(0x01464c522f55534400000000000000000000000000), // FLR/USD        bytes21(0x014254432f55534400000000000000000000000000), // BTC/USD        bytes21(0x014554482f55534400000000000000000000000000) // ETH/USD    ];    function getFlrUsdPrice() external view returns (uint256, int8, uint64) {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedById(flrUsdId);    }    function getFlrUsdPriceWei() external view returns (uint256, uint64) {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedByIdInWei(flrUsdId);    }    function getFtsoV2CurrentFeedValues()        external        view        returns (            uint256[] memory _feedValues,            int8[] memory _decimals,            uint64 _timestamp        )    {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedsById(feedIds);    }}
    ```
    
2.  Set EVM version to `cancun` in `foundry.toml`:
    
    foundry.toml
    
    ```
    [profile.default]...evm_version = "cancun"
    ```
    
3.  To ensure everything is set up correctly, compile the contract by running:
    
    ```
    forge build
    ```
    

The output should indicate that the compilation was successful.

```
[⠊] Compiling...[⠃] Compiling 27 files with Solc 0.8.27[⠊] Solc 0.8.27 finished in 853.78msCompiler run successful!
```

1.  Create a contract file `contracts/FTSOV2Consumer.sol`:
    
    contracts/FTSOV2Consumer.sol
    
    ```
    // SPDX-License-Identifier: MITpragma solidity >=0.8.0 <0.9.0;import {TestFtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/TestFtsoV2Interface.sol";import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";import {IFeeCalculator} from "@flarenetwork/flare-periphery-contracts/coston2/IFeeCalculator.sol";contract FtsoV2Consumer {    bytes21 public constant flrUsdId =        0x01464c522f55534400000000000000000000000000; // "FLR/USD"    // Feed IDs, see https://dev.flare.network/ftso/feeds for full list    bytes21[] public feedIds = [        bytes21(0x01464c522f55534400000000000000000000000000), // FLR/USD        bytes21(0x014254432f55534400000000000000000000000000), // BTC/USD        bytes21(0x014554482f55534400000000000000000000000000) // ETH/USD    ];    function getFlrUsdPrice() external view returns (uint256, int8, uint64) {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedById(flrUsdId);    }    function getFlrUsdPriceWei() external view returns (uint256, uint64) {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedByIdInWei(flrUsdId);    }    function getFtsoV2CurrentFeedValues()        external        view        returns (            uint256[] memory _feedValues,            int8[] memory _decimals,            uint64 _timestamp        )    {        /* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */        TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();        /* Your custom feed consumption logic. In this example the values are just returned. */        return ftsoV2.getFeedsById(feedIds);    }}
    ```
    
2.  Set EVM version to `cancun` in `hardhat.config.ts`:
    
    ```
    module.exports = {  solidity: {    version: "0.8.27",    settings: {      evmVersion: "cancun",      optimizer: {        enabled: true,        runs: 200,      },    },  },};
    ```
    
3.  Compile the contract:
    
    ```
    yarn hardhat compile
    ```
    

## Write tests[​](#write-tests "Direct link to Write tests")

1.  Create a test file `test/FtsoV2FeedConsumer_foundry.t.sol`, and add the following code:
    
    test/FtsoV2FeedConsumer\_foundry.t.sol
    
    ```
    // SPDX-License-Identifier: UNLICENSEDpragma solidity >=0.8.0 <0.9.0;import "forge-std/Test.sol";import {FtsoV2FeedConsumer} from "../src/FtsoV2FeedConsumer.sol";contract MockFtsoV2 {    function getFeedById(        bytes21 /*_feedId*/    ) external payable returns (uint256, int8, uint64) {        return (150000, 7, uint64(block.timestamp));    }}contract MockFeeCalculator {    function calculateFeeByIds(        bytes21[] memory /*_feedIds*/    ) external pure returns (uint256) {        return 0;    }}contract FtsoV2FeedConsumerTest is Test {    FtsoV2FeedConsumer public feedConsumer;    MockFtsoV2 public mockFtsoV2;    MockFeeCalculator public mockFeeCalc;    bytes21 constant flrUsdId =        bytes21(0x01464c522f55534400000000000000000000000000);    function setUp() public {        mockFtsoV2 = new MockFtsoV2();        mockFeeCalc = new MockFeeCalculator();        feedConsumer = new FtsoV2FeedConsumer(            address(mockFtsoV2),            address(mockFeeCalc),            flrUsdId        );    }    function testCheckFees() public {        assertEq(feedConsumer.checkFees(), 0, "Feed value mismatch");    }    function testGetFlrUsdPrice() public {        (uint256 feedValue, int8 decimals, uint64 timestamp) = feedConsumer            .getFlrUsdPrice{value: 0}();        assertEq(feedValue, 150000, "Feed value mismatch");        assertEq(decimals, 7, "Decimals mismatch");        assertApproxEqAbs(            timestamp,            uint64(block.timestamp),            2,            "Timestamp mismatch"        );    }}
    ```
    
2.  Run the tests:
    
    ```
    forge test
    ```
    
    You should see a successful test result:
    
    ```
    [⠊] Compiling...[⠘] Compiling 27 files with Solc 0.8.27[⠃] Solc 0.8.27 finished in 797.51msCompiler run successful!Ran 2 tests for test/FtsoV2FeedConsumer_foundry.t.sol:FtsoV2FeedConsumerTest[PASS] testCheckFees() (gas: 21085)[PASS] testGetFlrUsdPrice() (gas: 25610)Logs:  msg.value matches fee  feedValue 150000  decimals 7  timestamp 1Suite result: ok. 2 passed; 0 failed; 0 skipped; finished in 7.72ms (2.91ms CPU time)Ran 1 test suite in 122.65ms (7.72ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
    ```
    

1.  Create a test directory and file `test/FtsoV2Consumer.test.ts`:
    
    test/FtsoV2ConsumerHardhat.ts
    
    ```
    import { expect } from "chai";import { ethers } from "hardhat";import type { FtsoV2Consumer } from "../typechain-types"; // Adjust the path based on your typechain output directorydescribe("FtsoV2Consumer", function () {  let ftsoV2Consumer: FtsoV2Consumer;  // Deploy a new instance of the contract before each test  beforeEach(async function () {    const FtsoV2ConsumerFactory =      await ethers.getContractFactory("FtsoV2Consumer");    ftsoV2Consumer = await FtsoV2ConsumerFactory.deploy();    await ftsoV2Consumer.waitForDeployment();    // console.log("FtsoV2Consumer deployed to:", await ftsoV2Consumer.getAddress()); // Optional: log address  });  it("Should return the FLR/USD price, decimals, and timestamp", async function () {    const [price, decimals, timestamp] = await ftsoV2Consumer.getFlrUsdPrice();    // We can't know the exact price, but we can check the types and basic validity    expect(price).to.be.a("bigint");    expect(price).to.be.gt(0); // Price should be greater than 0    expect(decimals).to.be.a("number");    // Typical FTSO decimals are often 5 for USD pairs, but let's be flexible    expect(decimals).to.be.within(-128, 127); // Check it's a valid int8    expect(timestamp).to.be.a("bigint");    expect(timestamp).to.be.gt(0); // Timestamp should be positive    // Check if the timestamp is reasonably recent (e.g., within the last hour)    const currentTimestamp = Math.floor(Date.now() / 1000);    expect(Number(timestamp)).to.be.closeTo(currentTimestamp, 3600); // within 1 hour  });  it("Should return the FLR/USD price in Wei and timestamp", async function () {    const [priceWei, timestamp] = await ftsoV2Consumer.getFlrUsdPriceWei();    expect(priceWei).to.be.a("bigint");    expect(priceWei).to.be.gt(0); // Price in Wei should be greater than 0    expect(timestamp).to.be.a("bigint");    expect(timestamp).to.be.gt(0); // Timestamp should be positive    const currentTimestamp = Math.floor(Date.now() / 1000);    expect(Number(timestamp)).to.be.closeTo(currentTimestamp, 3600); // within 1 hour  });  it("Should return current feed values for multiple feeds", async function () {    const [feedValues, decimals, timestamp] =      await ftsoV2Consumer.getFtsoV2CurrentFeedValues();    // Get the expected number of feeds from the contract's public variable    const expectedFeedCount = (await ftsoV2Consumer.feedIds()).length;    expect(feedValues).to.be.an("array");    expect(feedValues).to.have.lengthOf(expectedFeedCount);    feedValues.forEach((value) => {      expect(value).to.be.a("bigint");      expect(value).to.be.gt(0);    });    expect(decimals).to.be.an("array");    expect(decimals).to.have.lengthOf(expectedFeedCount);    decimals.forEach((dec) => {      expect(dec).to.be.a("number");      expect(dec).to.be.within(-128, 127); // Check it's a valid int8    });    expect(timestamp).to.be.a("bigint");    expect(timestamp).to.be.gt(0); // Timestamp should be positive    const currentTimestamp = Math.floor(Date.now() / 1000);    expect(Number(timestamp)).to.be.closeTo(currentTimestamp, 3600); // within 1 hour  });  it("Should have the correct constant flrUsdId", async function () {    const expectedId = "0x01464c522f55534400000000000000000000000000"; // "FLR/USD"    const actualId = await ftsoV2Consumer.flrUsdId();    expect(actualId).to.equal(expectedId);  });  it("Should have the correct initial feedIds array", async function () {    const expectedIds = [      "0x01464c522f55534400000000000000000000000000", // FLR/USD      "0x014254432f55534400000000000000000000000000", // BTC/USD      "0x014554482f55534400000000000000000000000000", // ETH/USD    ];    const actualIds = await ftsoV2Consumer.feedIds();    expect(actualIds).to.deep.equal(expectedIds);  });});
    ```
    
2.  Run the tests:
    
    ```
    yarn hardhat test
    ```
    

## Deploy and interact with contract[​](#deploy-and-interact-with-contract "Direct link to Deploy and interact with contract")

1.  Generate a new wallet using the [cast](https://getfoundry.sh/):
    
    ```
    cast wallet new
    ```
    
    The output will look something like:
    
    ```
    Successfully created new keypair.Address:     0x3f6BdD26f2AE4e77AcDfA1FA24B2774ed93984B4Private key: 0x84cf77b009a92777f75b49864e4166ddcaf8f3f5f119a19b226ab362a0cf7bf5
    ```
    
2.  Store your wallet details and the RPC URL as environment variables:
    
    danger
    
    -   Never share your private keys.
    -   Never put your private keys in source code.
    -   Never commit private keys to a Git repository.
    
    ```
    export ACCOUNT=<address above>export ACCOUNT_PRIVATE_KEY=<private key above>export RPC_URL="https://coston2-api.flare.network/ext/C/rpc"
    ```
    
3.  Use the [Coston2 Faucet](https://faucet.flare.network/coston2) to get some testnet C2FLR tokens.
    

You can verify that the 100 C2FLR has arrived in your wallet:

```
cast balance $ACCOUNT -r $RPC_URL -e
```

4.  The final step before deploying is to set the constructor arguments with the address of [`FtsoV2`](/ftso/solidity-reference) and [`FeeCalculator`](/ftso/solidity-reference) on Flare Testnet Coston2 and the [feed ID](/ftso/feeds) of FLR/USD:
    
    ```
    # see https://dev.flare.network/ftso/solidity-referenceexport FTSOV2_COSTON2=0x3d893C53D9e8056135C26C8c638B76C8b60Df726export FEECALCULATOR_COSTON2=0x88A9315f96c9b5518BBeC58dC6a914e13fAb13e2# see https://dev.flare.network/ftso/feedsexport FLRUSD_FEED_ID=0x01464c522f55534400000000000000000000000000
    ```
    
    You can now deploy the contract:
    
    ```
    forge create src/FtsoV2FeedConsumer.sol:FtsoV2FeedConsumer \  --private-key $ACCOUNT_PRIVATE_KEY \  --rpc-url $RPC_URL \  --constructor-args $FTSOV2_COSTON2 $FEECALCULATOR_COSTON2 $FLRUSD_FEED_ID
    ```
    
    If the deployment is successful, the output will display the contract address, save that for later use:
    
    ```
    [⠊] Compiling...[⠘] Compiling 24 files with Solc 0.8.27[⠃] Solc 0.8.27 finished in 733.41msCompiler run successful!Deployer: 0x3f6BdD26f2AE4e77AcDfA1FA24B2774ed93984B4Deployed to: 0x80Ee4091348d9fA4B4A84Eb525c25049EbDa6152Transaction hash: 0x38604a643695959dd9fa5547d95610fb0b7393c7e8358079f47ed4bdb53c9a8f
    ```
    
    ```
    export DEPLOYMENT_ADDRESS=<deployed to address above>
    ```
    
5.  Use `cast` to interact with the contract, note that this command uses the environment variables defined in the sections above.:
    
    ```
    cast send \  --private-key $ACCOUNT_PRIVATE_KEY \  --rpc-url $RPC_URL -j \  --value 0 $DEPLOYMENT_ADDRESS "getFlrUsdPrice()"
    ```
    
    <details>
    <summary>Expected output of the command above.</summary>
    
    Expected output of the command above.
    
    ```
    {  "status": "0x1",  "cumulativeGasUsed": "0x1cbab",  "logs": [    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x00000000000000000000000098b8e9b5830f04fe3b8d56a2f8455e337037ba280000000000000000000000000000000000000000000000000000000000004231",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x0",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x0000000000000000000000004f52e61907b0ed9f26b88f16b2510a4ca524d6d00000000000000000000000000000000000000000000000000000000000003099",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x1",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x000000000000000000000000d2a1bb23eb350814a30dd6f9de78bb2c8fdd9f1d0000000000000000000000000000000000000000000000000000000000003b68",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x2",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x0000000000000000000000006892bdbbb14e1c9bd46bf31e7bac94d038fc82a6000000000000000000000000000000000000000000000000000000000000422d",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x3",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x000000000000000000000000bd33bdff04c357f7fc019e72d0504c24cf4aa0100000000000000000000000000000000000000000000000000000000000008f11",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x4",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x000000000000000000000000a90db6d10f856799b10ef2a77ebcbf460ac71e520000000000000000000000000000000000000000000000000000000000004e9c",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x5",      "removed": false    },    {      "address": "0x1000000000000000000000000000000000000002",      "topics": [        "0xe7aa66356adbd5e839ef210626f6d8f6f72109c17fadf4c4f9ca82b315ae79b4"      ],      "data": "0x0000000000000000000000000b162ca3acf3482d3357972e12d794434085d839000000000000000000000000000000000000000000000000000000000000e5a6",      "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",      "blockNumber": "0xb2b972",      "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",      "transactionIndex": "0x0",      "logIndex": "0x6",      "removed": false    }  ],  "logsBloom": "0x000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000004000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",  "type": "0x2",  "transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",  "transactionIndex": "0x0",  "blockHash": "0x94f50404f8205caff551ef2b08d20afc4c080bd7b8231cd3941f1a7a6b1b80dd",  "blockNumber": "0xb2b972",  "gasUsed": "0x1cbab",  "effectiveGasPrice": "0x6fc23ac00",  "from": "0x3f6bdd26f2ae4e77acdfa1fa24b2774ed93984b4",  "to": "0x80ee4091348d9fa4b4a84eb525c25049ebda6152",  "contractAddress": null}
    ```
    
    </details>
    

You can see the transaction using the [Coston2 Explorer](https://coston2-explorer.flare.network) by searching for its `transactionHash`.

Congratulations!

You've built your first FTSOv2 app using Foundry.

1.  Create a deployment script `scripts/deployFTSOConsumer.ts`:
    
    scripts/deployFTSOConsumer.ts
    
    ```
    import { ethers, run } from "hardhat";import type { FtsoV2ConsumerContract } from "../typechain-types";const FtsoV2Consumer = artifacts.require("FtsoV2Consumer");async function verifyContract(address: string) {  console.log("\nVerifying contract...");  try {    await run("verify:verify", { address });    console.log("Contract verified successfully");  } catch (error) {    console.error("Error verifying contract:", error);  }}async function callGetFlrUsdPrice(ftsoV2Consumer: FtsoV2ConsumerContract) {  console.log("\nCalling getFlrUsdPrice...");  try {    const result = await ftsoV2Consumer.getFlrUsdPrice();    console.log("FLR/USD Price Data:");    console.log("  Price:", result[0].toString());    console.log("  Decimals:", result[1].toString());    console.log(      "  Timestamp:",      new Date(result[2].toNumber() * 1000).toISOString(),    );  } catch (error) {    console.error("Error calling getFlrUsdPrice:", error);  }}async function callGetFlrUsdPriceWei(ftsoV2Consumer: FtsoV2ConsumerContract) {  console.log("\nCalling getFlrUsdPriceWei...");  try {    const resultWei = await ftsoV2Consumer.getFlrUsdPriceWei();    console.log("FLR/USD Price Data (Wei):");    console.log("  Price (Wei):", resultWei[0].toString());    console.log(      "  Timestamp:",      new Date(resultWei[1].toNumber() * 1000).toISOString(),    );  } catch (error) {    console.error("Error calling getFlrUsdPriceWei:", error);  }}async function callFtsoV2CurrentFeedValues(  ftsoV2Consumer: FtsoV2ConsumerContract,) {  console.log("\nCalling getFtsoV2CurrentFeedValues...");  try {    const feedValuesResult = await ftsoV2Consumer.getFtsoV2CurrentFeedValues();    console.log("Current Feed Values (FLR/USD, BTC/USD, ETH/USD):");    const feedIds = ["FLR/USD", "BTC/USD", "ETH/USD"]; // Match order in contract    for (let i = 0; i < feedValuesResult[0].length; i++) {      console.log(`  Feed ${feedIds[i]}:`);      console.log(`    Price: ${feedValuesResult[0][i].toString()}`);      console.log(`    Decimals: ${feedValuesResult[1][i].toString()}`);    }    console.log(      `  Timestamp: ${new Date(feedValuesResult[2].toNumber() * 1000).toISOString()}`,    );  } catch (error) {    console.error("Error calling getFtsoV2CurrentFeedValues:", error);  }}async function callContractFunctions(ftsoV2Consumer: FtsoV2ConsumerContract) {  await callGetFlrUsdPrice(ftsoV2Consumer);  await callGetFlrUsdPriceWei(ftsoV2Consumer);  await callFtsoV2CurrentFeedValues(ftsoV2Consumer);}// --- Main Deployment Script ---async function main() {  const [deployer] = await ethers.getSigners();  console.log("Deploying contracts with the account:", deployer.address);  // Deploy FtsoV2Consumer  const ftsoV2Consumer: FtsoV2ConsumerContract = await FtsoV2Consumer.new(    "0x01464c522f55534400000000000000000000000000", // Deploying contract with FLR Feed (see more feeds here: https://dev.flare.network/ftso/feeds)  );  await ftsoV2Consumer.deployed();  console.log("FtsoV2Consumer deployed to:", ftsoV2Consumer.address);  // Verify the contract  await verifyContract(ftsoV2Consumer.address);  // Call the contract functions  await callContractFunctions(ftsoV2Consumer);  console.log("\nDeployment and calling script finished.");}main()  .then(() => process.exit(0))  .catch((error) => {    console.error("Unhandled error in main execution:", error);    process.exit(1);  });
    ```
    
2.  Deploy to Flare Testnet Coston2:
    
    ```
    yarn hardhat run scripts/deployFTSOConsumer.ts --network coston2
    ```
    
3.  Interact with the contract:
    
    Copy and paste the deployed contract address into the [Coston2 explorer](https://coston2-explorer.flare.network) to view and interact with the contract.
    

Congratulations!

You've built your first FTSOv2 app using Hardhat.

What's next

Learn how to [read feeds offchain](/ftso/guides/read-feeds-offchain) using JavaScript, Python, Rust and Go, or learn how to [change quote feed](/ftso/guides/change-quote-feed) with an onchain Solidity contract.
