Как перейти на dAPI: руководство для разработчиков по переходу на Oracle
13 апреля 2023 г.Оракулы обеспечивают смарт-контрактам безопасный и надежный способ доступа к рыночным данным в режиме реального времени, что необходимо для различных приложений DeFi.
API3 разработал узел оракула, которым управляет поставщик API, удалив промежуточный уровень узла или посредника. Это изменение в архитектуре оракула создает масштабируемое и прозрачное решение, которое позволяет агрегировать собственные оракулы в соответствии с требованиями пользователя. Протоколы DeFi, стремящиеся использовать собственные оракулы, делают это путем интеграции dAPI в свои смарт-контракты.
Для разработчиков dAPI были разработаны с простым процессом интеграции, который абстрагируется от технической реализации доступа к потокам данных. API3 Market предоставляет возможность управлять этими потоками данных, предоставляя пользователям доступ к целому ряду собственных каналов данных. В будущем после интеграции dAPI будут доступны дополнительные службы подачи данных, такие как сбор Oracle Extracted Value (OEV). .
В этом руководстве показано, как легко разработчикам переключаться с каналов данных Chainlink на собственные оракулы API3.
Выбор контракта
Чтобы продемонстрировать, насколько легко выполнить перенос с Chainlink на API3, мы собираемся перенести контракт опционов DeFi из этого Cучебник по хайлинку.
Нам не нужно просматривать весь контракт, потому что мы будем изменять только небольшую часть контракта, чтобы перенести его, но в целом контракт позволяет пользователям создавать, покупать, исполнять и отменять опционы в рамках контракта, используя Токены Ethereum (ETH) и Chainlink (LINK). Он использует интерфейс агрегатора Chainlink для получения информации о ценах при исполнении опционов. Более подробное объяснение вы можете найти в полном руководстве.
Теперь давайте посмотрим на код, который мы хотим изменить:
pragma solidity ^0.8.17;
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";
contract chainlinkOptions {
//Pricefeed interfaces
AggregatorV3Interface internal ethFeed;
AggregatorV3Interface internal linkFeed;
//Interface for LINK token functions
LinkTokenInterface internal LINK;
uint ethPrice;
uint linkPrice;
//Precomputing hash of strings
bytes32 ethHash = keccak256(abi.encodePacked("ETH"));
bytes32 linkHash = keccak256(abi.encodePacked("LINK"));
address payable contractAddr;
//Options stored in arrays of structs
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 feeds: https://docs.chain.link/docs/reference-contracts
constructor() public {
//ETH/USD Kovan feed
ethFeed = AggregatorV3Interface(0x9326BFA02ADD2366b30bacB125260Af641031331);
//LINK/USD Kovan feed
linkFeed = AggregatorV3Interface(0x396c5E36DD0a0F5a5D33dae44368D4193f69a1F0);
//LINK token address on Kovan
LINK = LinkTokenInterface(0xa36085F69e2889c224210F603D836748e7dC0088);
contractAddr = payable(address(this));
}
//Returns the latest ETH price
function getEthPrice() public view returns (uint) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = ethFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
//Price should never be negative thus cast int to unit is ok
//Price is 8 decimal places and will require 1e10 correction later to 18 places
return uint(price);
}
//Returns the latest LINK price
function getLinkPrice() public view returns (uint) {
(
uint80 roundID,
int price,
uint startedAt,
uint timeStamp,
uint80 answeredInRound
) = linkFeed.latestRoundData();
// If the round is not complete yet, timestamp is 0
require(timeStamp > 0, "Round not complete");
//Price should never be negative thus cast int to unit is ok
//Price is 8 decimal places and will require 1e10 correction later to 18 places
return uint(price);
}
//Updates prices to latest
function updatePrices() internal {
ethPrice = getEthPrice();
linkPrice = getLinkPrice();
}
-----------------------
-----------------------
}
Контракт инициализирует AggregatorV3Interfaces через конструктор для каналов данных Eth и Link; позже их можно использовать для получения цены. Две функции getEthPrice()
и getLinkPrice()
используют AggregatorV3Interface для получения и возврата цены. Структура options
определяет, как параметры сохраняются при их создании, а также массивы ethOpts
и linkOpts
для хранения созданных параметров.
Перенос
Перенос контракта на использование dAPI API3 в этом опционном контракте можно выполнить в три простых шага:
- Получение прокси-адреса dAPI «ETH/USD» и «LINK/USD» из API3 Market< /а>
- Импортируйте интерфейс прокси из репозитория API3 и задайте адреса прокси в конструкторе.
- Замените логику
getEthPrice()
иgetLinkPrice()
вызовом функции.read()
для интерфейса прокси
Вот и все. Вам не нужно иметь какой-либо особый тип токена, чтобы иметь возможность читать из оракула, чтение совершенно бесплатно.
В настоящее время на рынке API3 доступно более 100+ dAPI, которые работают на основе самофинансирования, т. е. вы можете пополнить газовые кошельки, чтобы начать чтение из оракула. Если dAPI уже профинансирован, вам просто нужно скопировать адрес прокси, как показано ниже:
Вот тот же опционный контракт DeFi, обновленный для использования dAPI API3
pragma solidity ^0.8.17;
import "@api3/contracts/v0.8/interfaces/IProxy.sol";
contract Api3Options {
//Pricefeed proxies
address public ethProxy;
address public linkProxy;
uint ethPrice;
uint linkPrice;
//Precomputing hash of strings
bytes32 ethHash = keccak256(abi.encodePacked("ETH"));
bytes32 linkHash = keccak256(abi.encodePacked("LINK"));
address payable contractAddr;
//Options stored in arrays of structs
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 feeds: https://docs.chain.link/docs/reference-contracts
constructor(address _ethProxy, address _linkProxy) public {
//ETH/USD Proxy on Goerli
ethProxy = _ethProxy
//LINK/USD Proxy on Goerli
linkProxy = _linkProxy
contractAddr = payable(address(this));
}
//Returns the latest ETH price
function getEthPrice() public view returns (uint) {
(int224 value,uint32 timestamp) = IProxy(ethProxy).read();
// if the data feed is being updated with a one day-heartbeat
// interval, you may want to check for that.
require(
timestamp + 1 days > block.timestamp,
"Timestamp older than one day"
);
//Price should never be negative thus cast int to unit is ok
//Price is 18 decimal places
return uint(uint224(value));
}
//Returns the latest LINK price
function getLinkPrice() public view returns (uint) {
(int224 value,uint32 timestamp) = IProxy(linkProxy).read();
// if the data feed is being updated with a one day-heartbeat
// interval, you may want to check for that.
require(
timestamp + 1 days > block.timestamp,
"Timestamp older than one day"
);
//Price should never be negative thus cast int to unit is ok
//Price is 18 decimal places
return uint(uint224(value));
}
//Updates prices to latest
function updatePrices() internal {
ethPrice = getEthPrice();
linkPrice = getLinkPrice();
}
-----------------------
-----------------------
}
Как видите, нам нужно было только вызвать .read()
в интерфейсе IProxy(ethProxy)
, чтобы начать чтение из потока данных. В итоге мы получили меньше строк кода и гораздо более простой интерфейс для чтения. Вы можете попробовать запустить контракт самостоятельно на странице REMIX. (Примечание: версия ремикса позволяет открывать и закрывать параметры только в ETH для простоты).
Зачем использовать dAPI?
dAPI были разработаны, чтобы абстрагироваться от технической реализации фидов данных. После импорта dAPI в контракты оракула DAO API3 может перенаправить сопоставление dAPI по запросам пользователей. Это означает, что фиды данных могут быть переведены с самофинансируемых на управляемые dAPI или направлены на чтение альтернативных справочных данных без технической реализации.
Любое обновление каналов данных или их отсутствие может создать возможности для OEV, такие как арбитраж и ликвидация. Во время каждого из этих взаимодействий ценность утекает от пользователей dApp как к поисковикам, так и к валидаторам. После интеграции dAPI протоколы DeFi смогут получать Oracle Extractable Value без какой-либо дополнительной технической реализации.
Кроме того, переход с каналов данных Chainlink на API3 означает отсутствие серьезных изменений в смарт-контрактах. Это снижает потребность в проверках, сохраняя при этом проверенный в боевых условиях код.
Оригинал