乐趣区

关于区块链开发:成长随笔开源中心化区块链预言机

前言

2020~2021 是一个 Defi 的时代,很多公链都开始反对 EVM,开始搞 web3.0。包含,Algorand,BCH,BSC,NEO 和 Conflux 等等等等。很多公链生态利用都是呈现爆发式呈现,例如 Gamefi,多种 Defi 协定。然而,Defi 各种生态里,预言机服务是十分十分重要的一个服务。很多 Defi 协定都须要一个预言机合约进行报价,为了解决新生公链生态没有预言机的问题,我开源了我在 TriangleDAO 写的一个中心化区块链预言机。外围团队 or 外围社区人员能够部署到对应的链上,做一个长期的中心化区块链预言机计划。这同样也适宜联盟链,联盟链应该是很少或者简直没有预言机服务的吧。

开源地址:

https://github.com/shanxuanch…

技术架构 (v1.0)

外围其实是 2 个合约:一个是 TriangleOracle.sol,另外一个 PriceFeed.sol。

TriangleOracle.sol 继承了 IERC2362 规范,次要是 2 个函数:一个是 putPirce 和 ValueFor。有一个定时的过程每 5 分钟会从 Binance 和 Okex 读取价格,而后触发 putPrice,把价格写到链上。



pragma solidity 0.6.11;
import "./Ownable.sol";
import "./Interfaces/IERC2362.sol";

contract TriangleOracle is Ownable, IERC2362 {    

    // 32 + 16 + 16 = 64 bytes
    // default 1e+18 
    struct PriceOracle {
        int256 price;              // 16 bytes
        uint256 timestamp;
        uint256 status;           // 16 bytes
    }
    PriceOracle latestPrice;

    event PutLatestTokenPrice(int256 price, uint256 timestamp, uint256 status);

    function putPrice(bytes32 _id, int256 price, uint256 timestamp) public onlyOwner {
        
        uint256 _status = 200;
        latestPrice = PriceOracle ({
            price: price,
            timestamp: timestamp,
            status: _status
        });

        emit PutLatestTokenPrice(price,timestamp, _status);

    }

    function valueFor(bytes32 _id) external view override returns(int256,uint256,uint256) {return (latestPrice.price, latestPrice.timestamp, latestPrice.status);
    }

}

PriceFeed 合约的职责是读 TriangleOracle.sol 的价格,而后把价格保留到 lastGoodPrice 这个变量中,以便其余合约读取这个价格。这里有其余逻辑,例如 scale。 比拟重要的是,这里波及到可更新和合约交互的逻辑,让我对智能合约的了解晋升了一个数量级。


// SPDX-License-Identifier: MIT
pragma solidity 0.6.11;

import "./Dependencies/SafeMath.sol";
import "./Interfaces/IERC2362.sol";
import "./Interfaces/IPriceFeed.sol";
import "./Dependencies/Initializable.sol";
import "./Dependencies/CheckContract.sol";

contract PriceFeed is CheckContract, IPriceFeed, Initializable {
    using SafeMath for uint256;

    uint constant public TARGET_DIGITS = 18;

    // CFX/USDT assertID
    bytes32 constant public assetID = bytes32(0x65784185a07d3add5e7a99a6ddd4477e3c8caad717bac3ba3c3361d99a978c29);

    struct WitnetResponse {
        int256 value;
        uint256 _timestamp;
        uint256 status;
    }

    IERC2362 public witnet;
    uint public lastGoodPrice;

    event LastGoodPriceUpdated(uint _lastGoodPrice);

    // --- Dependency setters ---

    function initialize(address _IWitnetCFXUSDTAddress) public initializer {checkContract(_IWitnetCFXUSDTAddress);
        witnet = IERC2362(_IWitnetCFXUSDTAddress);
        WitnetResponse memory witnetResponse = _getCurrentWitnetResponse(assetID);
        _storePrice(witnetResponse);
    }

    function getPrice() external view returns (uint) {return lastGoodPrice;}

    function fetchPrice() external override returns (uint) {WitnetResponse memory witnetResponse = _getCurrentWitnetResponse(assetID);
        _storePrice(witnetResponse);
        return lastGoodPrice;
    }

    function _getCurrentWitnetResponse(bytes32 _assetID) internal view returns(WitnetResponse memory response) {try witnet.valueFor(_assetID) returns (
            int256 value,
            uint256 _timestamp,
            uint256 status
        ) {
            response.value = value;
            response._timestamp = _timestamp;
            response.status = status;
        } catch {
            // If call to Chainlink aggregator reverts, return a zero response with success = false
            return response;
        }
    }

    function _storePrice(WitnetResponse memory witnetResponse) internal {require(witnetResponse.status == 200, "witnet Response is not 200. response error.");
        uint goodPrice = _scaleWitnetPriceByDigits(uint(witnetResponse.value), 6);
        if (goodPrice != lastGoodPrice) {
            lastGoodPrice = goodPrice;
            emit LastGoodPriceUpdated(lastGoodPrice);
        }
    }

    function _scaleWitnetPriceByDigits(uint _price, uint _answerDigits) internal pure returns (uint) {
        /*
        * Convert the price returned by the Chainlink oracle to an 18-digit decimal for use by Liquity.
        * At date of Liquity launch, Chainlink uses an 8-digit price, but we also handle the possibility of
        * future changes.
        *
        */
        uint price;
        if (_answerDigits >= TARGET_DIGITS) {
            // Scale the returned price value down to Liquity's target precision
            price = _price.div(10 ** (_answerDigits - TARGET_DIGITS));
        }
        else if (_answerDigits < TARGET_DIGITS) {
            // Scale the returned price value up to Liquity's target precision
            price = _price.mul(10 ** (TARGET_DIGITS - _answerDigits));
        }
        return price;
    }

}

总结

地址:https://github.com/shanxuanch…

总体而言没有什么技术难度,就是一个定时脚步读写工作而已。中心化的预言机服务是比较简单的,然而去中心化就十分有难度了。当初为止有靠近 $300W 的 TVL 了,尽管 token 的价格稳定很大,然而我心田十分有信念。起因?可能是我提前晓得各种利好吧。如果 Token 解锁了,我想所有帮忙我一路上爬过去的人都散发一部分 token,想了想,可能这是我去北京之前惟一能拿得出手的礼物了。

退出移动版