DeFi 개발자: NEST Oracle 가격 데이터를 호출하는 방법

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 Offeror 지갑 주소


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

또한 "Quotation Contract"만이 호출 권한을 갖도록 제한됩니다. 테이커 작업이 트리거된 후에야 해당 유효 블록의 가격이 수정되고 "가격 추가" 시점의 호가 수량은 "테이커"의 규모에 따라 차감됩니다.


입력 매개변수            설명하다

ethAmount 테이커의 ETH 금액

tokenAmount Taker 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를 약정하고 하루를 기다려야 합니다. 이 작업은 "계약 도용 가격"을 방지하기 위한 것입니다. 그러한 제한이 없다면 대금을 얻기 위해 대리 계약을 작성할 수 있으며 한 번만 지불하면 다른 발신자가 가격을 함께 사용할 수 있습니다.


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(수익 수집, 오라클 가격 콜, 프런트 엔드 액세스 및 기타 개발 교환)