币非凡 - 区块链|交易所导航 www.bifeifan.com
当前位置:网站首页 > 新闻 > 正文

DeFi开发者:如何调用NEST预言机价格数据

admin 2020-11-12 1138 浏览


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

endBlock 价格生效区块号

tokenAddress 报价的 ERC20 Token 合约地址

offerOwner 报价者钱包地址

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

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 Token 合约地址

输出参数 描述

ethAmount ETH 数量

erc20Amount ERC20 Token 数量

blockNum 生效价格区块

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

检查是否有权限使用 NEST 价格。

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

获取对应 Token 的价格数据源。

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

解释一下 while 循环的判断,需要从最新的报价区块开始往后倒推找到当前已经生效并且没有被吃单的价格数据所在的区块号(checkBlock)。

require(checkBlock != 0);

这个判断个人猜测是为了防止有些 token 刚开始报价,还没有有效价格生成,又因为调用价格是要付费的。所以加了限制,如果没找到生效价格的区块号,交易直接失败。

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(收益领取、预言机价格调用、前端接入等开发交流)

相关推荐

Grayscale以太坊信托将按照9比1比例拆股

...

Visa宣布将USDC接入其6000万全球商家支付网络,稳定币格局或迎巨变

...

DEX聚合器1inch完成1200万美元A轮融资,由Pantera Capital牵头

...

加密市场“机构化”大步前行的另一面:增强加密货币实用性

...

科普 | 一文读懂Filecoin Gas费

...

观点丨为什么不存在 &quot;Ethereum杀手&quot;?
观点丨为什么不存在 &quot;Ethereum杀手&quot;?

作者 | EricOlszewskiEthereum 社区一直坚持在前沿和冒险的道路上发展。事实上,我们大多数人都同意这样的观点:如果有更好的平台出现,我们会...

1小时前 admin

观察 | 一文回顾YFI的“合并”之路

...

【分析师看后市】盲目追随大佬脚步,可能成为一介莽夫
【分析师看后市】盲目追随大佬脚步,可能成为一介莽夫

非常感谢大家对币世界研究院原创栏目【分析师看后市】的喜爱,现已推出同名深度系列原创栏目。如果你有喜欢的分析师,请在快讯、深度文章、B圈进行留言,点名你喜欢的分析...

1小时前 admin

盲目追随大佬脚步,可能成为一介莽夫
盲目追随大佬脚步,可能成为一介莽夫

狂人说市场的走势总是令人出乎意料,前一秒还在嘲笑别人的多头,后一秒便被疾风暴雨带走了,昨晚这将近两千刀的大面,带走了39亿的合约投资者,这还只是爆仓的数据,那些...

1小时前 admin

2万是一个重要心理关口
2万是一个重要心理关口

新高后的比特币没有走出大家期待的行情,而是在2万整数关口之前一再徘徊,不仅如此,还多次出现短时5%或10%跌幅的暴跌行情,随后又快速拉升。昨晚的那根针,一小时全...

1小时前 admin

全球最大资管公司贝莱德:比特币或演变成全球性市场资产
全球最大资管公司贝莱德:比特币或演变成全球性市场资产

全球最大的资产管理公司贝莱德(BlackRock黑石)负责人近日罕见正面发声比特币,认为比特币可能会演变成全球性市场资产。贝莱德旗下知名基金包括贝莱德环球资产配...

1小时前 admin

比特币和ETH的相关性上升,但掩盖了它们的区别

 尽管加密货币市场的情绪仍然是看涨,但比特币的价格还是下跌了。尽管以太坊和比特币的价值主张存在差异,但ETH与比特币的相关性仍在上升。CoinDesk20的数据显示,在昨晚比特币的势头停滞之前,比特...

DeFi的巨无霸要来了吗
DeFi的巨无霸要来了吗

/01/基本面分析▍摘录整理,侵权删:两天时间,SUSHI飙升50%。到底发生了什么呢?12月1日,Yearn宣布与SushiSwap“合并”,Sushisw...

1小时前 admin

Deribit期权市场播报:1202—大宗交易
  • Deribit期权市场播报:1202—大宗交易
  • Deribit期权市场播报:1202—大宗交易
  • Deribit期权市场播报:1202—大宗交易
  • Deribit期权市场播报:1202—大宗交易
适配器:波卡和ETH2.0两大公链IP造就的机会真空地带
适配器:波卡和ETH2.0两大公链IP造就的机会真空地带

今年下半年波卡和ETH2.0的巨大进展掀起了新一轮的公链热潮,而在DeFi退潮的大背景下,这两大公链IP成为了保持行业热度的重要支撑点。公链依然是去中心化理想的...

1小时前 admin