DeFi这个大类下包含许多智能合约应用场景,如区块链投票、去中心化彩票、流动性挖矿以及去中心化交易平台。本文将教大家如何使用Chainlink喂价预言机在以太坊主网上用Solidity开发简单的看涨期权DeFi交易平台。当然,你也可以将这个实例稍作修改,开发一个看跌期权交易平台。这个平台拥有一个强大的功能,那就是所有价值转移都通过智能合约进行,交易双方可以绕过中间方直接展开交易。因此,这个过程不包含任何第三方,只包含智能合约和去中心化的Chainlink喂价,这就是最典型的DeFi应用。开发一个去中心化期权交易平台将涵盖以下内容:
在Solidity中对比字符串
将整数转换成固定位数的小数
创建并初始化一个通证接口,比如LINK
在用户/智能合约之间转移通证
批准通证转移
SafeMath
智能合约ABI接口
用require()执行交易状态
以太坊msg.Value及其与通证价值交易的区别
在int和uint之间进行转换
应付的地址
最后,用Chainlink数据聚合商的接口获取DeFi价格数据
各位可以去GitHub和Remix上查看相关代码。在我们正式开始前,先来简单介绍一下什么是期权合约。期权合约让你有权选择在某个期限前以约定的价格执行交易。具体而言,如果期权合约内容是买入股票或通证等资产,则被称为看涨期权。另外,本文的示例代码可以稍作修成看跌期权。看跌期权与看涨期权正好相反,其内容不是买入资产而是卖出资产。以下是期权相关的一些专有名词:
行权价格:约定的资产买进/卖出价格
期权费用:购买合约时支付给卖家的费用
到期日:合约终止的时间
行权:买家行使其权利以行权价格买卖资产的行为
无论是开发看涨期权还是看跌期权,都需要导入、构造函数和全局变量这些基本元素。
pragmasolidity^0.6.7;
import"https://github.com/smartcontractkit/chainlink/blob/develop/evm-contracts/src/v0.6/interfaces/LinkTokenInterface.sol";
import"https://github.com/smartcontractkit/chainlink/blob/master/evm-contracts/src/v0.6/interfaces/AggregatorV3Interface.sol";
import"https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/math/SafeMath.sol";
contractchainlinkOptions{
//溢出安全操作符
usingSafeMathforuint;
//喂价接口
AggregatorV3InterfaceinternalethFeed;
AggregatorV3InterfaceinternallinkFeed;
//LINK通证接口
LinkTokenInterfaceinternalLINK;
uintethPrice;
uintlinkPrice;
数字人民币接入支付宝:7月16日消息,网商银行(支付宝)成为继工、农、中、建、交、邮六大国有银行后第七家参与公测试点的商业银行。目前,在支付宝APP上只有部分用户已经加入了“数字人民币”入口。支付宝客服表示该功能仍处于小范围内测中,所以不是每个人都可以看到的。(凤凰网)[2021/7/16 0:56:20]
//预计算字符串哈希值
bytes32ethHash=keccak256(abi.encodePacked("ETH"));
bytes32linkHash=keccak256(abi.encodePacked("LINK"));
addresspayablecontractAddr;
//期权以结构数组形式储存
structoption{
uintstrike;//PriceinUSD(18decimalplaces)optionallowsbuyertopurchasetokensat
uintpremium;//Feeincontracttokenthatoptionwritercharges
uintexpiry;//Unixtimestampofexpirationtime
uintamount;//Amountoftokenstheoptioncontractisfor
boolexercised;//Hasoptionbeenexercised
boolcanceled;//Hasoptionbeencanceled
uintid;//UniqueIDofoption,alsoarrayindex
uintlatestCost;//Helpertoshowlastupdatedcosttoexercise
addresspayablewriter;//Issuerofoption
addresspayablebuyer;//Buyerofoption
}
optionpublicethOpts;
optionpubliclinkOpts;
//Kovan喂价:https://docs.chain.link/docs/reference-contracts
constructor()public{
//以太币/美元的Kovan喂价
ethFeed=AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
//LINK/美元的Kovan喂价
linkFeed=AggregatorV3Interface(0x396c5E36DD0a0F5a5D33dae44368D4193f69a1F0);
//Kovan上的LINK通证地址
LINK=LinkTokenInterface(0xa36085F69e2889c224210F603D836748e7dC0088);
contractAddr=payable(address(this));
}
在导入时,我们需要接入Chainlink的数据聚合商接口实现喂价功能,并接入LINK通证接口。最后,我们导入OpenZeppelin的SafeMath?合约,这是执行内置溢出检查运算的标准库,而Solidity的内置操作符中不包含溢出检查。
接下来,我们重新定义运算类型和uint,使用导入的SafeMath,定义我们的喂价、LINK接口、价格变量,计算以太币和LINK字符串的keccak256哈希值,以及地址变量来储存我们的合约地址。要注意一点,地址被定义为“应付”,因为我们的合约需要用这个地址收款。接着,在构建完成后将接口初始化成Kovan合约地址,这样就可以调用合约函数,并用“address(this)”设置合约地址。我们再将地址转换成“应付”,因为否则address()会返回无法支付的地址类型。至于期权本身的数据类型,可以用一个结构数组,也可以用结构链表。使用标准数组的好处是我们可以直接访问期权,这是链表无法做到的,但同时,删除标准数组中的值计算成本非常高。因此,我们不对期权做删除操作,而只将它们标记为“到期”或“取消”,这样就能牺牲储存空间以换取计算速度和简便性。最后,期权的买卖和行权可以通过O(1)operations降低gas费用。
互联网自动化平台IFTTT已支持接入Coinbase个人交易数据:5月6日消息,互联网自动化平台IFTTT宣布支持加密货币交易所Coinbase,用户可以在IFTTT授权登录并接入Coinbase账户的买入或卖出交易行为,然后触发其他功能,比如在Google表格上进行交易记录。成立于2011年的IFTTT是一个可以连接打通多个互联网应用的平台,其注册用户数超过1800万。[2021/5/6 21:30:29]
Chainlink喂价
//返回最新的LINK价格
functiongetLinkPrice()publicviewreturns(uint){
(
uint80roundID,
intprice,
uintstartedAt,
uinttimeStamp,
uint80answeredInRound
)=linkFeed.latestRoundData();
//如果这轮还没有结束,则timestamp是0
require(timeStamp>0,"Roundnotcomplete");
//价格永远不会是负数,因此可以将int转换成uint
//价格小数点后有8位,之后需要增加10位变成18位。
returnuint(price);
}
我们首先实现的是两个getter函数,获取以太币和LINK喂价。以太币的函数与上方LINK函数一样,唯一不同的是接入以太币喂价。这会调用latestRoundData()函数查看我们初始化的喂价,并且会自动返回最新的去中心化市场聚合价格数据。因为这是一个view函数,所以甚至连gas费也用不着!我们对默认喂价getter函数做了一个调整,将价格从int转换成uint,以匹配之后使用uint的函数。这里要注意一点,这样转换是ok的,因为价格永远不可能是负数,所以不会用到int的符号位。在类型之间转换的时候需要考虑到这些细节。
写一个看涨期权合约
//允许用户写保持看涨期权
//接收的通证类型,行权价格,期权费用,到期日,合约中的通证数量
functionwriteOption(stringmemorytoken,uintstrike,uintpremium,uintexpiry,uinttknAmt)publicpayable{
bytes32tokenHash=keccak256(abi.encodePacked(token));
require(tokenHash==ethHash||tokenHash==linkHash,"OnlyETHandLINKtokensaresupported");
updatePrices();
if(tokenHash==ethHash){
require(msg.value==tknAmt,"IncorrectamountofETHsupplied");
uintlatestCost=strike.mul(tknAmt).div(ethPrice.mul(10**10));//以以太币计价的行权费用,小数点位数调整
ethOpts.push(option(strike,premium,expiry,tknAmt,false,false,ethOpts.length,latestCost,msg.sender,address(0)));
}else{
require(LINK.transferFrom(msg.sender,contractAddr,tknAmt),"IncorrectamountofLINKsupplied");
supt宣布将支持ZSC链上资产,跨链接入ZSC链:4月29日消息,supt宣布作为跨链资产接入ZT智能链ZSC链。届时,supt将参与ZSC链上交互,深度参与ZSC生态,支持DEX、借贷、流动性挖矿等多种链上应用场景。
超级出行链(SUPT)是区块链和AI智能驾驶大数据相结合的落地项目,利用区块链技术致力于提高全球道路交通安全,以AI智能驾驶创始区块矿机为数据价值载体,打造基于可信云计算出行数据链。
supt将支持ZSC共同打造多元化区块链生态,促进区块链技术项目的生态发展,丰富区块链应用场景。[2021/4/29 21:10:31]
uintlatestCost=strike.mul(tknAmt).div(linkPrice.mul(10**10));
linkOpts.push(option(strike,premium,expiry,tknAmt,false,false,linkOpts.length,latestCost,msg.sender,address(0)));
}
}
初始设置完成并接入喂价后,我们接下来就可以调用函数了,先来写一个期权合约。卖家调用writeOption函数,并填入期权具体的参数,小数点后保留18位。这里必须要明确小数点位数,以确保合约中使用的所有参数都格式统一。比如,整数777没有小数点,但是如果我们规定的逻辑是保留两位小数,则表示成7.77。我们这里的规则是小数点后保留18位,因为以太币和LINK都是18位小数。如果小数点后不到18位,则可以添加0变成18位。接下来,我们就可以第一次使用之前计算出的以太币和LINK字符串哈希值。为了明确卖家的期权合约针对的是什么通证,我们需要比较字符串。然而Solidity不支持在字符串之间进行==操作,因为其长度是动态的。我们不需要写一个函数一个个字节地比较字符串,而只需用keccak256哈希函数计算每个字符串的32位哈希值,并直接对比。只要哈希值一样,字符串就一样。现在我们知道卖家用的是哪种通证,就可以有的放矢了。如果是以太币,我们就可以用msg.value确认转账到期权合约的以太币数量是否正确。我们可以用require()函数严格执行。如果require的第一个字段为false,则交易会被拒绝,无法进行下去。这样一来,我们可以确保所有期权合约的转账都完全符合之前约定的金额。检查通过后,我们就可以创建期权合约,提供所有必须的字段生成结构。基于当前以太币价格,使用SafeMath函数而非内置操作符计算当前行使期权的费用。使用Chainlink的updatePrices()helper函数获取当前价格,这个函数会更新全局以太币和LINK价格。注意ethPrice要乘以10的10次方。这样做是因为Chainlink喂价返回的是8位小数的美元价格,但正如上文所述,我们现在的标准是18位小数。所以添加10个零可以将其调整成18位小数,符合以太币和LINK通证的格式。最后,我们将期权的结构压入ethOpts的数组中,这样期权合约就写完了,而且里面有足够的资金。
针对一枚LINK通证写一个LINK期权合约,设定Unix到期时间,行权价格为10美元,期权费用为0.1个LINK。
如果是LINK期权合约,那么就需要做一些修改了。Msg.value只提供交易中以太币的金额。因此如果要确保LINK的金额充足,我们需要直接接入LINK通证合约。我们之前已经导入并初始化了LINK通证接口,因此可以访问所有LINK通证函数,其中一个是transferFrom(),这个函数可以将LINK从一个地址转移到另一个地址。当然,我们不能让任何合约都可以随便转移你的LINK资产,所以必须首先调用LINK的approve()函数,并具体说明允许转移的LINK数量以及转移到的合约地址。
合约ABI接口
当你在Etherscan上查看合约时,会出现两个tab,即:ReadContract和WriteContract。你可以用这两个tab与合约进行交互。比如:LINK通证主网合约。Etherscan知道这些函数是什么以及如何通过合约的ABI调用函数。使用JSON格式调用ABI,规定函数调用参数。在主网上可以直接调用,但是在Kovan上的LINK合约需要导入这个模块。各位可以在LinkToken的Github上查看ABI。所幸,在生产系统中,这些都可以用web3js的界面来处理,用户可以用一个简单的MetaMask请求来进行批准。但在我们这个开发实例中,暂时需要手动操作。
波卡周报:Rococo重启成功,目前接入9个平行链:根据PolkaWorld最新发布的波卡周报,本周重要事件包括:
1. Parity计划开发并提议一个名为Statemint的通用资产平行链,作为首批公共利益平行链之一;
2. 关于SubstraTEE Milestone 3的国库申请已通过;
3. 一项开发EVM NFT桥的国库议案已经通过;
4. Figment发起一项国库议案,申请DOTs作为他们的Figment Learn和DataHub平台发展的激励,以发展Polkadot开发者。目前波卡理事会正在审查这个议案;
5. 取消10号提案(一个force_transfer的提案)的议案已获得波卡理事会的通过。该提案的地址有误,已由提案11取代;
6. 目前提名成功最小的DOT数增加到了175个,Staking率增加到了64%;
7. Rococo已经重启,目前已经连接9个平行链:Tick,Trick,Track,Phala,Acala,Bifrost,Crust,Plasm和Hydrate。网络仍然处于正在稳定和优化中。[2021/3/14 18:43:42]
用导入的ABI通过MEW与Kovan上的LINK合约交互。
批准Kovan上的LINK合约转入/转出100个LINK通证。
购买看涨期权
//购买看涨期权,需要通证,期权ID和付款
functionbuyOption(stringmemorytoken,uintID)publicpayable{
bytes32tokenHash=keccak256(abi.encodePacked(token));
require(tokenHash==ethHash||tokenHash==linkHash,"OnlyETHandLINKtokensaresupported");
updatePrices();
if(tokenHash==ethHash){
require(!ethOpts.canceled&ðOpts.expiry>now,"Optioniscanceled/expiredandcannotbebought");
//买家支付期权费
require(msg.value==ethOpts.premium,"IncorrectamountofETHsentforpremium");
//卖家收到期权费
ethOpts.writer.transfer(ethOpts.premium);
ethOpts.buyer=msg.sender;
}else{
require(!linkOpts.canceled&&linkOpts.expiry>now,"Optioniscanceled/expiredandcannotbebought");
//将期权费从买家转给卖家
require(LINK.transferFrom(msg.sender,linkOpts.writer,linkOpts.premium),"IncorrectamountofLINKsentforpremium");
linkOpts.buyer=msg.sender;
京东商城成为数字人民币红包试点中首个接入数字人民币的线上场景:据京东数科官微消息,苏州2000万数字人民币消费红包启动预约,中签人员可通过京东商城进行线上消费。京东数科表示,支持市民在京东商城购买自营爆品时使用数字人民币支付,京东商城也成为试点中首个接入数字人民币的线上场景。此外,京东与苏州市相城区联合打造了数字人民币特色支付场景,收货地址在相城区的市民可选择京东商城自营商品货到付款场景并使用数字人民币支付,足不出户即可体验数字人民币的便捷支付服务。
京东数科相关负责人表示,公司依托科技+产业+生态优势,在支付领域长期深耕,并持续探索产业数字化发展。此次参与苏州数字人民币试点项目,京东数科提供“技术+服务”,连接了运营机构与广大场景,将持续配合人民银行数字货币研究所、苏州市政府加快建设数字人民币钱包生态,助推数字经济与实体经济融合发展。[2020/12/5 14:05:46]
}
}
现在期权合约创建完成且资金充足。接下来就等人来买了!买家只需表明购买以太币或LINK期权的意愿以及期权ID即可。由于期权数组被定义成公开的,因此可以直接查看,无需支付gas费,买家可以查看所有期权合约及其ID字段。选择完期权合约后,我们再次调用require()函数验证期权费用的支付金额是否正确。这次,我们不仅需要确认msg.value,还需要将期权费用转给卖家。Solidity中的所有以太币地址都有一个address.transfer()函数,我们调用这个函数将期权费用从合约转账给卖家。然后设置期权合约的买家地址字段,就完成购买了!如果是LINK的话,操作就稍微简单一些。可以用transferFrom函数直接将买家的期权费转账给卖家。如果是以太币的话,期权费则需要先经过合约再到卖家地址。
行使期权
//行使看涨期权,需要通证,期权ID和付款
functionexercise(stringmemorytoken,uintID)publicpayable{
//如果期权没到期且还没有被行使,则允许期权所有者行使
//要行使期权,买家需向卖家支付行权价格*数量的金额,并获得合约中约定数量的通证
bytes32tokenHash=keccak256(abi.encodePacked(token));
require(tokenHash==ethHash||tokenHash==linkHash,"OnlyETHandLINKtokensaresupported");
if(tokenHash==ethHash){
require(ethOpts.buyer==msg.sender,"Youdonotownthisoption");
require(!ethOpts.exercised,"Optionhasalreadybeenexercised");
require(ethOpts.expiry>now,"Optionisexpired");
//符合条件,进行付款
updatePrices();
//行权费用
uintexerciseVal=ethOpts.strike*ethOpts.amount;
//接入Chainlink喂价换算成以太币
uintequivEth=exerciseVal.div(ethPrice.mul(10**10));//将喂价的8位小数转换成18位
//买家支付与行权价格*数量等值的以太币,行使期权。
require(msg.value==equivEth,"IncorrectLINKamountsenttoexercise");
//向卖家支付行权费
ethOpts.writer.transfer(equivEth);
//向买家支付合约数量的以太币
msg.sender.transfer(ethOpts.amount);
ethOpts.exercised=true;
}else{
require(linkOpts.buyer==msg.sender,"Youdonotownthisoption");
require(!linkOpts.exercised,"Optionhasalreadybeenexercised");
require(linkOpts.expiry>now,"Optionisexpired");
updatePrices();
uintexerciseVal=linkOpts.strike*linkOpts.amount;
uintequivLink=exerciseVal.div(linkPrice.mul(10**10));
//买家行权,向卖家支付行权费
require(LINK.transferFrom(msg.sender,linkOpts.writer,equivLink),"IncorrectLINKamountsenttoexercise");
//向卖家支付合约数量的LINK通证
require(LINK.transfer(msg.sender,linkOpts.amount),"Error:buyerwasnotpaid");
linkOpts.exercised=true;
}
}
对于期权所有者来说,以太币或LINK的价格如果超过行权价格,就能获利。这样一来,他们便愿意行使期权,以行权价购买通证。这次我们必须先确认几个条件,即:合约由消息发送者所有;合约还未行权;以及现在期权还没到期。如果以上任何一个条件不满足,则撤回交易。
示例:交易未满足一个或以上条件时Remix输出的结果。
如果条件都满足,则向卖家支付行权费,并向买家支付合约数量的通证。行权时,买家需以行权价购买每一个通证。然而,行权价是以美元计价,而合约数量是以以太币或LINK计价。因此我们需要接入Chainlink喂价计算与行权费等值的以太币或LINK数量。换算成等值的以太币或LINK后,我们就可以开始转账了。转账时需使用之前提过的方法,即以太币会调用msg.value/address.transfer函数,LINK则调用transferFrom()函数。
以上就是成功行使期权的完整交易过程。LINK价格是11.56美元,合约行权价格是10美元,数量1个LINK。也就是说,买家只需要花10美元而不是11.56美元购便可购买一个LINK。10/11.56=0.86,即买家只需要花0.86个LINK就可以获得1个LINK。算上0.1LINK的期权费用,总共获利0.04LINK。
取消合约/删除资金
//允许卖家取消合约或从没有成功达成交易的期权中退回资金。
functioncancelOption(stringmemorytoken,uintID)publicpayable{
bytes32tokenHash=keccak256(abi.encodePacked(token));
require(tokenHash==ethHash||tokenHash==linkHash,"OnlyETHandLINKtokensaresupported");
if(tokenHash==ethHash){
require(msg.sender==ethOpts.writer,"Youdidnotwritethisoption");
//必须还没有被取消或购买
require(!ethOpts.canceled&ðOpts.buyer==address(0),"Thisoptioncannotbecanceled");
ethOpts.writer.transfer(ethOpts.amount);
ethOpts.canceled=true;
}else{
require(msg.sender==linkOpts.writer,"Youdidnotwritethisoption");
require(!linkOpts.canceled&&linkOpts.buyer==address(0),"Thisoptioncannotbecanceled");
require(LINK.transferFrom(address(this),linkOpts.writer,linkOpts.amount),"IncorrectamountofLINKsent");
linkOpts.canceled=true;
}
}
//允许卖家从到期、未行使以及未取消的期权中赎回资金。
functionretrieveExpiredFunds(stringmemorytoken,uintID)publicpayable{
bytes32tokenHash=keccak256(abi.encodePacked(token));
require(tokenHash==ethHash||tokenHash==linkHash,"OnlyETHandLINKtokensaresupported");
if(tokenHash==ethHash){
require(msg.sender==ethOpts.writer,"Youdidnotwritethisoption");
//必须是到期、未行使且未取消的状态。
require(ethOpts.expiry<=now&&!ethOpts.exercised&&!ethOpts.canceled,"Thisoptionisnoteligibleforwithdraw");
ethOpts.writer.transfer(ethOpts.amount);
//将取消标志修改为true,避免多次赎回
ethOpts.canceled=true;
}else{
require(msg.sender==linkOpts.writer,"Youdidnotwritethisoption");
require(linkOpts.expiry<=now&&!linkOpts.exercised&&!linkOpts.canceled,"Thisoptionisnoteligibleforwithdraw");
require(LINK.transferFrom(address(this),linkOpts.writer,linkOpts.amount),"IncorrectamountofLINKsent");
linkOpts.canceled=true;
}
}
随着市场波动,如果期权还没卖出去,卖家可能会取消期权合约并赎回资金。同样地,期权如果一直未行使就到期了,卖家肯定会想要赎回合约中的资金。因此,我们添加了cancelOption()和retrieveExpiredFunds()函数
这两个函数最关键的一点是必须满足赎回条件才能调用成功。卖家要赎回资金必须满足特定的条件,而且只能赎回一次。卖家不能取消已经被卖出的合约,因此我们要确认买家地址仍然是初始值0。另外,我们还要确认期权还未被取消,然后再退款。如果是期权到期后再赎回资金,那情况就会稍有不同。这种情况下,期权可能已经卖出去但没有行使,资金仍应被退还给卖家。我们要确认合约已经到期并且还未被行使。然后也要将期权的取消标志设置为true,如果条件满足则进行退款。
希望本文能帮助各位立刻在主网上开发Chainlink用例,并让各位了解了Solidity独特的功能。如果你想了解更多的Chainlink功能,请查看ChainlinkVRF,或查看Chainlink公允排序服务,了解Chainlink如何解决矿工抢跑问题。
如果你是一名开发者,并希望快速将智能合约连接至链下数据和系统,请查看?我们的开发者文档并加入我们在Discord上的技术讨论群。如果你希望透过电话具体讨论集成细节,请点击此处联系我们。
BTC行情分析BTC给全世界的不确定性配置了一个确定性,所以当世界越来越不确定的时候,确定的是BTC成了避险的共识,这是BTC的第一层共识.
1900/1/1 0:00:00BTC行情分析BTC从14000的放量突破之后,开始进行4H周期的缩量调整,在美股持续下跌的避风险行情中,BTC和黄金表现出了异常的抗风险能力。目前来看,4H周期的回调中枢下轨在13500左右.
1900/1/1 0:00:004日晚间原火币CTO一条朋友圈引发行业关注,“一个香港职业经理人连坑了两家世界头部交易所”,意指原OK、火币CFO李书沸。 随后李书沸回应,称没有为OK买壳,火币买壳则是合规渠道.
1900/1/1 0:00:00说出来你们可能不信,生活把我打倒在地,是比特币把我扶了起来。2010年,1个比特币的价格是0.0025美元,今天价值18250美元,10年730万倍.
1900/1/1 0:00:00下面是近期大家留言的一些解答,分析只是我个人的理解,不作为直接投资建议!大家有疑问可以留言,或者私发我微信上,因为精力有限,就不一对一回复,我集中整理后,下次在文章里统一分析解答.
1900/1/1 0:00:00每一个伟大的事物,都是从微小状态开始的,但这丝毫不会影响它在接下来的时间里创造奇迹。大多数普通人对于新生事物的认知过程,一般都会经历看不起,看不懂,追不上三个阶段.
1900/1/1 0:00:00