DeFi 開発者: NEST オラクル価格データを呼び出す方法

NEST爱好者
本文约7668字,阅读全文需要约31分钟
NEST のエコロジー開発者の経験を共有し、NEST オラクル価格データを簡単に呼び出す方法を教えます。

導入

導入

最初のレベルのタイトル

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

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

NEST Protocol:https://nestprotocol.org/

オンチェーン価格を取得してみる

仕組みを理解する

NEST オラクル マシンはブロック単位で価格を生成します。ブロック内に価格がない場合は、最新のブロック価格が使用されます。

ブロック価格は相場によって生成され、ブロック内に複数の相場がある場合は加重平均が使用されます。

各相場には 25 ブロックの検証時間があり (注文の取得)、検証時間内に注文が取得されない場合は、市場が相場を承認したことを意味し、価格は相場ブロックの 25 ブロック後に有効になります。

NEST オラクル価格契約 sol ファイル

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;
       }
   }

この方法は、価格契約に追加される価格データのデータ ソースが正しいことを確認するために呼び出すことができる「見積契約」のみに限定されます。

入力パラメータ                 説明する

ethAmount 見積 ETH 金額

tokenAmount 見積 ERC20 トークン数量

endBlock 価格の実効ブロック数

tokenAddress ERC20 見積書のトークンコントラクトアドレス

OfferOwner オファーまたはウォレットのアドレス


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

これら 3 行のコードは、同じブロック内で加重平均を実装します。


価格を変更する

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 数量

tokenAddress 引用 ERC20 アドレス

endBlock 価格の実効ブロック数


価格を取得(最新)

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);
   }


入力パラメータ 説明

tokenAddress ERC20 トークンコントラクトアドレス

出力パラメータ 説明

ethAmount ETH金額

erc20Amount ERC20 トークンの数量

blockNum 実効価格ブロック


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

NEST 価格の使用許可を確認します。


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;
}

while ループの判定を説明するには、最新の相場ブロックから逆算して、現在有効でまだ取得されていない価格データが存在するブロック番号 (checkBlock) を見つける必要があります。


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));
       }

コードのこの部分は、呼び出し元が支払ったオラクル料金を対応する収益プールに分配します。超過料金は発信者に返金されます。


オフチェーンで価格を取得 (最新価格)

// 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);
   }

原理は前の方法と同じです。違いは、契約通話が禁止されていることと、料金の支払いが必要ないことです。オフチェーン アプリケーションの価格を確認するために使用する必要があります。


通話権限を有効にする

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

NEST オラクル マシンを使用するには、一定量の NEST をプレッジし、1 日待つ必要があります。この操作は「契約価格の盗用」を防ぐためのものであるはずです。そのような制限がない場合は、代理契約を作成して料金を取得することができ、一度支払うだけで済み、他の発信者もその料金を一緒に使用できます。


DEMO

公式文書

/**
    * @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]);
   }

NEST 開発者向けコミュニケーション:https://t.me/nestdevs(収益徴収、オラクルプライスコール、フロントエンドアクセスおよびその他の開発エクスチェンジ)