Skip to content

Commit 5ab03e4

Browse files
feat(ftso): add custom feed tutorial and examples (#722)
2 parents e006098 + 7dc7948 commit 5ab03e4

File tree

4 files changed

+880
-2
lines changed

4 files changed

+880
-2
lines changed

docs/ftso/2-feeds.mdx

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,14 +41,24 @@ The resulting string is then prefixed with `0x`.
4141

4242
## Custom Feeds
4343

44-
:::warning
44+
:::warning[Risk Profile]
4545

46-
Custom Feeds, introduced in [FIP.13](https://proposals.flare.network/FIP/FIP_13.html), have a distinct risk profile compared to standard FTSO feeds. Unlike standard FTSO feeds, the security of a Custom Feed is conditional on the logic defined in its smart contract.
46+
Each Custom Feed has a unique risk profile determined by its smart contract and data source, which users and developers must assess individually.
4747

4848
:::
4949

50+
Custom Feeds, introduced in [FIP.13](https://proposals.flare.network/FIP/FIP_13.html), extend the FTSO by enabling developers to create onchain feeds for arbitrary time-series data.
51+
Unlike standard FTSO feeds, which are secured by a decentralized network of data providers, Custom Feeds derive their values from logic defined in a developer-controlled smart contract.
52+
This expands the FTSO's capabilities beyond traditional price pairs, allowing for a wider variety of data to be brought onto the Flare network, such as prices for Liquid Staked Tokens (LSTs), data from specific offchain APIs, or other bespoke datasets.
53+
5054
<CustomFeeds />
5155

56+
:::tip[Create a new Custom Feed]
57+
58+
Follow the [create a Custom Feed](/ftso/guides/create-custom-feed) guide to learn how a build a new Custom Feed.
59+
60+
:::
61+
5262
## Need more feeds?
5363

5464
FTSOv2 can scale up to 1000 feeds. If you need additional FTSOv2 feeds beyond what is currently available, you can raise a New Feed Request Issue on GitHub. When a feed request is submitted, it is reviewed by the FTSO Management Group, which is comprised of the FTSO data providers as outlined in [FIP.08](https://proposals.flare.network/FIP/FIP_8.html#222-through-the-ftso-management-group).
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
---
2+
title: Create a Custom Feed
3+
tags: [intermediate, ftso, solidity]
4+
slug: create-custom-feed
5+
description: Bring any time-series data onchain with Custom Feeds.
6+
keywords:
7+
[
8+
ftso,
9+
oracle,
10+
custom feeds,
11+
data feeds,
12+
flare-time-series-oracle,
13+
flare-network,
14+
smart-contracts,
15+
solidity,
16+
]
17+
sidebar_position: 6
18+
---
19+
20+
import CodeBlock from "@theme/CodeBlock";
21+
import PriceVerifierCustomFeedSol from "!!raw-loader!/examples/developer-hub-solidity/PriceVerifierCustomFeed.sol";
22+
import PriceVerificationTs from "!!raw-loader!/examples/developer-hub-javascript/PriceVerification.ts";
23+
import NewGithubIssue from "@site/src/components/newGithubIssue";
24+
25+
Custom Feeds, introduced in [FIP.13](https://proposals.flare.network/FIP/FIP_13.html), extend the FTSO [block-latency feeds](/ftso/feeds) by enabling developers to create onchain feeds for arbitrary time-series data.
26+
Unlike standard FTSO feeds, which are secured by a decentralized network of data providers, Custom Feeds derive their values from logic defined in a developer-controlled smart contract.
27+
This expands the FTSO's capabilities beyond traditional price pairs, allowing for a wider variety of data to be brought onto the Flare network, such as prices for Liquid Staked Tokens (LSTs), data from specific offchain APIs, or other bespoke datasets.
28+
29+
:::warning[Risk Profile]
30+
31+
Each Custom Feed has a unique risk profile determined by its smart contract and data source, which users and developers must assess individually.
32+
33+
:::
34+
35+
This guide demonstrates how to build a Custom Feed feed that uses the [Flare Data Connector (FDC)](/fdc/overview) to fetch data from an external API, verify it onchain, and make it available to other smart contracts.
36+
37+
## Prerequisites
38+
39+
Before you begin, ensure you have:
40+
41+
- A conceptual understanding of smart contracts and Solidity.
42+
- Familiarity with TypeScript/JavaScript and Node.js.
43+
- Hardhat development environment set up.
44+
- Knowledge of the FTSO and an overview of the Flare Data Connector (FDC).
45+
- Environment variables set up for `WEB2JSON_VERIFIER_URL_TESTNET`, `VERIFIER_API_KEY_TESTNET`, and `COSTON2_DA_LAYER_URL` as used in the `PriceVerification.ts` script.
46+
47+
## Concepts
48+
49+
### `IICustomFeed` Interface
50+
51+
To ensure compatibility with the FTSO system, any custom feed contract must implement the `IICustomFeed` interface.
52+
This interface, found in the [`@flarenetwork/flare-periphery-contracts`](https://www.npmjs.com/package/@flarenetwork/flare-periphery-contracts) package, acts as a standard entry point for consumers of the data.
53+
54+
#### Key functions
55+
56+
- `feedId() external view returns (bytes21 _feedId)`:Returns the feed's unique identifier. The first byte must be 0x21 to signify a custom feed.
57+
- `read() public view returns (uint256 value)`: Returns the latest value of the feed.
58+
- `decimals() external view returns (int8)`: Returns the number of decimals for the feed's value.
59+
- `calculateFee() external pure returns (uint256 _fee)`: Calculates the fee for reading the feed. This can be zero.
60+
- `getCurrentFeed() external payable returns (uint256 _value, int8 _decimals, uint64 _timestamp)`: The primary method for retrieving the current feed data, including value, decimals, and a timestamp.
61+
62+
### Onchain contract: `PriceVerifierCustomFeed.sol`
63+
64+
The `PriceVerifierCustomFeed.sol` contract is designed to store a historical price for a crypto asset and allow this price to be updated by verifying a proof from the [Web2Json](/fdc/attestation-types/web-2-json) FDC attestation type.
65+
It then exposes this price through the `IICustomFeed` interface.
66+
67+
#### Key components
68+
69+
- **State Variables**: The contract stores its configuration and the latest verified price.
70+
71+
```solidity
72+
// --- State Variables ---
73+
bytes21 public immutable feedIdentifier; // Unique ID for this custom feed. ID, e.g., 0x21 + keccak256("BTC/USD-HIST")
74+
string public expectedSymbol; // Asset symbol this feed tracks (e.g., "BTC").
75+
int8 public decimals_; // Precision for the price.
76+
uint256 public latestVerifiedPrice; // Stores the most recent verified price.
77+
address public owner; // Address that deployed the contract, with privileges to update mappings.
78+
```
79+
80+
- **Constructor**: Initializes the feed's immutable properties, such as its `feedIdentifier`, symbol, and decimals. It also sets up an initial mapping of asset symbols (e.g., "BTC") to API-specific identifiers (e.g., CoinGecko's "bitcoin").
81+
82+
```solidity
83+
constructor(
84+
bytes21 _feedId,
85+
string memory _expectedSymbol,
86+
int8 _decimals
87+
) {
88+
// ... validation logic ...
89+
owner = msg.sender;
90+
feedIdentifier = _feedId;
91+
expectedSymbol = _expectedSymbol;
92+
decimals_ = _decimals;
93+
// ...
94+
}
95+
```
96+
97+
- **`verifyPrice(IWeb2Json.Proof calldata _proof)`**:
98+
This is the heart of the contract.
99+
It accepts a proof from the FDC and performs a series of checks before updating the `latestVerifiedPrice`.
100+
101+
1. **Parses the API URL** from the proof to ensure the data is from the expected source.
102+
103+
2. **Verifies the proof's authenticity** by calling the FDC's onchain `verifyJsonApi` function.
104+
105+
3. **Decodes the price data** from the proof's response body.
106+
107+
4. **Stores the new price** in the `latestVerifiedPrice` state variable.
108+
109+
5. **Emits an event** to log the update.
110+
111+
```solidity
112+
function verifyPrice(IWeb2Json.Proof calldata _proof) external {
113+
require(ContractRegistry.getFdcVerification().verifyJsonApi(_proof), "FDC: Invalid Web2Json proof");
114+
115+
PriceData memory newPriceData = abi.decode(_proof.data.responseBody.abiEncodedData, (PriceData));
116+
latestVerifiedPrice = newPriceData.price;
117+
118+
emit PriceVerified(expectedSymbol, newPriceData.price, _proof.data.requestBody.url);
119+
}
120+
```
121+
122+
- **`IICustomFeed` Implementation**:
123+
The contract provides concrete implementations for the interface methods.
124+
For example, `read()` and `getCurrentFeed()` simply return the latestVerifiedPrice and other stored data.
125+
126+
<details>
127+
<summary>
128+
View full <code>PriceVerifierCustomFeed.sol</code> contract
129+
</summary>
130+
<CodeBlock
131+
language="solidity"
132+
title="/examples/developer-hub-solidity/PriceVerifierCustomFeed.sol"
133+
>
134+
{PriceVerifierCustomFeedSol}
135+
</CodeBlock>
136+
</details>
137+
138+
### Offchain script: `PriceVerification.ts`
139+
140+
The `PriceVerification.ts` script automates fetching data from CoinGecko via the FDC and submitting it to your `PriceVerifierCustomFeed` contract.
141+
142+
It follows these sequential steps:
143+
144+
1. **Deploy Contract:** The script first deploys the `PriceVerifierCustomFeed.sol` contract to the network. It constructs the unique `feedId` by combining the `0x21` prefix with a hash of the asset string (e.g., "BTC/USD-HIST").
145+
146+
2. **Prepare Attestation Request:** It constructs a request for the FDC [Web2Json](/fdc/attestation-types/web-2-json) attestation type, specifying the target API endpoint (CoinGecko), the parameters (which coin and date), and the JQ filter to extract the exact data point (the USD price) from the JSON response.
147+
148+
3. **Submit Request to FDC:** The script sends this request to the FDC, which fetches the data, secures it through an attestation process, and makes a proof available.
149+
150+
4. **Retrieve Proof:** After the attestation round is final, the script queries the [Data Availability (DA)](/fdc/reference/data-availability-api) layer to retrieve the finalized data and its corresponding Merkle proof.
151+
152+
5. **Submit Proof to Custom Feed:** Finally, the script calls the `verifyPrice()` function on the deployed `PriceVerifierCustomFeed` contract, passing the retrieved proof. The contract then executes its verification logic and, if successful, updates the onchain price.
153+
154+
<details>
155+
<summary>
156+
View full <code>PriceVerification.ts</code> script.
157+
</summary>
158+
<CodeBlock
159+
language="typescript"
160+
title="/examples/developer-hub-javascript/PriceVerification.ts"
161+
>
162+
{PriceVerificationTs}
163+
</CodeBlock>
164+
</details>
165+
166+
## Deploy and use a Custom feed
167+
168+
This guide walks you through using the [Flare Hardhat Starter](https://github.com/flare-foundation/flare-hardhat-starter) to deploy and interact with a custom price feed.
169+
170+
### 1. Clone the hardhat starter
171+
172+
First, clone the [`flare-hardhat-starter`](https://github.com/flare-foundation/flare-hardhat-starter) repository and navigate into the project directory:
173+
174+
```bash
175+
git clone https://github.com/flare-foundation/flare-hardhat-starter.git
176+
cd flare-hardhat-starter
177+
```
178+
179+
### 2. Install dependencies
180+
181+
Install the project dependencies using `npm` or `yarn`:
182+
183+
```bash
184+
npm install # or yarn install
185+
```
186+
187+
### 3. Set up environment variables
188+
189+
Copy the example environment file and update it with your own credentials.
190+
191+
```bash
192+
cp .env.example .env
193+
```
194+
195+
You will need to provide the following:
196+
197+
- `PRIVATE_KEY`: The private key of the account you want to use for deployment on the Coston2 testnet. This account must be funded with C2FLR tokens from the [Coston2 Faucet](https://faucet.flare.network/coston2).
198+
- `WEB2JSON_VERIFIER_URL_TESTNET`: The URL for the Web2Json Verifier service. You can leave the default value.
199+
- `VERIFIER_API_KEY_TESTNET`: An API key for the verifier service. You can get one from the [Flare Developer Portal](https://portal.flare.network/).
200+
- `COSTON2_DA_LAYER_URL`: The URL for the Data Availability Layer on Coston2. You can leave the default value.
201+
202+
:::danger
203+
204+
Never commit your `.env` file or share your private keys publicly.
205+
206+
:::
207+
208+
### 4. Run the verification script
209+
210+
The `PriceVerification.ts` script, located in `scripts/customFeeds/`, automates the entire process. Execute it on the Coston2 Testnet:
211+
212+
```bash
213+
npx hardhat run scripts/customFeeds/PriceVerification.ts --network coston2
214+
```
215+
216+
The script will:
217+
218+
1. Deploy the `PriceVerifierCustomFeed.sol` contract.
219+
2. Prepare an attestation request for the CoinGecko API.
220+
3. Submit the request to the Flare Data Connector (FDC).
221+
4. Wait for the attestation to be finalized.
222+
5. Retrieve the proof from the Data Availability layer.
223+
6. Submit the proof to the deployed `PriceVerifierCustomFeed` contract.
224+
225+
### 5. Understanding the output
226+
227+
The script will log its progress.
228+
A successful run will display the deployed contract address and the final verified price:
229+
230+
```text
231+
Deploying PriceVerifierCustomFeed...
232+
PriceVerifierCustomFeed deployed to: 0x... (contract address)
233+
234+
Preparing data...
235+
// ... (attestation request details)
236+
237+
Submitting attestation requests...
238+
// ... (transaction details)
239+
240+
Waiting for round 12345 to be finalized...
241+
Round finalized.
242+
243+
Retrieving proofs...
244+
// ... (proof details)
245+
246+
Submitting proof to custom feed...
247+
Proof for BTCPrice submitted successfully...
248+
249+
Latest verified price: 12345
250+
Price verification process completed successfully.
251+
```
252+
253+
### 6. Verify on explorer
254+
255+
You can view your deployed contract on the [Coston2 Explorer](https://coston2-explorer.flare.network/).
256+
Use the contract address from the script's output to look it up. On the **Read Contract** tab, call the `latestVerifiedPrice()` function to confirm the price was stored onchain.
257+
258+
## Propose a new Custom Feed
259+
260+
If you have developed a Custom Feed that could benefit the wider ecosystem, you can propose it for official listing on the [Block-Latency Feeds](/ftso/feeds) page.
261+
To do so, submit a "New Feed Request" via an issue in the [Flare Developer Hub](https://github.com/flare-foundation/developer-hub) GitHub repository.
262+
The request should include a business justification and a link to the verified contract on a block explorer.
263+
The Flare Foundation will review the submission for eligibility.
264+
265+
{/* prettier-ignore */}
266+
<NewGithubIssue issueType="feed_request">Propose Custom Feed</NewGithubIssue>
267+
268+
## Conclusion
269+
270+
You now have the tools to create FTSO Custom Feeds, bringing diverse, verifiable data from any API onto the Flare network.
271+
This capability greatly expands the possibilities for DeFi and other onchain applications.
272+
Remember, the security of your Custom Feed depends entirely on its smart contract logic and data verification process.
273+
Prioritize careful design and rigorous testing in your implementations.

0 commit comments

Comments
 (0)