以去中心化方式从 Chainlink 获取代币历史价格数据的最佳方式是什么?

What is the best way to access historical price data from Chainlink on a token in a decentralised manner?

我需要获取代币从特定时间到最近一轮的 Chainlink 价格。这个时间根据用户输入而变化,但根据令牌的心跳会相对较短 windows(最多 1 天到 2 周)。这用于计算智能合约和应用程序主页上的支付价格。

要获取历史价格数据,Chainlink 需要一个 'roundId',这是一个非增量值。

从 Chainlink 获取给定时间 window 的所有 roundId 或以开放、去中心化且可在 solidity 智能合约中访问的方式记录它们的最佳方式是什么?

Chainlink 喂价的每个代理合约都可以有多个基础聚合器合约,其中一个在任何给定实例中被设置为 'active' 一个。即每当 Chainlink Labs 团队部署新版本的聚合器时,他们都会更新代理合约以将当前聚合器版本设置为新版本,并且所有新价格数据开始写入新聚合器。

基于此,如果您想要过去几周左右的代理 contract/price 对的历史价格数据,您需要转到代理合约的当前聚合器,然后获取当前回合信息(包括时间戳),然后返回循环中的聚合器回合,直到您到达小于搜索参数时间戳的回合。

要查找代理合约的当前聚合器合约地址,您可以在代理合约中调用aggregator getter函数。

阶段 ID 可以被认为是一个递增的 ID 号,代理合同使用它来识别每个聚合器,因为它被添加到代理,所以第一个是 1,第二个是 2 等等。找出什么当前阶段ID是当前聚合器的,您可以在代理合约中使用phaseId getter函数。

聚合轮ID从1开始,每次加1。例如,您可以使用聚合器 latestRound getter 函数找到它存储的最后一轮,然后从该数字作为输入参数开始到 getRoundData,然后将数字减一并循环直到你到达你的时间点。

与聚合器轮次 ID 不同,代理轮次 ID 是像 36893488147419113293 这样的大而长的数字,实际上只是基于聚合器阶段 ID 和聚合器轮次 ID 的派生值。这样做是为了确保代理轮 ID 的值始终在增加,并且不同聚合器的相同轮之间永远不会有任何重叠,例如,来自阶段 ID 1 的聚合器的第 5000 轮应该具有比第 5000 轮更低的代理轮 ID来自具有阶段 ID 2 的聚合器

在 Solidity 中,您可以使用以下公式轻松复制代理轮 ID 派生值,传入聚合器的阶段 ID 和聚合器轮 ID

return uint80(uint256(_phaseId) << 64 | _aggregatorRoundId);

您甚至可以获取该派生值并将其传递给代理合约 getRoundData 函数,并获得与在给定聚合器回合 ID 的聚合器合约中调用 getRoundData 相同的数据结果.

我发现玩弄这些的一个好方法是直接在 etherescan 上与函数交互,并查看返回的数据。即这里是 Kovan ETH/USD proxy and its latest aggregator

关于第二个问题的答案,最好的方法是从'now'开始,即获取最新的聚合器,获取聚合器中的最新轮次,然后通过减少聚合器轮次ID及时返回1,并继续获取定价信息,直到回合时间戳小于您的搜索时间戳。

最后,Solidity 中的循环不是很高效。您可以采用的另一种方法是将外部计算卸载到预言机,预言机可以为您提供正确的回合 ID,然后您可以使用它并在链上验证结果。 Here's an example 采用外部适配器形式的设计模式,可根据时间戳检索历史价格数据。 Kovan 上也有此 运行 的实时版本,请查看自述文件了解更多信息