introduce
introduce
first level title
white paper:https://nestprotocol.org/doc/zhnestwhitepaper.pdf
GitHub:https://github.com/NEST-Protocol
NEST Protocol:https://nestprotocol.org/
Try to get the on-chain price
understand the mechanism
The NEST oracle machine generates prices in units of blocks. If there is no price in the block, the most recent block price will be used.
The block price is generated by means of quotes, and if there are multiple quotes in a block, the weighted average is used.
Each quotation has a verification time of 25 blocks (take order). If no order is taken within the verification time, it means that the market approves the quotation, and the price will take effect 25 blocks after the quotation block.
NEST oracle price contract sol file
GitHub:
https://github.com/NEST-Protocol/NEST-oracle-V3/blob/master/NestOffer/Nest_3_OfferPrice.sol
code analysis
text
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;
}
}
This method is limited to only the quote contract that can be called to ensure that the data source of the price data added to the price contract is correct.
Input parameters describe
ethAmount quote ETH amount
tokenAmount Quotation ERC20 Token quantity
endBlock price effective block number
tokenAddress ERC20 Token contract address of quotation
offerOwner Offeror wallet address
PriceInfo storage priceInfo = tokenInfo.priceInfoList[endBlock];
priceInfo.ethAmount = priceInfo.ethAmount.add(ethAmount);
priceInfo.erc20Amount = priceInfo.erc20Amount.add(tokenAmount);
These three lines of code implement weighted averaging within the same block.
Modify the price
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);
}
It is also restricted that only the Quotation Contract has permission to call. Only after the taker operation is triggered, the price in the corresponding effective block will be modified, and the quotation quantity at the time of adding price will be subtracted according to the scale of taker.
Input parameters describe
ethAmount Taker amount of ETH
tokenAmount Taker ERC20 Quantity
tokenAddress Quotation ERC20 address
endBlock price effective block number
Get Price (Latest)
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);
}
Input parameter Description
tokenAddress ERC20 Token contract address
Output parameter Description
ethAmount ETH amount
erc20Amount ERC20 Token quantity
blockNum effective price block
require(checkUseNestPrice(address(msg.sender)));
Check for permission to use NEST prices.
mapping(uint256 => PriceInfo) storage priceInfoList = _tokenInfo[tokenAddress].priceInfoList;
Get the price data source of the corresponding Token.
uint256 checkBlock = _tokenInfo[tokenAddress].latestOffer;
while(checkBlock > 0 && (checkBlock >= block.number || priceInfoList[checkBlock].ethAmount == 0)) {
checkBlock = priceInfoList[checkBlock].frontBlock;
}
To explain the judgment of the while loop, you need to work backwards from the latest quotation block to find the block number (checkBlock) where the price data that is currently in effect and has not been taken.
require(checkBlock != 0);
This judgment is a personal guess to prevent some tokens from quoting at the beginning, and no effective price has been generated yet, and because calling the price requires payment. Therefore, restrictions are added. If the block number of the effective price is not found, the transaction will directly fail.
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));
}
This part of the code distributes the oracle fee paid by the caller to the corresponding revenue pool. Excess charges are refunded to the caller.
Get price off-chain (latest price)
// 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 cant 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);
}
The principle is the same as the previous method. The difference is that contract calls are prohibited and no payment is required. It should be used to check prices for off-chain applications.
Activate calling privileges
function activation() public {
_nestToken.safeTransferFrom(address(msg.sender), _destructionAddress, destructionAmount);
_addressEffect[address(msg.sender)] = now.add(effectTime);
}
official document
DEMO
official document
/**
* @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 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 Developer Communication:https://t.me/nestdevs(Revenue collection, oracle price call, front-end access and other development exchanges)