如何通过 Chainlink Price Feeds获得加密资产的历史价格

这篇具有很好参考价值的文章主要介绍了如何通过 Chainlink Price Feeds获得加密资产的历史价格。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

对于 Web3 应用来说,获取加密资产的价格数据是一个很常见的要求,许多协议都需要依赖于高质量且及时更新的数据来运营DeFi 应用并且保证其安全性。除此之外,智能合约开发者有的时候也需要获取加密资产的历史数据。

在这篇文章中,我们将演示如何从 Chainlink Price Feeds 中获得历史价格数据,并且在链上验证获得的结果,你可以在这里查看代码。

获得历史价格数据的需求

在过去的几年中,我们见证了 DeFi 爆炸式增长,这些 DeFi 协议的一个共同的需求是它们需要非常安全,准确和值得信任的数据。Chainlink Price Feeds 已经成为了在 DeFi 生态中最常被使用的价格预言机,并且集成进来数十个百亿美元级别的协议,比如 Aave,Synthetix 和 Trader Joe。

Price Feeds 最常见的使用场景是从既定的资产对中获取最新的价格数据。然而,有的时候 DeFi 协议或者 dApp 也会有查看某个资产在某个时间点的的历史价格。相关案例很多,比如一个金融产品会比较不同时间段的资产价格,比如加密资产保险就会使用历史数据来计算出来市场波动率,然后动态调整保证金。

历史价格数据可以通过很多市场数据的 API 被获得,然后通过 Chainlink Any API 的功能在链上获取。然而这个解决方案有安全性的顾虑:就是数据源和传递数据的预言机可能是中心化的,并且没有办法验证这个数据是否是准确的。就像实时价格数据一样,历史价格数据也需要去中心化的方式获得,同时也需要足够多的数据源,这就要求不能使用单一的数据源,交易所或者 API。

Chainlink Price Feeds 通过多个数据提供商,提供去中心化的,高质量的价格数据解决了问题。当需要历史价格数据的时候,Price Feeds 可以保证价格数据是准确的,同时某个时间点的价格数据来源是整个市场,而不是单一交易所。

当然,还有一些其他解决方案来获取 Chainlink Price Feed 的数据,比如 Reputation.link’s API 或者是 Graph 的一个 subGraph。尽管这些都是有效的解决方案,但是它们还是依赖于单一 API 或者是链下数据是正确的这个前提条件。

在这个解决方案中,我们将展示通过使用 Chainlink 语言来进行必要的链下计算,然后从 Chainlink Price Feeds 中获得历史数据。通过使用 Chainlink Price Feeds 这个入口,用户合约可以通过信任最小化的方式在链上获得历史价格数据。

Price Feed 合约简介

从用户合约的视角来看,Chainlink Price Feeds 智能合约大体上可以分为两个类型:代理合约和聚合合约。

代理合约代表一个价格对(像是 ETH/USD),同时也是用户要交互的合约。这个合约有多个函数,可以根据特定的参数获取 round 数据,比如说获取某个交易对最新的 round 数据或者在某个 round 中获取价格数据。

代理合约中有所有的聚合合约的信息,同时也知道现在正在使用哪一个聚合合约。可以通过 aggregator 或者给 phaseAggregator 传入正确 phase ID 来获得这些信息。在下面的例子中,我们可以看到 Kovan ETH/USD 这个代理合约中,第二个聚合合约正在被使用(phase ID = 2)。

如何通过 Chainlink Price Feeds获得加密资产的历史价格
获得 Kovan ETH/USD 中第二个聚合合约

如何通过 Chainlink Price Feeds获得加密资产的历史价格
获得 ETH/USD 代理合约中当前使用的聚合合约

这些聚合合约在实现的时候稍有不同,这取决于它们是在哪个版本上开发的(FluxAggregator,legacy Aggregator 等等),但是它们都存储了聚合以后的 round 数据,同时也都有一个函数让预言机可以提交价格数据。

如何通过 Chainlink Price Feeds获得加密资产的历史价格
Chainlink Price Feeds 合约架构

解决方案概述

Chainlink Price Feeds 的价格数据是存在链上的,同时开发者可以执行 getLatestRoundData 函数来获取某一个交易对最新的价格数据。然而,从 Price Feeds 中获取历史数据却并不简单,过程比较复杂。

Chainlink Price Feeds 存储聚合以后的价格数据,每一个数据都会有一个独特的 round ID。当价格对的波动超过特性的波动阈值,或者超过心跳时间上限,一个新的 round 就会产生。如果开发者知道历史价格数据的 round ID,那么可以很容易通过 getHistoricaPrice 这个函数找到这个历史价格。然而,roundID 和时间戳,区块,或者任何其他可以被用来决定时间的变量都没有直接的联系。

另外要注意的是,Chainlink Price Feed 有很多版本的聚合合约,比如 FluxAggregator 和使用 Off-Chain Reporting 的 OffchainAggregator 合约。一个交易对的喂价有可能在以前的一段时间里使用的是 FluxAggregator,然后换成兼容 OCR 的 OffchainAggregator 来更新价格。所以在找到对应时间的 round ID 的时候,需要分辨不同的聚合合约版本。

另外因为 round ID 在面向用户的代理合约和底层的聚合合约中,是被有目的地分成不同的 phase,所以代理合约中的 round ID 比聚合合约中的要大很多。代理合约中的 round ID 总是需要自增 1,而底层的聚合合约在每次部署的时候 round ID 都是重新从 1 开始计数。通过下面的方法,可以由聚合合约的 round ID 计算出代理合约的 round ID,首先 phase 是基于代理合约部署的聚合合约的顺序(第一次,第二次,第三次等等),originalRoundId 就是聚合合约部署的 round。你可以通过调用代理合约的 phaseAggregators 这个 getter 方法来获得 phase 参数。

external proxy round ID = uint80(uint256(phase) << 64 | originalRoundId);

最后,并不是每一个 round 都会有价格数据。有一些 round(主要是在测试网)可能没有价格或者时间戳数据,主要是因为当时连接超时或者一些环境问题。

因为这些复杂性,在链上获得一个可验证且准确的历史数据是很复杂的,比如你需要做一个循环,就要去遍历大量的 round 数据,或者在链上存储一个 round 和时间戳的 mapping,但是这些操作都非常贵。

除了能够给智能合约提供链上数据和时间,Chainlink 去中心化预言机网络还提供了一个通用的框架来做链下运算,这样用户就不用存储大量的链上数据,也不用对未知数量的 round 做循环了。链上合约还可以通过一个运行着外部适配器(external adapter)的预言机来获得某个时间点的特定交易对价格。预言机把 round ID 返回到链上,然后用户合约可以马上使用这个数据,并通过历史数据 API 验证链上数据来验证数据的准确性,验证方式是通过 round ID 获得价格数据,然后与返回的价格数据比较。

这个解决方案所基于的概念是:预言机会处理任何区块链自身所不能处理的数据,或者区块链因为容量限制和效率不能或不应该处理的问题。除此以外,通过这种方式获得历史价格数据,还有很多优势:

  • 不需要在链上存储大量的数据,或者对链上数据进行大量的循环检查。
  • 使用链上的函数获取历史数据,和从链下取得的数据比较,杜绝恶意预言机提供的错误数据的风险。
  • 外部适配器是无状态的,不会像 Chainlink 节点运营商一样存储数据并且提供一个通用的方法来获取历史数据。
  • 外部适配器提供的解决方案不依赖于外部 API 或者其他系统,它直接与链上数据交互。

如何通过 Chainlink Price Feeds获得加密资产的历史价格
在智能合约中使用外部适配器

怎样使用 Chainlink 预言机获得加密资产历史价格数据

创建一个历史价格数据请求

在初始化一个历史价格数据的请求之前,用户合约需要给一个预言机提交一个 API 请求,这个预言机在自己的任务中需要运营一个自定义的历史价格数据的外部适配器。在发送请求的参数中,用户合约需要传入有效的代理合约地址和时间戳(Unix 时间)以返回价格数据。

function getHistoricalPrice(address _proxyAddress, uint _unixTime) public returns (bytes32
requestId)
    {

        Chainlink.Request memory request = buildChainlinkRequest(jobId, address(this), 
this.singleResponseFulfill.selector);

        // Set the URL to perform the GET request on
        request.add("proxyAddress", addressToString(_proxyAddress));
        request.add("unixDateTime", uint2str(_unixTime));

        //set the timestamp being searched, we will use it for verification after
        searchTimestamp = _unixTime;

        //reset any previous values
        answerRound = 0;
        previousRound = 0;
        nextRound = 0;
        nextPrice = 0;
        nextPriceTimestamp = 0;
        previousPrice = 0;
        previousPriceTimestamp = 0;
        priceAnswer = 0;
        priceTimestamp = 0;

        // Sends the request
        return sendChainlinkRequestTo(oracle, request, fee);
    }

历史价格数据外部适配器

一旦 Chainlink 节点收到这个请求,它会将输入信息发送给历史价格外部适配器,这个外部适配器将会找时间戳所对应的 round ID,以及这个 round ID 之前和之后的 round ID。这两个额外的 round ID 在验证环节中会被用到,我们需要通过这些信息来验证价格数据的时间是最接近所查找的时间戳的,在这个 round 中 updatedAt 这个时间戳一定要比要搜索的时间戳参数要小。

一旦外部适配器接收到代理合约的地址和要查找的时间戳参数之后,它会进行以下的计算:

  • 对传入的地址和时间戳进行验证
  • 确定哪一个聚合合约包含要寻找的时间戳的 round
  • 在上一步找到的聚合合约中,获取存储在聚合合约中的 round ID 列表(使用 eth_getlogs)
  • 在返回的 round ID 列表中,进行二分查找,找到包含时间戳的round ID。这个搜索的时间复杂度是 O(logN),相比之下,线性搜索的的时间复杂度更高是 O(N)
  • 如果找到的 round 是空或者无效(因为超时的原因等等),这个算法会分辨出在二分查找中的空值,然后把这些 round 排除掉,再在新的列表中进行新的二分查找。
  • 当被查找的 round ID 被找到之后,外部适配器会使用这个聚合合约的 round ID 以及它前面和后面的 round ID,算出这三个 round ID 在代理合约中对应的 round ID,然后在适配器中返回 roundAnswerearlierRoundAnswerlaterRoundAnswer 这三个值。
  • 处理历史价格数据的请求的预言机会获取上一步的三个值,然后在通过 multi-variable response 的功能链上返回给链上的用户合约。
{
    "jobRunID": "534ea675a9524e8e834585b00368b178",
    "data": {
        "roundAnswer": "36893488147419111519",
        "earlierRoundAnswer": "36893488147419111518",
        "laterRoundAnswer": "36893488147419111520"
    },
    "result": null,
    "statusCode": 200
}

在链上验证结果

使用中心化数据源或者预言机来获得历史价格数据,会给智能合约带来潜在的安全风险。然而,在这个例子中,对于预言机返回的 round ID,我们可以利用现存的历史价格数据函数在链上验证 round ID,这样就可以用信任最小化的方式验证获取到的历史价格数据。在这种方式中,数据还是从链上获取的,只不过 round ID 是由外部的预言机计算出来的。

我们可以通过以下的方式来验证 round 数据,然后使用这个数据来获取到最终的历史数据:

首先,我们需要验证三个 round(answerRound, previousRound, nextRound) 中包含的有效的返回 round 数据

//verify the responses
        //first get back the responses for each round
        (
            uint80 id,
            int price,
            uint startedAt,
            uint timeStamp,
            uint80 answeredInRound
        ) = priceFeed.getRoundData(_answerRound);
        require(timeStamp > 0, "Round not complete");
        priceAnswer = price;
        priceTimestamp = timeStamp;

        (
            id,
            price,
            startedAt,
            timeStamp,
            answeredInRound
        ) = priceFeed.getRoundData(_previousRound);
        require(timeStamp > 0, "Round not complete");
        previousPrice = price;
        previousPriceTimestamp = timeStamp;

        (
            id,
            price,
            startedAt,
            timeStamp,
            answeredInRound
        ) = priceFeed.getRoundData(_previousRound);
        require(timeStamp > 0, "Round not complete");
        nextPrice = price;
        nextPriceTimestamp = timeStamp;

下一步,我们验证这些 round 数据以及它们包含的时间戳。

  • 保证 round 的顺序是正确的
  • 保证三个 round 中包含的时间戳时间顺序是对的
  • 如果这些 round 中有任何的间隔(比如 previousRound = 1625097600 而 answerRound = 1625097605),要确保在这两个 ID 之间没有任何有效的 round ID。所以在这个例子中,previousRound = 1625097600 并且 answerRound = 1625097605,这个合约需要保证 1625097601, 1625097602, 1625097603 和 1625097604 这些 round 不会返回有效数据
//first, make sure order of rounds is correct
        require(previousPriceTimestamp < timeStamp, "Previous price timetamp must be < answer
timestamp");
        require(timeStamp < nextPriceTimestamp, "Answer timetamp must be < next round
timestamp");

        //next, make sure prev round is before timestamp that was searched, and next round is
after
        require(previousPriceTimestamp < searchTimestamp, "Previous price timetamp must be < 
search timestamp");
        require(searchTimestamp < nextPriceTimestamp, "Search timetamp must be < next round 
timestamp");
require(priceTimestamp <= searchTimestamp, "Answer timetamp must be less than or equal to 
searchTimestamp timestamp");

        //check if gaps in round numbers, and if so, ensure there's no valid data in between
        if (answerRound - previousRound > 1) {
            for (uint80 i= previousRound; i<answerRound; i++) {
                (uint80 id,
                int price,
                uint startedAt,
                uint timeStamp,
                uint80 answeredInRound
                ) = priceFeed.getRoundData(i);
                require(timeStamp == 0, "Missing Round Data");
            }
        }

        if (nextRound - answerRound > 1) {
            for (uint80 i= answerRound; i<nextRound; i++) {
                (uint80 id,
                int price,
                uint startedAt,
                uint timeStamp,
                uint80 answeredInRound
                ) = priceFeed.getRoundData(i);
                require(timeStamp == 0, "Missing Round Data");
            }
        }

如果上述的检查都可以通过,那么返回的 round (answerRound) 的价格数据,就是某个交易对在某个时间点上经过验证的历史价格数据。

总结

Chainlink Price Feeds 提供一种方法让 Solidity 智能合约获得高质量的价格数据。除此之外,Chainlink 的预言机框架可以实现链下计算,允许开发者可以通过信任最小化的模式,获得安全可验证的历史价格数据。

您可以关注 Chainlink 预言机并且私信加入开发者社区,有大量关于智能合约的学习资料以及关于区块链的话题!文章来源地址https://www.toymoban.com/news/detail-423405.html

到了这里,关于如何通过 Chainlink Price Feeds获得加密资产的历史价格的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 操作指南 | 如何使用Chainlink喂价功能获取价格数据

    Chainlink的去中心化预言机网络中的智能合约包含由运行商为其他智能合约(DApps)使用或截取所持续更新的实施价格数据。其中有两个主要架构:喂价和基础要求模型。此教程将会展现如何在Moonbeam、Moonriver或是Moonbase Alpha测试网上使用喂价功能。 这些信息仅用于教育目的,永

    2024年02月11日
    浏览(50)
  • ULinx: 全新的加密资产兑换方式 高效 便捷

           

    2024年02月03日
    浏览(36)
  • 开创加密资产新纪元:深度解析ERC-314协议

    随着加密资产市场的不断发展和区块链技术的日益成熟,新的协议和标准不断涌现,其中包括了ERC-314协议。本文将深入分析ERC-314协议的特点、功能以及对加密资产市场可能产生的影响。 1. ERC-314协议简介 ERC-314协议是一项建立在以太坊区块链上的新提案,旨在为NFT提供更加标

    2024年04月15日
    浏览(40)
  • 邮件服务支持Exchange协议,资产历史账号支持设置保留数量,JumpServer堡垒机v3.10.2 LTS版本发布

    2024年1月22日,JumpServer开源堡垒机正式发布v3.10.2 LTS版本。JumpServer开源项目组将对v3.10 LTS版本提供长期的支持和维护,并定期迭代发布小版本。欢迎广大社区用户升级至v3.10 LTS版本,以获得更佳的使用体验。 在v3.10.2 LTS版本中,JumpServer的邮件服务功能新增支持Exchange协议。目

    2024年01月24日
    浏览(38)
  • 通过数组的指针获得数组个数

      这几天学习智能指针时,自己在练习写个管理数组指针的类时碰到了通过数组指针获取数组个数的问题 1.在网上查询了通过数组指针获取数组个数的方法,对于自定义数据在前四个节点保存了数组个数   测试是成功的,但是是对于内置数据类型int,double,char等就不成功了 2.又晕菜

    2024年02月08日
    浏览(34)
  • 赛联区块链培训:Web3的核心要素——区块链、加密资产、智能合约和预言机

    在2008年,中本聪发布了比特币白皮书,彻底颠覆了我们对数字化交易的概念,并首次提出了一种无需可信中间方的安全在线交易模式。中本聪写道:“需要基于加密证明,而非信任,来建立电子支付系统。” 智能合约被发明后,去中心化的互联网模式进入公众视野。如果说

    2024年02月07日
    浏览(57)
  • 欧盟加密资产市场监管法案(Markets in Crypto Assets Regulation Bill,MiCAR法案)整体简介

    1# 2022年10月,欧盟立法者以28票对1票通过了一项旨在监管数字资产市场的里程碑式立法——《加密资产市场监管法案》(MiCAR法案),该法案将转交给欧洲议会,在下一届议会会议上进行最后表决,于2024年生效。这是欧盟数字金融一揽子计划的一部分,旨在进一步促进和支持

    2024年02月02日
    浏览(41)
  • UE5.1编辑器拓展【二、脚本化资产行为,快速更改资产名字,1.直接添加前缀或后缀2.通过资产类判断添加修改前缀】

    目录 了解相关的函数 第一种做法:自定义添加选择资产的前缀或后缀 代码 效果 第二种做法:通过映射来获取资产类型添加前缀和修改前缀 映射代码 代码 效果 在之前一章中,我们创建了插件,用来扩展编辑器的使用: UE5.1编辑器拓展【一、脚本化资产行为,通知,弹窗,

    2024年02月07日
    浏览(46)
  • 技术干货|通过 Substrate - IBC 实现 Substrate 资产跨链

    全长 2760 字,预计阅读 10 分钟 作者:DaviRain 撰文:MiX 微信交流:MixMetaverse 章鱼网络 DaviRain 受邀参加 RUST.CC 中国大会,分享主题为《通过 Substrate - IBC 实现 Substrate 资产跨链》的 Workshop。主要讨论了 IBC 跨链通信协议,以及 Substrate-IBC 在 Substrate 上实现的 IBC 协议,最后通过实

    2024年01月19日
    浏览(50)
  • uni-app通过uni.getSystemInfoSync()获得的手机信息

    突发奇想,记录一下,hbuilder的真机测试可以获取到哪些手机信息。 console.log(uni.getSystemInfoSync()) 打印之后的信息为: 假设,我设计一个页面,设计图如下所示:已知button部分的高度为100rpx,空白求空白部分的高度。 设:空白部分高度为heightW,空白部分的高度为: uni.height

    2024年02月20日
    浏览(36)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包