Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(docs): add support for hardhat FTSO guide and fix FTSO interfaces #608

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 FtsoV2FeedConsumerHardhat from "!!raw-loader!/examples/developer-hub-solidity/FTSOV2FeedConsumer_hardhat.sol";
import FtsoV2FeedConsumerTestHardhat from "!!raw-loader!/examples/developer-hub-javascript/FTSOV2FeedConsumer_hardhat.js";
import FtsoV2FeedConsumerHardhatDeploy 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/FTSOV2FeedConsumer_hardhat.sol`:

<CodeBlock language="solidity" title="contracts/FTSOV2FeedConsumer_hardhat.sol">
{FtsoV2FeedConsumerHardhat}
</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 file `test/FtsoV2FeedConsumer.test.ts`:

<CodeBlock language="typescript" title="test/FtsoV2FeedConsumerHardhat.js">
{FtsoV2FeedConsumerTestHardhat}
</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">
{FtsoV2FeedConsumerHardhatDeploy}
</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
53 changes: 53 additions & 0 deletions examples/developer-hub-javascript/FTSOV2FeedConsumer_hardhat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { artifacts, ethers } from "hardhat";
import { expect } from "chai";
import { deployFlareContractRegistry } from "../lib/utils";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this live?


const FtsoV2FeedConsumer = artifacts.require("FtsoV2FeedConsumer");

describe("FtsoV2FeedConsumer", () => {
let ftsoConsumer;
const mockFtsoV2Address = "0x1234567890123456789012345678901234567890";
const mockFeeCalcAddress = "0x2345678901234567890123456789012345678901";
const mockFlrUsdId = "0x0123456789012345678901234567890123456789012345";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic numbers?


beforeEach(async () => {
// Deploy mock Flare Contract Registry
await deployFlareContractRegistry();

// Deploy the FtsoV2FeedConsumer contract
ftsoConsumer = await FtsoV2FeedConsumer.new(
mockFtsoV2Address,
mockFeeCalcAddress,
mockFlrUsdId,
);
});

describe("Constructor", () => {
it("should set the initial values correctly", async () => {
const storedFlrUsdId = await ftsoConsumer.flrUsdId();
expect(storedFlrUsdId).to.equal(mockFlrUsdId);

const feedIds = await ftsoConsumer.feedIds(0);
expect(feedIds).to.equal(mockFlrUsdId);
});
});

describe("checkFees", () => {
it("should update and return the fee", async () => {
// Since we're using mocks, we'll just verify the function can be called
const fee = await ftsoConsumer.fee();

expect(fee).to.be.a("number");
});
});

describe("getFlrUsdPrice", () => {
it("should revert if fee does not match msg.value", async () => {
const fee = ethers.parseEther("1.0");
await ftsoConsumer.checkFees();

await expect(ftsoConsumer.getFlrUsdPrice({ value: fee + 1n })).to.be
.reverted;
});
});
});
42 changes: 42 additions & 0 deletions examples/developer-hub-javascript/deployFTSO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { artifacts, ethers } from "hardhat";
import { FtsoV2FeedConsumerInstance } from "../typechain-types";

const FtsoV2FeedConsumer = artifacts.require("FtsoV2FeedConsumer");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which contract does this one refer to?


async function main() {
const [deployer] = await ethers.getSigners();

console.log("Deploying contracts with the account:", deployer.address);

// Deploy FtsoV2FeedConsumer
const ftsoConsumer: FtsoV2FeedConsumerInstance = await FtsoV2FeedConsumer.new(
"0x01464c522f55534400000000000000000000000000",
);
console.log("FtsoV2FeedConsumer deployed to:", ftsoConsumer.address);

// Test the contract functions
try {
// Test getFLRUSDPrice
const result = await ftsoConsumer.getFlrUsdPrice();
console.log("\nFLR/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(),
);

// Test checkFees
const fees = await ftsoConsumer.checkFees();
console.log("\nFees:", fees.toString());
} catch (error) {
console.error("Error testing contract:", error);
}
}

main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});
2 changes: 1 addition & 1 deletion examples/developer-hub-solidity/FTSOV2FeedById.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract FtsoV2FeedConsumerById {
*/
function getFtsoV2FeedValueById()
external
payable
view
returns (uint256 _feedValue, int8 _decimals, uint64 _timestamp)
{
/* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */
Expand Down
5 changes: 2 additions & 3 deletions examples/developer-hub-solidity/FTSOV2FeedByIdWei.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import {TestFtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/costo
* DO NOT USE THIS CODE IN PRODUCTION.
*/
contract FtsoV2FeedTracker {
TestFtsoV2Interface internal ftsoV2;
bytes21 public feedId = bytes21(0x01464c522f55534400000000000000000000000000); // FLR/USD

// Event to track feed retrieval
Expand All @@ -23,11 +22,11 @@ contract FtsoV2FeedTracker {
*/
function getFeedValueByIdWei()
external
payable
view
returns (uint256 _feedValue, uint64 _timestamp)
{
/* THIS IS A TEST METHOD, in production use: ftsoV2 = ContractRegistry.getFtsoV2(); */
ftsoV2 = ContractRegistry.getTestFtsoV2();
TestFtsoV2Interface ftsoV2 = ContractRegistry.getTestFtsoV2();
// Retrieve feed value and timestamp from the FtsoV2 contract
(_feedValue, _timestamp) = ftsoV2.getFeedByIdInWei(feedId);

Expand Down
Loading
Loading