DeFi 開発者: 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(収益徴収、オラクルプライスコール、フロントエンドアクセスおよびその他の開発エクスチェンジ)


