如何使用Flare的内置预言机:FTSOv2构建数据驱动的dApp

本文介绍了如何在Solidity智能合约中使用Flare的FTSOv2,并解释了其特性,以及如何在dApp中高效集成FTSOv2。此外,文章通过一个众筹dApp的实例,展示了如何利用FTSOv2的实时价格数据将FLR捐款动态转换为美元,从而实现以美元为目标的捐款筹集。

本文将指导你了解在 Solidity 智能合约中使用 Flare 的 FTSOv2 的技术细节,解释其功能,并为在 dApp 中进行高效集成提供参考。

前言—

智能合约是在区块链 VM 上运行的独立的代码片段。 它们无法知道任何动态的现实世界信息,例如 - “谁是美国现任总统?” 或 “现在 BTC 的价格是多少?” - 除非提供手动输入。

预言机使这成为可能,它以“去中心化”的方式向智能合约提供外部数据。 但是这些预言机是区块链的外部提供商,这意味着你的合约现在并非完全封闭,它们有一条来自外部世界的管道,并且该管道可能会被切断,或者有奇怪的东西流入。 明白了吗? 这意味着使用预言机将你的应用程序的有效控制权从你手中转移出去,并与预言机提供商共享。

Flare Network 的构建旨在解决这个问题。 它是一个具有 FTSO 的区块链,FTSO 是一种嵌入式预言机:一种内置于网络中的数据系统,嵌入在网络本身中。 这意味着你对预言机的信任程度与对区块链的信任程度相同,这使得 Flare 成为数据区块链。 公平吗?

现在问问自己,对于构建数据驱动的 dApp(或 3dApp,这是我刚才自己创造的一个术语)来说,这难道不是最好的解决方案吗? 我的意思是,你完全在链上工作,并在链中工作,并以最可靠的方式获得实时数据。

那么让我们在 Flare 上构建吧!

来自 FTSO 的问候

Flare 时间序列预言机(FTSO)v2 是 Flare Network 上的一个去中心化预言机系统,它为各种资产提供实时、可靠和防篡改的价格信息。 将 FTSOv2 集成到你的智能合约中,使你能够构建高级 DeFi、预测市场和其他需要准确链上价格数据的 dApp。

了解更多 - https://dev.flare.network/ftso/overview

你可以记住的是,FTSO 协议在将外部数据引入链上时不会损害安全性——它嵌入在 Flare 的核心共识和网络架构中。 这与依赖于单独的链下节点网络来收集、聚合并将数据发布到智能合约的其他预言机系统从根本上不同。

这确保了数据是原生验证的透明的,并且由保护 Flare 链本身的相同机制保护,从而减少了外部信任假设并提高了链上应用程序的可组合性。

FTSO 提供:

  • 高频率、高度可靠的价格更新
  • 多资产支持(例如,FLR/USD、BTC/USD、ETH/USD 等)

它是 Flare 上 DeFi 协议的核心构建块,支持价格保护的众筹、合成资产等用例。

如何在你的智能合约中使用 FTSOv2

关键函数和接口

  • FtsoV2Interface:与 Ftsov2 交互的主要接口。
  • ContractRegistry:提供 FtsoV2 合约的当前地址,确保可升级性和灵活性。

主要函数

  • getFeedById(bytes21 feedId):返回给定 feed 的最新价格、小数位数和时间戳。
  • getFeedsById(bytes21[] calldata feedIds):批量查询多个 feed。
  • getFeedByIdInWei(bytes21 feedId):返回 Wei 中的价格(对于直接链上计算很有用)。

示例 Feed ID

  • FLR/USD:0x01464c522f55534400000000000000000000000000
  • BTC/USD:0x01464254432f555344000000000000000000000000

所有 Feed ID 可在此处获得 - https://dev.flare.network/ftso/scaling/anchor-feeds

用法:

  1. 导入接口并查询价格 Feed

你需要 FtsoV2Interface 和 ContractRegistry 的 ABI 或接口定义。 这些可在 Flare periphery contracts 包中找到。

import {FtsoV2Interface} from "@flarenetwork/flare-periphery-contracts/coston2/FtsoV2Interface.sol";
import {ContractRegistry} from "@flarenetwork/flare-periphery-contracts/coston2/ContractRegistry.sol";

bytes21 public constant FLR_USD = 0x01464c522f55534400000000000000000000000000;

function getFlrUsdPrice() public returns (uint256 price, int8 decimals, uint64 timestamp) {
    FtsoV2Interface ftsoV2 = ContractRegistry.getFtsoV2();
    return ftsoV2.getFeedById(FLR_USD_ID);
}

ftsoV2.getFeedById(FLR_USD_ID) 返回你 -

  • price:资产的最新价格(按小数位数缩放)
  • decimals:小数位数(例如,5 表示价格以 10⁵ 为单位)
  • timestamp:上次更新的 Unix 时间戳

技术细节和最佳实践

1. 处理小数

FTSO 价格返回时带有一个小数位数 。 始终相应地缩放你的计算:

(uint256 price, int8 decimals, ) = getFlrUsdPrice();
uint256 usdValue = (amountInFlr * price) / (10 ** uint8(decimals));

2. Gas 考虑因素

  • 当你需要多个价格时,批量查询更节省 gas。
  • 避免在紧密循环或高频函数中调用 FTSO 函数,以最大限度地降低 gas 成本。

3. 可升级性

  • 始终使用 ContractRegistry.getFtsoV2() 来获取当前的 FTSO 合约地址。 这确保你的合约与未来的 FTSO 升级保持兼容。

4. 安全性

  • 永远不要信任链下价格数据来执行关键逻辑。 始终直接从链上 FTSO 获取价格。
  • 对任何基于价格数据处理资金的合约使用 ReentrancyGuard 和适当的访问控制。

使用 FTSOv2 构建众筹 dApp

我已经准备了一个示例实现供你参考—

Github 链接 — 存储库包含一个完整的 CrowdFunding dApp,可帮助你筹集以美元为单位的目标捐款,但以 FLR 为单位付款。 使用 FTSOv2 通过实时价格信息动态地将 FLR 捐款转换为美元。

Youtube 教程— 视频中,我帮助解释和开发该应用程序。

Faucet-获取 Coston2 测试网的测试代币

你可以在 README 文件和 Youtube 视频中找到你需要的一切

在你的收件箱中获取 0xMaues 的故事

免费加入 Medium 以获取来自此作者的更新。

有两个合约在

backend/contracts/—

  1. CrowdFundingFactory.sol 管理并帮助创建 Campaigns(子合约)的中枢
//在存储库中查找整个合约
....
....
function createCampaign(
        address _beneficiary,
        uint256 _fundingGoalInUsd,
        uint256 _durationInDays,
        string memory _title
    ) external returns (address) {

        CrowdfundingCampaign newCampaign = new CrowdfundingCampaign(
            msg.sender,
            _beneficiary,
            _fundingGoalInUsd,
            _durationInDays,
            _title
        );
....
....
function getCampaignCount() external view returns (uint256 count) {
        return campaigns.length;
    }

function getCampaigns() external view returns (address[] memory) {
        return campaigns;
    }

function getCreatorCampaignCount(address creator) external view returns (uint256 count) {
        return creatorCampaigns[creator].length;
    }

function getCreatorCampaigns(address creator) external view returns (address[] memory) {
        return creatorCampaigns[creator];
    }

2. CrowdFundingCampaign.sol 从 Factory 合约创建,处理从接收捐款到从 FTSOv2 获取数据再到处理 campaign 的最终确定以及在截止日期前未达到目标时申请退款的所有事情。

....
....
function getFlrUsdPrice() public returns (uint256 price, int8 decimals, uint64 timestamp) {
        FtsoV2Interface ftsov2 = ContractRegistry.getFtsoV2();
        return ftsov2.getFeedById(FLR_USD_ID);
    }

    function contribute() external payable nonReentrant {
        require(!campaignClosed, "Campaign Closed");
        require(block.timestamp <= deadline, "Deadline passed");
        require(msg.value > 0, "Zero contribution");
        contributions[msg.sender] += msg.value;
        totalFundsRaised += msg.value;
        emit FundingReceived(msg.sender, msg.value, totalFundsRaised);
        checkGoalReached();
    }

    function checkGoalReached() public {
        if (campaignClosed) return;

        (uint256 currentPrice, int8 currentDecimals, ) = getFlrUsdPrice();
        uint256 raisedValueInUsd = (totalFundsRaised * currentPrice) / (10 ** uint8(currentDecimals));

        if (raisedValueInUsd >= fundingGoalInUsd) {
            fundingGoalReached = true;
        }
    }
....
function claimRefund() external nonReentrant {...}
function withdrawFunds() external nonReentrant {...}
function finalizeCampaign() external onlyOwner nonReentrant {...}
function getCampaignInfo() external view returns (...}

backend/scripts/deploy.ts —

部署和测试脚本(需要五个帐户进行测试,因此请将 5 个私钥粘贴到 .env 中)

PRIVATE_KEY_1=""
PRIVATE_KEY_2=""
PRIVATE_KEY_3=""
PRIVATE_KEY_4=""
PRIVATE_KEY_5=""

进入 /backend 文件夹并运行(按顺序)-

npx hardhat compile
npx hardhat run scripts/deploy.ts --network coston2

你应该看到类似以下的输出 —

浏览 deploy.ts 并尝试匹配合约中的函数,然后注意此输出如何显示 dApp 的真实场景

这很重要。 Factory 合约是你需要在前端中使用它来与合约交互并创建 campaign,以及各自的 ABI。

--- 部署 FACTORY ---
CrowdfundingFactory deployed to: 0x1300d4F7181316E25E9E060F20257c41c7Fd51a8

尝试从临时 campaign 获取实时 FLR/USD 价格...
成功获取 FLR/USD 价格:0.02474(原始值:247400,FTSO 小数位数:7)

好的,你的 CrowdFundingFactory 合约已部署。

现在 /frontend/ 中的前端

是使用 NextJs、用于钱包连接的 RainbowKit 以及用于交互的 Wagmi 和 Viem 构建的。

但在我们继续之前,请回到

/backend/artifacts/contracts

并找到合约的 JSON ABI。

复制它们,并替换

/frontend/src/abis

中的现有 ABI

现在转到

frontend/src/hooks/useCrowdfunding.ts —

它包含 React 组件中使用的所有函数,以简化与合约的交互、处理价格等。

你可以在第一行看到—

// src/hooks/useCrowdfunding.ts
import { useMemo } from 'react';
import { useReadContract, useWriteContract, useWaitForTransactionReceipt, useAccount } from 'wagmi';
import { parseEther, formatUnits, type Address } from 'viem'; // 从此处删除 parseUnits,因为它不用于 createCampaign
import type { Abi } from 'viem';

//abi import
import CrowdfundingFactoryJSON from '../abis/CrowdfundingFactory.json';
import CrowdfundingCampaignJSON from '../abis/CrowdfundingCampaign.json';

// 替换为你实际部署的 factory 地址
const FACTORY_ADDRESS = '' as const;

在这里粘贴我们之前谈到的 Factory 地址:

// 替换为你实际部署的 factory 地址
const FACTORY_ADDRESS = '' as const;

现在浏览此文件中的所有 hook 及其函数,例如:

useCrowdfundingFactory, useCrowdfundingCampaign

handleCreateCampaign, handleContribute, handleFinalizeCampaign, handleWithdrawFunds, handleClaimRefund.

这些是处理程序,可以简化从前端的按钮与实际部署的合约进行交互的过程。

现在,你有一个可供使用的 dApp。

只需转到 /frontend 并运行(按顺序)—

npm i
npm run dev

在你的浏览器上转到 http://localhost:3000/,你应该看到类似以下内容:

继续,连接你的钱包并尝试一下。 当你看到 FTSOv2 价格 feed 对 FLR/USD 的动态定价时,之前的所有步骤现在都应该有意义了。

希望本文能帮助你使用 FTSOv2 构建,并为你提供灵感和指导,以构建一些使用价格 feed 的炫酷 dApp。

Twitter 上与我联系,或在下方发表评论——欢迎提供反馈、问题和构建!

  • 原文链接: blog.blockmagnates.com/h...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
blockmagnates
blockmagnates
The New Crypto Publication on The Block