DeFi開發者:如何調用NEST預言機價格數據

NEST爱好者
本文约7668字,阅读全文需要约31分钟
NEST生態開發者經驗分享,教你輕鬆調用NEST 預言機價格數據。

介紹

介紹

一級標題

白皮書:https://nestprotocol.org/doc/zhnestwhitepaper.pdf

GitHub:https://github.com/NEST-Protocol

NEST Protocol:https://nestprotocol.org/

介紹

一級標題

介紹

一級標題

白皮書:

白皮書

GitHub:

https://github.com/NEST-Protocol/NEST-oracle-V3/blob/master/NestOffer/Nest_3_OfferPrice.sol

了解機制

正文

function addPrice(uint256 ethAmount, uint256 tokenAmount, uint256 endBlock, address tokenAddress, address offerOwner) public onlyOfferMain{
       // Add effective block price information
       TokenInfo storage tokenInfo = _tokenInfo[tokenAddress];
       PriceInfo storage priceInfo = tokenInfo.priceInfoList[endBlock];
       priceInfo.ethAmount = priceInfo.ethAmount.add(ethAmount);
       priceInfo.erc20Amount = priceInfo.erc20Amount.add(tokenAmount);
       if (endBlock != tokenInfo.latestOffer) {
           // If different block offer
           priceInfo.frontBlock = tokenInfo.latestOffer;
           tokenInfo.latestOffer = endBlock;
       }
   }

NEST 預言機以區塊為單位生成價格,如果區塊內沒有價格,則使用最近的區塊價格。

使用報價的方式生成區塊價格,如果一個區塊內有多筆報價,則加權平均。                 每筆報價有25 區塊的驗證時間(吃單),如果驗證時間內沒有被吃單則代表市場認可這筆報價,將會在報價區塊後的25 個區塊價格生效。

NEST預言機價格合約sol 文件

代碼解析

正文

正文

該方法限制了只有“報價合約”才可以調用,保證添加到價格合約中的價格數據的數據源正確。


PriceInfo storage priceInfo = tokenInfo.priceInfoList[endBlock];
priceInfo.ethAmount = priceInfo.ethAmount.add(ethAmount);
priceInfo.erc20Amount = priceInfo.erc20Amount.add(tokenAmount);

輸入參數


描述

function changePrice(uint256 ethAmount, uint256 tokenAmount, address tokenAddress, uint256 endBlock) public onlyOfferMain {
       TokenInfo storage tokenInfo = _tokenInfo[tokenAddress];
       PriceInfo storage priceInfo = tokenInfo.priceInfoList[endBlock];
       priceInfo.ethAmount = priceInfo.ethAmount.sub(ethAmount);
       priceInfo.erc20Amount = priceInfo.erc20Amount.sub(tokenAmount);
   }

ethAmount 報價ETH 數量


tokenAmount 報價ERC20 Token數量            endBlock 價格生效區塊號

tokenAddress 報價的ERC20 Token 合約地址

offerOwner 報價者錢包地址

這三行代碼實現在同一個區塊內加權平均。

修改價格


同樣限制了只有“報價合約”才有權限調用。只有在觸發吃單操作後,才會修改對應生效區塊中的價格,將”添加價格“時的報價數量按照”吃單“規模減掉。

function updateAndCheckPriceNow(address tokenAddress) public payable returns(uint256 ethAmount, uint256 erc20Amount, uint256 blockNum) {
       require(checkUseNestPrice(address(msg.sender)));
       mapping(uint256 => PriceInfo) storage priceInfoList = _tokenInfo[tokenAddress].priceInfoList;
       uint256 checkBlock = _tokenInfo[tokenAddress].latestOffer;
       while(checkBlock > 0 && (checkBlock >= block.number || priceInfoList[checkBlock].ethAmount == 0)) {
           checkBlock = priceInfoList[checkBlock].frontBlock;
       }
       require(checkBlock != 0);
       PriceInfo memory priceInfo = priceInfoList[checkBlock];
       address nToken = _tokenMapping.checkTokenMapping(tokenAddress);
       if (nToken == address(0x0)) {
           _abonus.switchToEth.value(_priceCost)(address(_nestToken));
       } else {
           _abonus.switchToEth.value(_priceCost)(address(nToken));
       }
       if (msg.value > _priceCost) {
           repayEth(address(msg.sender), msg.value.sub(_priceCost));
       }
       emit NowTokenPrice(tokenAddress,priceInfo.ethAmount, priceInfo.erc20Amount);
       return (priceInfo.ethAmount,priceInfo.erc20Amount, checkBlock);
   }


輸入參數

描述

ethAmount 吃單ETH 數量

tokenAmount 吃單ERC20 數量

tokenAddress 報價ERC20 地址

endBlock 價格生效區塊號


require(checkUseNestPrice(address(msg.sender)));

獲取價格(最新)


mapping(uint256 => PriceInfo) storage priceInfoList = _tokenInfo[tokenAddress].priceInfoList;

輸入參數描述


uint256 checkBlock = _tokenInfo[tokenAddress].latestOffer;
while(checkBlock > 0 && (checkBlock >= block.number || priceInfoList[checkBlock].ethAmount == 0)) {
checkBlock = priceInfoList[checkBlock].frontBlock;
}

tokenAddress ERC20 Token 合約地址


require(checkBlock != 0);

輸出參數描述


PriceInfo memory priceInfo = priceInfoList[checkBlock];
       address nToken = _tokenMapping.checkTokenMapping(tokenAddress);
       if (nToken == address(0x0)) {
           _abonus.switchToEth.value(_priceCost)(address(_nestToken));
       } else {
           _abonus.switchToEth.value(_priceCost)(address(nToken));
       }
       if (msg.value > _priceCost) {
           repayEth(address(msg.sender), msg.value.sub(_priceCost));
       }

ethAmount ETH 數量


erc20Amount ERC20 Token 數量

// Check real-time price - user account only
   function checkPriceNow(address tokenAddress) public view returns (uint256 ethAmount, uint256 erc20Amount, uint256 blockNum) {
       require(address(msg.sender) == address(tx.origin), "It can't be a contract");
       mapping(uint256 => PriceInfo) storage priceInfoList = _tokenInfo[tokenAddress].priceInfoList;
       uint256 checkBlock = _tokenInfo[tokenAddress].latestOffer;
       while(checkBlock > 0 && (checkBlock >= block.number || priceInfoList[checkBlock].ethAmount == 0)) {
           checkBlock = priceInfoList[checkBlock].frontBlock;
       }
       if (checkBlock == 0) {
           return (0,0,0);
       }
       PriceInfo storage priceInfo = priceInfoList[checkBlock];
       return (priceInfo.ethAmount,priceInfo.erc20Amount, checkBlock);
   }

blockNum 生效價格區塊


檢查是否有權限使用NEST 價格。

function activation() public {
       _nestToken.safeTransferFrom(address(msg.sender), _destructionAddress, destructionAmount);
       _addressEffect[address(msg.sender)] = now.add(effectTime);
   }

獲取對應Token 的價格數據源。


DEMO

解釋一下while 循環的判斷,需要從最新的報價區塊開始往後倒推找到當前已經生效並且沒有被吃單的價格數據所在的區塊號(checkBlock)。

/**
    * @dev Get a single price
    * @param token Token address of the price
    */
   function getSinglePrice(address token) public payable {
       // In consideration of future upgrades, the possibility of upgrading the price contract is not ruled out, and the voting contract must be used to query the price contract address.
       Nest_3_OfferPrice _offerPrice = Nest_3_OfferPrice(address(_voteFactory.checkAddress("nest.v3.offerPrice")));
       // Request the latest price, return the eth quantity, token quantity, and effective price block number. Tentative fee.
       (uint256 ethAmount, uint256 tokenAmount, uint256 blockNum) = _offerPrice.updateAndCheckPriceNow.value(0.001 ether)(token);
       uint256 ethMultiple = ethAmount.div(1 ether);
       uint256 tokenForEth = tokenAmount.div(ethMultiple);
       // If the eth paid for the price is left, it needs to be processed.
       // ........
       
       emit price(ethAmount, tokenAmount, blockNum, ethMultiple, tokenForEth);
   }
   
   /**
    * @dev Get multiple prices
    * @param token The token address of the price
    * @param priceNum Get the number of prices, sorted from the latest price
    */
   function getBatchPrice(address token, uint256 priceNum) public payable {
       // In consideration of future upgrades, the possibility of upgrading the price contract is not ruled out, and the voting contract must be used to query the price contract address.
       Nest_3_OfferPrice _offerPrice = Nest_3_OfferPrice(address(_voteFactory.checkAddress("nest.v3.offerPrice")));
       /**
        * The returned array is an integer multiple of 3, 3 data is a price data.
        * Corresponding respectively, eth quantity, token quantity, effective price block number.
        */
       uint256[] memory priceData = _offerPrice.updateAndCheckPriceList.value(0.01 ether)(token, priceNum);
       // Data processing
       uint256 allTokenForEth = 0;
       uint256 priceDataNum = priceData.length.div(3);
       for (uint256 i = 0; i < priceData.length;) {
           uint256 ethMultiple = priceData[i].div(1 ether);
           uint256 tokenForEth = priceData[i.add(1)].div(ethMultiple);
           allTokenForEth = allTokenForEth.add(tokenForEth);
           i = i.add(3);
       }
       // Average price
       uint256 calculationPrice = allTokenForEth.div(priceDataNum);
       // If the eth paid for the price is left, it needs to be processed.
       // ........
       
       
       emit averagePrice(calculationPrice);
   }


CoFiX

GitHub:

https://github.com/Computable-Finance/CoFiX/blob/master/contracts/CoFiXController.sol#L282

function getLatestPrice(address token) internal returns (uint256 _ethAmount, uint256 _erc20Amount, uint256 _blockNum) {
       uint256 _balanceBefore = address(this).balance;
       address oracle = voteFactory.checkAddress("nest.v3.offerPrice");
       uint256[] memory _rawPriceList = INest_3_OfferPrice(oracle).updateAndCheckPriceList{value: msg.value}(token, 1);
       require(_rawPriceList.length == 3, "CoFiXCtrl: bad price len");
       // validate T
       uint256 _T = block.number.sub(_rawPriceList[2]).mul(timespan);
       require(_T < 900, "CoFiXCtrl: oralce price outdated");
       uint256 oracleFeeChange = msg.value.sub(_balanceBefore.sub(address(this).balance));
       if (oracleFeeChange > 0) TransferHelper.safeTransferETH(msg.sender, oracleFeeChange);
       return (_rawPriceList[0], _rawPriceList[1], _rawPriceList[2]);
       // return (K_EXPECTED_VALUE, _rawPriceList[0], _rawPriceList[1], _rawPriceList[2], KInfoMap[token][2]);
   }

這個判斷個人猜測是為了防止有些token 剛開始報價,還沒有有效價格生成,又因為調用價格是要付費的。所以加了限制,如果沒找到生效價格的區塊號,交易直接失敗。https://t.me/nestdevs鏈下獲取價格(最新價格)