Kết nối với máy tiên tri phi tập trung Chainlink để cung cấp giá và phát triển ví dụ về nền tảng giao dịch tùy chọn cuộc gọi DeFi
Danh mục rộng của DeFi bao gồm nhiều kịch bản ứng dụng hợp đồng thông minh, chẳng hạn nhưbỏ phiếu chuỗi khối、Khai thác thanh khoản、Khai thác thanh khoảnNguồn cấp dữ liệu giá ChainlinkNguồn cấp dữ liệu giá ChainlinkOracle sử dụng Solidity để phát triển một nền tảng giao dịch DeFi tùy chọn cuộc gọi đơn giản trên mạng chính Ethereum. Tất nhiên, bạn cũng có thể sửa đổi một chút ví dụ này để phát triển nền tảng giao dịch quyền chọn bán. Nền tảng này có một chức năng mạnh mẽ, đó là tất cả việc chuyển giá trị được thực hiện thông qua hợp đồng thông minh và cả hai bên tham gia giao dịch có thể trực tiếp thực hiện giao dịch mà không cần bên trung gian. Do đó, quá trình này không liên quan đến bất kỳ bên thứ ba nào, chỉ có hợp đồng thông minh và nguồn cấp dữ liệu giá Chainlink phi tập trung, là những ứng dụng DeFi điển hình nhất. Phát triển một nền tảng giao dịch quyền chọn phi tập trung sẽ bao gồm những điều sau:
So sánh các chuỗi trong Solidity
chuyển đổi một số nguyên thành một số thập phân cố định
Tạo và khởi tạo giao diện mã thông báo, chẳng hạn như LINK
Chuyển mã thông báo giữa người dùng/hợp đồng thông minh
Phê duyệt chuyển mã thông báo
SafeMath
Giao diện ABI hợp đồng thông minh
Thực hiện trạng thái giao dịch với yêu cầu ()
Ethereum msg.Value và sự khác biệt của nó với các giao dịch giá trị mã thông báo
Chuyển đổi giữa int và uint
địa chỉ phải trả
Cuối cùng, sử dụng giao diện tổng hợp dữ liệu Chainlink để lấy dữ liệu giá DeFi
VàGitHubVàRemixKiểm tra các mã có liên quan. Trước khi chính thức bắt đầu, chúng ta hãy giới thiệu ngắn gọn hợp đồng quyền chọn là gì. Hợp đồng quyền chọn cung cấp cho bạn tùy chọn để thực hiện giao dịch ở mức giá đã thỏa thuận vào một ngày nhất định. Cụ thể, nếu nội dung của hợp đồng quyền chọn là mua các tài sản như cổ phiếu hoặc mã thông báo, thì nó được gọi là quyền chọn mua. Ngoài ra, mã mẫu trong bài viết này có thể được sửa đổi một chút để đặt các tùy chọn. Quyền chọn bán trái ngược với quyền chọn mua và nội dung của nó không phải là mua một tài sản mà là bán nó. Sau đây là một số danh từ thích hợp liên quan đến các tùy chọn:
Giá thực hiện: giá mua/bán đã thỏa thuận của tài sản
Phí quyền chọn: Phí trả cho người bán khi mua hợp đồng
Expiration Date: Thời điểm hợp đồng kết thúc
Đình công: Hành động của người mua thực hiện quyền mua hoặc bán một tài sản với giá đình công
Cho dù bạn đang phát triển một cuộc gọi hay một cuộc gọi, bạn cần các yếu tố cơ bản của nhập khẩu, hàm tạo và biến toàn cục.
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 {
// tràn toán tử an toàn
using SafeMath for uint;
// giao diện nguồn cấp giá
AggregatorV3Interface internal ethFeed;
AggregatorV3Interface internal linkFeed;
// Giao diện mã thông báo LINK
LinkTokenInterface internal LINK;
uint ethPrice;
uint linkPrice;
// Tính toán trước giá trị băm của chuỗi
bytes32 ethHash = keccak256(abi.encodePacked("ETH"));
bytes32 linkHash = keccak256(abi.encodePacked("LINK"));
address payable contractAddr;
// Các tùy chọn được lưu trữ dưới dạng một mảng cấu trúc
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;
// Giá thức ăn Kovan: https://docs.chain.link/docs/reference-contracts
constructor() public {
// Nguồn cấp dữ liệu giá Kovan cho ETH/USD
ethFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
// LIÊN KẾT/USD giá thức ăn Kovan
linkFeed = AggregatorV3Interface(0x396c5E36DD0a0F5a5D33dae44368D4193f69a1F0);
//Địa chỉ mã thông báo liên kết trên Kovan
LINK = LinkTokenInterface(0xa36085F69e2889c224210F603D836748e7dC0088);
contractAddr = payable(address(this));
}
Khi nhập, chúng tôi cần truy cập giao diện tổng hợp dữ liệu của Chainlink để nhận ra chức năng cấp giá và truy cập giao diện mã thông báo LINK (Lưu ý: Ở đây chúng tôi cần sử dụng LINK để chuyển tiền, vì vậy chúng tôi cần sử dụng chức năng ERC20 của hợp đồng mã thông báo) . Cuối cùng, chúng tôi nhập khẩuSafeMath của OpenZeppelinHợp đồng, là các hoạt động thư viện tiêu chuẩn thực hiện kiểm tra tràn tích hợp, trong khi Solidity không bao gồm kiểm tra tràn trong các toán tử tích hợp sẵn của nó.
Nguồn cấp dữ liệu giá Chainlink
Nguồn cấp dữ liệu giá Chainlink
//Trả về giá LIÊN KẾT mới nhất
function getLinkPrice() public view returns (uint) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = linkFeed.latestRoundData();
// Nếu vòng này chưa kết thúc, dấu thời gian là 0
require(timeStamp > 0, "Round not complete");
// Giá sẽ không bao giờ âm, vì vậy bạn có thể chuyển đổi int thành uint
// Giá có 8 chữ số sau dấu thập phân, sau đó cần cộng 10 chữ số để thành 18 chữ số.
return uint(price);
}
tiêu đề cấp đầu tiên
Viết hợp đồng quyền chọn mua
//Cho phép người dùng viết các tùy chọn cuộc gọi được tổ chức
// Loại mã thông báo đã nhận, giá thực hiện (mã thông báo có mệnh giá bằng đô la Mỹ, với 18 chữ số sau dấu thập phân), phí tùy chọn (giống như dấu thập phân của mã thông báo), ngày hết hạn (unix), số lượng mã thông báo trong hợp đồng
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 latestCost = strike.mul(tknAmt).div(ethPrice.mul(10**10)); // Chi phí tập thể dục bằng ether, điều chỉnh dấu thập phân
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)));
}
}
Mô tả hình ảnh
Viết hợp đồng tùy chọn LINK cho mã thông báo LINK, đặt thời gian hết hạn Unix, giá thực hiện là 10 đô la và phí tùy chọn là 0,1 LINK.
tiêu đề cấp đầu tiên
Giao diện hợp đồng ABI
Khi bạn xem hợp đồng trên Etherscan, sẽ có hai tab, đó là: Đọc hợp đồng và Viết hợp đồng. Bạn có thể sử dụng hai tab này để tương tác với hợp đồng. Ví dụ: Hợp đồng mạng chính mã thông báo LINK. Etherscan biết những chức năng này là gì và cách gọi chúng thông qua ABI của hợp đồng. Sử dụng định dạng JSON để gọi ABI và chỉ định các tham số gọi hàm. Nó có thể được gọi trực tiếp trên mạng chính, nhưng hợp đồng LINK trên Kovan cần nhập mô-đun này. bạn có thể vàoLinkTokenMô tả hình ảnh
Mô tả hình ảnh
tiêu đề cấp đầu tiên
mua tùy chọn cuộc gọi
// Để mua tùy chọn cuộc gọi, bạn cần mã thông báo, ID tùy chọn và thanh toán
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");
// Người mua trả phí quyền chọn
require(msg.value == ethOpts[ID].premium, "Incorrect amount of ETH sent for premium");
// Người bán nhận được phí quyền chọn
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");
//Chuyển phí quyền chọn từ người mua sang người bán
require(LINK.transferFrom(msg.sender, linkOpts[ID].writer, linkOpts[ID].premium), "Incorrect amount of LINK sent for premium");
linkOpts[ID].buyer = msg.sender;
}
}
tiêu đề cấp đầu tiên
tùy chọn tập thể dục
// Tùy chọn cuộc gọi thực hiện, cần mã thông báo, ID tùy chọn và thanh toán
function exercise(string memory token, uint ID) public payable {
//Nếu quyền chọn chưa hết hạn và chưa được thực hiện thì người sở hữu quyền chọn được phép thực hiện
// Để thực hiện quyền chọn, người mua cần trả cho người bán giá thực hiện * số lượng và nhận số lượng token đã thỏa thuận trong hợp đồng
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");
//đáp ứng điều kiện thì thanh toán
updatePrices();
// Phí thực hiện
uint exerciseVal = ethOpts[ID].strike*ethOpts[ID].amount;
// Kết nối với Chainlink để cung cấp giá và chuyển đổi nó thành Ethereum
uint equivEth = ExerciseVal.div(ethPrice.mul(10**10)); //Chuyển đổi 8 chữ số thập phân của giá nguồn cấp dữ liệu thành 18
// Người mua trả ether tương đương với giá thực hiện * số lượng để thực hiện tùy chọn.
require(msg.value == equivEth, "Incorrect LINK amount sent to exercise");
// Trả phí thực hiện cho người bán
ethOpts[ID].writer.transfer(equivEth);
// Thanh toán cho người mua số lượng ether theo hợp đồng
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));
// Người mua thực hiện quyền chọn và trả phí thực hiện cho người bán
require(LINK.transferFrom(msg.sender, linkOpts[ID].writer, equivLink), "Incorrect LINK amount sent to exercise");
// Thanh toán số lượng hợp đồng của mã thông báo LINK cho người bán
require(LINK.transfer(msg.sender, linkOpts[ID].amount), "Error: buyer was not paid");
linkOpts[ID].exercised = true;
}
}
Mô tả hình ảnh
Ví dụ: Kết quả Remix xuất ra khi giao dịch không thỏa mãn một hoặc nhiều điều kiện.
Nếu các điều kiện được đáp ứng, phí thực hiện sẽ được trả cho người bán và số lượng mã thông báo theo hợp đồng sẽ được trả cho người mua. Khi thực hiện quyền chọn, người mua cần mua từng mã thông báo với giá thực hiện. Tuy nhiên, giá thực hiện được tính bằng USD, trong khi quy mô hợp đồng được tính bằng Ether hoặc LINK. Do đó, chúng tôi cần truy cập nguồn cấp dữ liệu giá Chainlink để tính toán số lượng ETH hoặc LINK tương đương với phí thực hiện. Sau khi chuyển đổi sang ETH hoặc LINK tương đương, chúng tôi có thể bắt đầu chuyển. Khi chuyển tiền, bạn cần sử dụng phương pháp đã đề cập ở trên, tức là tiền ether sẽ gọi hàm msg.value/address.transfer và LINK sẽ gọi hàm transferFrom().
tiêu đề cấp đầu tiên
Hủy hợp đồng/xóa tiền
// Cho phép người bán hủy hợp đồng hoặc lấy lại tiền từ các tùy chọn không đóng giao dịch thành công.
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");
// không được hủy hoặc mua
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;
}
}
//Cho phép người bán đổi tiền từ các tùy chọn đã hết hạn, chưa thanh toán và không bị hủy.
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");
// Phải hết hạn, không được thực hiện và không bị hủy bỏ.
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);
//Sửa đổi cờ hủy thành đúng để tránh nhiều lần quy đổi
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;
}
}
Khi thị trường biến động, người bán có thể hủy hợp đồng quyền chọn và mua lại tiền của họ nếu quyền chọn chưa được bán. Tương tự như vậy, nếu một quyền chọn hết hạn mà không được thực hiện, người bán chắc chắn sẽ muốn mua lại số tiền trong hợp đồng. Do đó, chúng tôi đã thêm các hàm cancelOption() và retrieveExpiredFunds()
Điểm quan trọng nhất của hai chức năng này là các điều kiện quy đổi phải được đáp ứng để gọi thành công. Người bán phải đáp ứng các điều kiện nhất định để đổi tiền và chỉ có thể đổi một lần. Người bán không thể hủy hợp đồng đã được bán, vì vậy chúng tôi cần xác nhận rằng địa chỉ của người mua vẫn là giá trị ban đầu 0. Ngoài ra, chúng tôi cũng cần xác nhận rằng tùy chọn chưa bị hủy và sau đó hoàn lại tiền. Nếu tiền được mua lại sau khi tùy chọn hết hạn, tình hình sẽ hơi khác một chút. Trong trường hợp này, quyền chọn có thể đã được bán nhưng không được thực hiện và tiền vẫn phải được trả lại cho người bán. Chúng tôi muốn xác nhận rằng hợp đồng đã hết hạn và chưa được thực hiện. Sau đó, cũng đặt cờ hủy của tùy chọn thành true để hoàn lại tiền nếu điều kiện được đáp ứng.
Tôi hy vọng bài viết này sẽ giúp bạn phát triển các trường hợp sử dụng Chainlink trên mainnet ngay lập tức và giúp bạn hiểu về các khả năng độc đáo của Solidity. Nếu bạn muốn tìm hiểu thêm về các tính năng của Chainlink, hãy xemChainlink VRF(chức năng ngẫu nhiên có thể kiểm chứng) hoặc xemDịch vụ đặt hàng công bằng Chainlink, hãy tìm hiểu cách Chainlink giải quyết vấn đề chạy trước của công cụ khai thác.
và tham gia với chúng tôi tạitài liệu dành cho nhà phát triểnvà tham gia với chúng tôi tạiDiscordliên hệ chúng tôi.bấm vào đâyliên hệ chúng tôi.


