Skip to content
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
3cac60b
feat(docs): add support for hardhat FTSO guide and fix FTSO interfaces
0xreflexivity Mar 20, 2025
fe0e02e
fix: lint issues
swarna1101 Mar 22, 2025
3ff7cfc
add: codeQL check
swarna1101 Mar 23, 2025
344634b
fix: v2->v3
swarna1101 Mar 23, 2025
435dcc3
fix: codeql script
swarna1101 Mar 23, 2025
f092a01
fix: remove the codeQL POC
swarna1101 Mar 23, 2025
896a9df
fix(docs): change desc. and hardhat link
0xreflexivity Mar 26, 2025
4ab621e
Merge branch 'hardhat-ftso' of https://github.com/AmadiaFlare/develop…
0xreflexivity Mar 26, 2025
cf3659f
Merge branch 'main' into fork/AmadiaFlare/hardhat-ftso
swarna1101 Apr 10, 2025
25a0e91
fix: minor change
swarna1101 Apr 10, 2025
a1c0eac
Merge branch 'flare-foundation:main' into hardhat-ftso
0xreflexivity Apr 16, 2025
d77c7d9
fix(docs): ftso hardhat contract, deploy script, and tests
0xreflexivity Apr 16, 2025
1367cd8
fix(docs): lint inside js directory
0xreflexivity Apr 16, 2025
250cb7e
fix: minor formatting
swarna1101 Apr 16, 2025
6365d97
fix(docs): change contract License to MIT
0xreflexivity Apr 16, 2025
139b69b
Merge branch 'hardhat-ftso' of https://github.com/AmadiaFlare/develop…
0xreflexivity Apr 16, 2025
2e31758
fix(docs): clean up
0xreflexivity Apr 18, 2025
bc8b44c
fix(docs): clean up
0xreflexivity Apr 18, 2025
8496019
Merge branch 'flare-foundation:main' into hardhat-ftso
0xreflexivity Apr 24, 2025
3481166
feat(doc): match new FTSO script in hardhat-starter
0xreflexivity Apr 24, 2025
a78f522
fix(docs): name change
0xreflexivity Apr 24, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 141 additions & 17 deletions docs/ftso/guides/build-first-app.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
title: Build your first FTSOv2 app
tags: [intermediate, ftso, solidity]
slug: build-first-app
description: Use FTSOv2 in your Foundry project.
description: Use FTSOv2 in your Foundry or Hardhat project.
keywords:
[
ftso,
Expand All @@ -17,25 +17,37 @@ sidebar_position: 1
---

import CodeBlock from "@theme/CodeBlock";
import FtsoV2FeedConsumer from "!!raw-loader!/examples/developer-hub-solidity/FtsoV2FeedConsumer_foundry.sol";
import FtsoV2FeedConsumerTest from "!!raw-loader!/examples/developer-hub-solidity/FtsoV2FeedConsumer_foundry.t.sol";
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import FtsoV2FeedConsumerFoundry from "!!raw-loader!/examples/developer-hub-solidity/FtsoV2FeedConsumer_foundry.sol";
import FtsoV2FeedConsumerTestFoundry from "!!raw-loader!/examples/developer-hub-solidity/FtsoV2FeedConsumer_foundry.t.sol";
import FtsoV2ConsumerHardhat from "!!raw-loader!/examples/developer-hub-solidity/FTSOV2Consumer_hardhat.sol";
import FtsoV2ConsumerTestHardhat from "!!raw-loader!/examples/developer-hub-javascript/FTSOV2Consumer_hardhat.ts";
import FtsoV2ConsumerHardhatDeploy from "!!raw-loader!/examples/developer-hub-javascript/deployFTSO.ts";

This guide is for developers who want to build an FTSOv2 application using Foundry. In this guide, you will learn how to:
This guide is for developers who want to build an FTSOv2 application. Choose your development framework:

<Tabs groupId="framework">
<TabItem value="foundry" label="Foundry" default>

## Building with Foundry

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://book.getfoundry.sh/reference/forge/).

- Deploy your contract to Flare Testnet Coston2, and interact with it using Foundry [cast](https://book.getfoundry.sh/reference/cast/).

## Prerequisites
### Prerequisites

Ensure you have the following tools installed:

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

## Clone the Foundry template
### Clone the Foundry template

1. Clone the [flare-foundry-starter](https://github.com/flare-foundation/flare-foundry-starter) and navigate into the project directory:

Expand All @@ -61,14 +73,14 @@ Ensure you have the following tools installed:
surl/=dependencies/surl-0.0.0/
```

## Create and compile a contract
### Create and compile a contract

Now, you can create a contract that consumes data from FTSOv2.

1. Create a contract file `src/FtsoV2FeedConsumer.sol`, and add the following code to it:
1. Create a contract file `src/FtsoV2FeedConsumer_foundry.sol`, and add the following code to it:

<CodeBlock language="solidity" title="src/FtsoV2FeedConsumer.sol">
{FtsoV2FeedConsumer}
<CodeBlock language="solidity" title="src/FtsoV2FeedConsumer_foundry.sol">
{FtsoV2FeedConsumerFoundry}
</CodeBlock>

2. Set EVM version to `london` in `foundry.toml`:
Expand All @@ -94,14 +106,14 @@ Now, you can create a contract that consumes data from FTSOv2.
Compiler run successful!
```

## Write tests
### Write tests

Before deploying, it's important to write tests for your contract.

1. Create a test file `test/FtsoV2FeedConsumer.t.sol`, and add the following code:
1. Create a test file `test/FtsoV2FeedConsumer_foundry.t.sol`, and add the following code:

<CodeBlock language="solidity" title="test/FtsoV2FeedConsumer.t.sol">
{FtsoV2FeedConsumerTest}
<CodeBlock language="solidity" title="test/FtsoV2FeedConsumer_foundry.t.sol">
{FtsoV2FeedConsumerTestFoundry}
</CodeBlock>

2. Run the tests:
Expand All @@ -118,7 +130,7 @@ Before deploying, it's important to write tests for your contract.
[⠃] Solc 0.8.27 finished in 797.51ms
Compiler run successful!

Ran 2 tests for test/FtsoV2FeedConsumer.t.sol:FtsoV2FeedConsumerTest
Ran 2 tests for test/FtsoV2FeedConsumer_foundry.t.sol:FtsoV2FeedConsumerTestFoundry
[PASS] testCheckFees() (gas: 21085)
[PASS] testGetFlrUsdPrice() (gas: 25610)
Logs:
Expand All @@ -132,7 +144,7 @@ Before deploying, it's important to write tests for your contract.
Ran 1 test suite in 122.65ms (7.72ms CPU time): 2 tests passed, 0 failed, 0 skipped (2 total tests)
```

## Deploy and interact with the contract
### Deploy and interact with the contract

You can now deploy your contract to Flare Testnet Coston2.

Expand Down Expand Up @@ -309,7 +321,7 @@ You can now deploy your contract to Flare Testnet Coston2.
"removed": false
}
],
"logsBloom": "0x00020000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000400000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"logsBloom": "0x000200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000004000000000800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"type": "0x2",
"transactionHash": "0x3fdc9cf00456a7878476877b6f8ae5c994dd3c224ca792f965f718340fd98402",
"transactionIndex": "0x0",
Expand All @@ -329,6 +341,118 @@ You can see the transaction using the [Coston2 Explorer](https://coston2.testnet

Congratulations! You've built your first app using FTSOv2.

</TabItem>

<TabItem value="hardhat" label="Hardhat">

## Building with Hardhat

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

Ensure you have the following tools installed:

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

### Clone the Hardhat template

1. Clone the [flare-hardhat-starter](https://github.com/flare-foundation/flare-hardhat-starter) and install dependencies:

```bash
git clone https://github.com/flare-foundation/flare-hardhat-starter.git
cd flare-hardhat-starter
yarn install
```

2. Copy the environment file and set your private key:

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

1. Create a contract file `contracts/FTSOV2Consumer_hardhat.sol`:

<CodeBlock language="solidity" title="contracts/FTSOV2Consumer_hardhat.sol">
{FtsoV2ConsumerHardhat}
</CodeBlock>

2. Set EVM version to `london` in `hardhat.config.ts`:

```typescript
module.exports = {
solidity: {
version: "0.8.27",
settings: {
evmVersion: "london",
optimizer: {
enabled: true,
runs: 200,
},
},
},
};
```

3. Compile the contract:

```bash
npx hardhat compile
```

### Write tests

1. Create a test directory and file `test/FtsoV2Consumer.test.ts`:

<CodeBlock language="typescript" title="test/FtsoV2ConsumerHardhat.ts">
{FtsoV2ConsumerTestHardhat}
</CodeBlock>

2. Run the tests:

```bash
npx hardhat test
```

### Deploy and interact with the contract

1. Create a deployment script `scripts/deployFTSO.ts`:

<CodeBlock language="typescript" title="scripts/deployFTSO.ts">
{FtsoV2ConsumerHardhatDeploy}
</CodeBlock>

2. Deploy to Coston2:

```bash
npx hardhat run scripts/deployFTSO.ts --network coston2
```

3. Interact with the contract:

Copy and paste the deployed contract address into the Coston2 block explorer [here](https://coston2-explorer.flare.network/) to view and interact with the contract.

Congratulations! You've built your first FTSOv2 app using Hardhat.

</TabItem>
</Tabs>

:::tip[What's next]

Learn how to [read feeds offchain](read-feeds-offchain) using JavaScript, Python, Rust and Go, or learn how to [change quote feed](change-quote-feed) with an onchain Solidity contract.
Expand Down
89 changes: 89 additions & 0 deletions examples/developer-hub-javascript/FTSOV2Consumer_hardhat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import { expect } from "chai";
import { ethers } from "hardhat";
import { FtsoV2Consumer } from "../typechain-types"; // Adjust the path based on your typechain output directory

describe("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);
});
});
Loading
Loading