DeFi の幅広いカテゴリには、次のような多くのスマート コントラクト アプリケーション シナリオが含まれます。ブロックチェーン投票、流動性マイニング、流動性マイニングチェーンリンクの価格フィードチェーンリンクの価格フィードオラクルはSolidityを使用して、イーサリアムメインネット上でシンプルなコールオプションDeFi取引プラットフォームを開発しています。もちろん、この例を少し変更してプット オプション取引プラットフォームを開発することもできます。このプラットフォームには強力な機能があり、すべての価値の転送はスマートコントラクトを通じて実行され、取引の両当事者は中間者を迂回して直接取引を行うことができます。したがって、このプロセスには第三者は関与せず、最も典型的なDeFiアプリケーションであるスマートコントラクトと分散型Chainlink価格フィードのみが関与します。分散型オプション取引プラットフォームの開発には、次の内容が含まれます。
Solidity で文字列を比較する
整数を固定の小数点以下の桁数に変換します
LINK などのトークン インターフェイスを作成して初期化する
ユーザー/スマートコントラクト間でのトークンの転送
トークン転送の承認
SafeMath
スマートコントラクトABIインターフェース
require() でトランザクション状態を実行する
Ethereum msg.Value とトークン価値トランザクションとの違い
int と uint 間の変換
支払い先住所
最後に、Chainlink データアグリゲーターインターフェイスを使用して DeFi 価格データを取得します
そしてGitHubそしてRemix関連するコードを確認してください。正式に始める前に、オプション契約とは何かを簡単に紹介しましょう。オプション契約では、特定の日付までに合意された価格で取引を実行するオプションが提供されます。具体的には、オプション契約の内容が株式やトークンなどの資産を購入するものである場合、コールオプションと呼ばれます。また、この記事のサンプル コードを少し変更してオプションを追加することもできます。プットオプションはコールオプションの逆で、その内容は資産を買うのではなく売ることです。オプションに関連する固有名詞を次に示します。
権利行使価格: 合意された資産の売買価格
オプション料金:契約購入時に売主に支払う手数料
有効期限:契約が終了する日
ストライク: 買い手がストライク価格で資産を売買する権利を行使する行為
呼び出しを開発する場合でも、PUT を開発する場合でも、インポート、コンストラクター、およびグローバル変数の基本要素が必要です。
pragma solidity ^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";
contract chainlinkOptions {
// オーバーフローセーフ演算子
using SafeMath for uint;
// 価格フィードインターフェース
AggregatorV3Interface internal ethFeed;
AggregatorV3Interface internal linkFeed;
//LINKトークンインターフェース
LinkTokenInterface internal LINK;
uint ethPrice;
uint linkPrice;
//文字列ハッシュ値を事前計算する
bytes32 ethHash = keccak256(abi.encodePacked("ETH"));
bytes32 linkHash = keccak256(abi.encodePacked("LINK"));
address payable contractAddr;
//オプションは構造体配列の形式で格納されます
struct option {
uint strike; //Price in USD (18 decimal places) option allows buyer to purchase tokens at
uint premium; //Fee in contract token that option writer charges
uint expiry; //Unix timestamp of expiration time
uint amount; //Amount of tokens the option contract is for
bool exercised; //Has option been exercised
bool canceled; //Has option been canceled
uint id; //Unique ID of option, also array index
uint latestCost; //Helper to show last updated cost to exercise
address payable writer; //Issuer of option
address payable buyer; //Buyer of option
}
option[] public ethOpts;
option[] public linkOpts;
//Kovan フィードの価格: https://docs.chain.link/docs/reference-contracts
constructor() public {
// ETH/USD の Kovan 価格フィード
ethFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
//LINK/USD コバンのフィード価格
linkFeed = AggregatorV3Interface(0x396c5E36DD0a0F5a5D33dae44368D4193f69a1F0);
//Kovan 上のリンク トークン アドレス
LINK = LinkTokenInterface(0xa36085F69e2889c224210F603D836748e7dC0088);
contractAddr = payable(address(this));
}
インポートする際、価格フィード機能を実現するためにChainlinkのデータアグリゲーターインターフェイスにアクセスし、LINKトークンインターフェイスにアクセスする必要があります(注:ここでは送金にLINKを使用する必要があるため、トークンコントラクトのERC20機能を使用する必要があります) 。最後にインポートしますOpenZeppelin の SafeMathコントラクトは、組み込みのオーバーフロー チェックを実行する標準ライブラリ操作ですが、Solidity の組み込み演算子にはオーバーフロー チェックが含まれていません。
チェーンリンクの価格フィード
チェーンリンクの価格フィード
//最新のリンク価格を返す
function getLinkPrice() public view returns (uint) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = linkFeed.latestRoundData();
//このラウンドがまだ終了していない場合、タイムスタンプは 0 です
require(timeStamp > 0, "Round not complete");
//価格がマイナスになることはないので、int を uint に変換できます
//価格は小数点以下 8 桁なので、18 桁にするためには 10 桁を追加する必要があります。
return uint(price);
}
最初のレベルのタイトル
コールオプション契約を書く
//ユーザーが保留中の通話オプションを書き込めるようにする
// 受け取ったトークンの種類、行使価格 (トークンは米ドル建て、小数点以下 18 桁)、オプション料金 (トークンの小数点と同じ)、有効期限 (UNIX)、契約数量のトークン
function writeOption(string memory token, uint strike, uint premium, uint expiry, uint tknAmt) public payable {
bytes32 tokenHash = keccak256(abi.encodePacked(token));
require(tokenHash == ethHash || tokenHash == linkHash, "Only ETH and LINK tokens are supported");
updatePrices();
if (tokenHash == ethHash) {
require(msg.value == tknAmt, "Incorrect amount of ETH supplied");
uint 最新コスト = 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), "Incorrect amount of LINK supplied");
uint latestCost = strike.mul(tknAmt).div(linkPrice.mul(10**10));
linkOpts.push(option(strike, premium, expiry, tknAmt, false, false, linkOpts.length, latestCost, msg.sender, address(0)));
}
}
画像の説明
LINK トークンの LINK オプション契約を作成し、Unix の有効期限を設定します。行使価格は 10 ドル、オプション料金は 0.1 LINK です。
最初のレベルのタイトル
コントラクト ABI インターフェイス
Etherscan でコントラクトを表示すると、「Read Contract」と「Write Contract」という 2 つのタブが表示されます。これら 2 つのタブを使用して、コントラクトを操作できます。例: LINK トークンのメイン ネットワーク契約。 Etherscan は、これらの関数が何であるか、およびコントラクトの ABI を介してそれらを呼び出す方法を知っています。 JSON 形式を使用して ABI を呼び出し、関数呼び出しパラメーターを指定します。メインネットワーク上で直接呼び出すこともできますが、Kovan 上の LINK コントラクトはこのモジュールをインポートする必要があります。でできますLinkToken画像の説明
画像の説明
最初のレベルのタイトル
コールオプションを買う
//コールオプションを購入するには、トークン、オプションID、支払いが必要です
function buyOption(string memory token, uint ID) public payable {
bytes32 tokenHash = keccak256(abi.encodePacked(token));
require(tokenHash == ethHash || tokenHash == linkHash, "Only ETH and LINK tokens are supported");
updatePrices();
if (tokenHash == ethHash) {
require(!ethOpts[ID].canceled && ethOpts[ID].expiry > now, "Option is canceled/expired and cannot be bought");
//買い手はオプションプレミアムを支払う
require(msg.value == ethOpts[ID].premium, "Incorrect amount of ETH sent for premium");
// 売り手はオプション料金を受け取ります
ethOpts[ID].writer.transfer(ethOpts[ID].premium);
ethOpts[ID].buyer = msg.sender;
} else {
require(!linkOpts[ID].canceled && linkOpts[ID].expiry > now, "Option is canceled/expired and cannot be bought");
//買い手から売り手にオプションプレミアムを譲渡
require(LINK.transferFrom(msg.sender, linkOpts[ID].writer, linkOpts[ID].premium), "Incorrect amount of LINK sent for premium");
linkOpts[ID].buyer = msg.sender;
}
}
最初のレベルのタイトル
運動オプション
//コールオプションを行使、トークン、オプションID、支払いが必要
function exercise(string memory token, uint ID) public payable {
// オプションの有効期限が切れておらず、行使されていない場合、オプション所有者は行使を許可されます
//オプションを行使するには、買い手は売り手に行使価格×数量を支払い、契約で合意されたトークンの数を取得する必要があります
bytes32 tokenHash = keccak256(abi.encodePacked(token));
require(tokenHash == ethHash || tokenHash == linkHash, "Only ETH and LINK tokens are supported");
if (tokenHash == ethHash) {
require(ethOpts[ID].buyer == msg.sender, "You do not own this option");
require(!ethOpts[ID].exercised, "Option has already been exercised");
require(ethOpts[ID].expiry > now, "Option is expired");
// 条件を満たしたら支払いを行う
updatePrices();
// 運動料金
uint exerciseVal = ethOpts[ID].strike*ethOpts[ID].amount;
//Chainlink に接続して価格をフィードし、イーサリアムに変換します
uint equivEth = ExerciseVal.div(ethPrice.mul(10**10)); //フィード価格の小数点以下 8 桁を 18 に変換します
//買い手はオプションを行使するために権利行使価格*数量に相当するイーサを支払います。
require(msg.value == equivEth, "Incorrect LINK amount sent to exercise");
//売主に行使手数料を支払う
ethOpts[ID].writer.transfer(equivEth);
//購入者に契約金額のイーサを支払う
msg.sender.transfer(ethOpts[ID].amount);
ethOpts[ID].exercised = true;
} else {
require(linkOpts[ID].buyer == msg.sender, "You do not own this option");
require(!linkOpts[ID].exercised, "Option has already been exercised");
require(linkOpts[ID].expiry > now, "Option is expired");
updatePrices();
uint exerciseVal = linkOpts[ID].strike*linkOpts[ID].amount;
uint equivLink = exerciseVal.div(linkPrice.mul(10**10));
//買い手はオプションを行使し、売り手に行使手数料を支払います
require(LINK.transferFrom(msg.sender, linkOpts[ID].writer, equivLink), "Incorrect LINK amount sent to exercise");
//LINKトークンの契約金額を販売者に支払う
require(LINK.transfer(msg.sender, linkOpts[ID].amount), "Error: buyer was not paid");
linkOpts[ID].exercised = true;
}
}
画像の説明
例: トランザクションが 1 つ以上の条件を満たさない場合に出力される Remix の結果。
条件が満たされた場合、売主には行使手数料が支払われ、買主には契約枚数分のトークンが支払われます。オプションを行使する場合、買い手は各トークンを行使価格で購入する必要があります。ただし、権利行使価格は米ドル建てであり、契約サイズはイーサまたはリンクで建てられます。したがって、行使手数料に相当するETHまたはLINKの金額を計算するには、Chainlink価格フィードにアクセスする必要があります。同等のETHまたはLINKに変換した後、送金を開始できます。送金するときは、上記のメソッドを使用する必要があります。つまり、イーサ通貨は msg.value/address.transfer 関数を呼び出し、LINK は transferFrom() 関数を呼び出します。
最初のレベルのタイトル
契約のキャンセル/資金の削除
// 売り手が契約をキャンセルしたり、取引が正常に終了しなかったオプションからお金を取り戻したりできるようにします。
function cancelOption(string memory token, uint ID) public payable {
bytes32 tokenHash = keccak256(abi.encodePacked(token));
require(tokenHash == ethHash || tokenHash == linkHash, "Only ETH and LINK tokens are supported");
if (tokenHash == ethHash) {
require(msg.sender == ethOpts[ID].writer, "You did not write this option");
// キャンセルまたは購入されていないこと
require(!ethOpts[ID].canceled && ethOpts[ID].buyer == address(0), "This option cannot be canceled");
ethOpts[ID].writer.transfer(ethOpts[ID].amount);
ethOpts[ID].canceled = true;
} else {
require(msg.sender == linkOpts[ID].writer, "You did not write this option");
require(!linkOpts[ID].canceled && linkOpts[ID].buyer == address(0), "This option cannot be canceled");
require(LINK.transferFrom(address(this), linkOpts[ID].writer, linkOpts[ID].amount), "Incorrect amount of LINK sent");
linkOpts[ID].canceled = true;
}
}
//売り手が期限切れ、未払い、キャンセルされていないオプションから資金を償還できるようにします。
function retrieveExpiredFunds(string memory token, uint ID) public payable {
bytes32 tokenHash = keccak256(abi.encodePacked(token));
require(tokenHash == ethHash || tokenHash == linkHash, "Only ETH and LINK tokens are supported");
if (tokenHash == ethHash) {
require(msg.sender == ethOpts[ID].writer, "You did not write this option");
//有効期限が切れており、行使されておらず、キャンセルされていない必要があります。
require(ethOpts[ID].expiry <= now && !ethOpts[ID].exercised && !ethOpts[ID].canceled, "This option is not eligible for withdraw");
ethOpts[ID].writer.transfer(ethOpts[ID].amount);
//複数の引き換えを避けるために、キャンセル フラグを true に変更します。
ethOpts[ID].canceled = true;
} else {
require(msg.sender == linkOpts[ID].writer, "You did not write this option");
require(linkOpts[ID].expiry <= now && !linkOpts[ID].exercised && !linkOpts[ID].canceled, "This option is not eligible for withdraw");
require(LINK.transferFrom(address(this), linkOpts[ID].writer, linkOpts[ID].amount), "Incorrect amount of LINK sent");
linkOpts[ID].canceled = true;
}
}
市場の変動に伴い、オプションが売却されていない場合、売り手はオプション契約をキャンセルし、資金を償還することがあります。同様に、オプションが行使されずに期限切れになった場合、売り手は必ず契約内の資金を償還したいと考えるでしょう。したがって、cancelOption() 関数とretrieveExpiredFunds() 関数を追加しました。
これら 2 つの関数の最も重要な点は、正常に呼び出すには引き換え条件が満たされている必要があることです。販売者が資金を引き換えるには特定の条件を満たす必要があり、引き換えることができるのは 1 回だけです。売主は一度売れた契約をキャンセルすることはできないので、買主の住所が初期値の0のままであることを確認する必要があります。また、オプションがキャンセルされていないことを確認してから返金する必要があります。オプションの期限が切れた後に資金が償還される場合、状況は若干異なります。この場合、オプションは売却されたものの行使されていない可能性があり、資金は依然として売り手に返還される必要があります。契約が期限切れで行使されていないことを確認したいと考えています。次に、条件が満たされた場合に払い戻しを行うために、オプションのキャンセルフラグも true に設定します。
この記事が、メインネット上で Chainlink のユースケースをすぐに開発するのに役立ち、Solidity の独自の機能を理解できることを願っています。 Chainlink の機能について詳しく知りたい場合は、こちらをご覧ください。Chainlink VRF(検証可能なランダム関数)、または「」を参照してください。Chainlink Fair 注文サービス、Chainlink がマイナーの最前線の問題をどのように解決するかを学びましょう。
参加してください開発者向けドキュメント参加してくださいDiscordお問い合わせ。ここをクリックお問い合わせ。
