Oracles provide a secure and reliable way for smart contracts to access real-time market data, which is essential for various DeFi applications. API3 has developed an oracle node that is operated by the API Provider, removing the intermediary node layer, or middleman. This change in oracle architecture creates a scalable and transparent solution that enables first-party oracles to be aggregated according to user requirements. DeFi protocols looking to utilize first-party oracles do so by integrating dAPIs to their smart contracts. With developers in mind, dAPIs have been designed with a simple integration process that abstracts away the technical implementation of accessing data feeds. The API3 Market provides the ability to manage these data feeds while giving users access to a range of first-party data feeds. In the future, additional data feed services, such as capturing (OEV) will be accessible once a dAPI has been integrated. Oracle Extracted Value This tutorial will demonstrate how easy it is for developers to switch from Chainlink data feeds to API3’s first-party oracles. Choosing the Contract To demonstrate how easy it is to port over from Chainlink to API3, we are going to port over the DeFi options contract from this C . hainlink tutorial We don’t need to go over the entire contract because we will only be modifying a small section of the contract to port it over, but in summary, the contract enables users to create, buy, exercise, and cancel options within the contract using Ethereum (ETH) and Chainlink (LINK) tokens. It makes use of Chainlink’s aggregator interface for obtaining price feeds while exercising options. For more of an in-depth explanation, you can check out the . full guide Now let’s look at the code we want to modify: 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(); } ----------------------- ----------------------- } The contract initializes the AggregatorV3Interfaces via the constructor for both the Eth and Link datafeeds; these can later be used to fetch the price. The two functions and use the AggregatorV3Interface to fetch the price and return it. The struct defines how options are stored when they are created, along with the and array to store the created options. getEthPrice() getLinkPrice() options ethOpts linkOpts Porting it Over Porting over the contract to use API3’s dAPIs in this options contract can be done in 3 easy steps: Fetching the proxy address of the “ETH/USD” and “LINK/USD” dAPIs from the API3 Market Import the proxy interface from API3 repo and set the proxy addresses in the constructor Replace the and the logic with a function call to the proxy interface getEthPrice() getLinkPrice() .read() And that's it. You do not need to hold any special type of token to be able to read from the oracle, it is completely free to read. Over 100+ dAPIs are currently available on the api3 market and work on a self-funded basis i.e you can top up the gas wallets to start reading from the oracle. If the dAPI is already funded you just need to copy the proxy address as seen below: Here’s the same DeFi options contract updated to use API3 dAPIs 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(); } ----------------------- ----------------------- } As you can see, we only needed to call on the interface to start reading from the datafeed. We ended up with lesser lines of code and a much simpler reading interface. You can try running the contract yourself on . (Note: The remix version only allows you to open and close options in ETH for simplicity). .read() IProxy(ethProxy) REMIX Why use dAPIs ? dAPIs have been designed to abstract away the technical implementation of data feeds. Once a dAPI has been imported to oracle contracts, the API3 DAO can redirect the dAPI mapping upon user requests. This means that data feeds can be upgraded from self-funded to managed dAPIs, or directed to read alternate reference data with zero technical implementation. Any update to data feeds, or a lack thereof, can create opportunities for OEV, such as arbitrage and liquidations. During each of these interactions value is leaking from the dApp users to both searchers and validators. Once a dAPI has been integrated, DeFi protocols will be able to capture Oracle Extractable Value without any further technical implementation. Additionally, switching from Chainlink to API3 data feeds means no major alterations to smart contracts. This mitigates the need for audits while keeping battle-tested code intact.