# "Oracles that pay you" (dApps)
Api3 provides data feeds and pays dApps for using them.
1. [Api3 Market](#api3-market) serves a large and dynamic catalog of data feeds on all major EVM networks.
2. Api3 enables the Oracle Extractable Value (OEV) resulting from the usage of these data feeds to be captured, and pays it to the respective dApps in the form of [OEV Rewards.](#oev-rewards)
::: info 💡 Tip
For quick reference, you can copy-paste [`llms-full.txt`](https://docs.api3.org/llms-full.txt) to your choice of AI assistant.
:::
## Api3 Market
Liquidity is increasingly shifting to newly launched L2 networks, and dApps that are able to branch out to these more quickly are at a significant competitive advantage.
For dApps that utilize data feeds, this is only possible with a data feed provider that has recognized this fact and designed their solutions accordingly.
Our answer to this is [Api3 Market,](https://market.api3.org/) which enables a dApp developer to purchase a plan for the data feed they need and integrate it within minutes, without speaking to a representative or signing a contract.
Furthermore, the whole system is designed to streamline the addition of support for new networks and data feed types, resulting in a large and dynamic catalog.
## OEV Rewards
The state of a blockchain can only be updated in a discrete manner, with a confirmed block or a sequenced transaction.
Practical limits (such as block size and block time) apply to this process, which implies that these updates will invariably lag.
Since data feeds are also updated by updating the chain state, every data feed is at least slightly out of date at all times.
Rent-seeking third parties can exploit this fact to extract funds from data feed users in the form of Maximal Extractable Value (MEV).
[Oracle Extractable Value (OEV)](https://medium.com/api3/oracle-extractable-value-oev-13c1b6d53c5b) is a subset of MEV that oracles have priority in extracting by batching additional operations with their updates.
Furthermore, instead of searching for such OEV opportunities themselves, oracles can auction off this privilege.
Api3 holds transparent and permissionless auctions for OEV opportunities on OEV Network, and [pays](/dapps/oev-rewards/) 80% of the auction proceeds to the respective dApps.
OEV Rewards serves as a new and sustainable revenue stream for dApps.
::: info ⚠️ Disclaimer
We refer you to the Api3 [terms and conditions](https://api3.org/terms-and-conditions/), which apply to all services and software provided by Api3 (including but not limited to data feeds, OEV Network, and any example OEV bots).
Nothing in this documentation nor related materials should be interpreted as financial, business, nor professional advice.
:::
# Quickstart (dApps)
[Api3 Market](https://market.api3.org/) serves a large and dynamic catalog of data feeds on all major EVM networks.
Follow this guide to learn how to use Api3 Market to integrate a data feed into an example contract.
## Using Api3 Market
- Go to [market.api3.org.](https://market.api3.org/)
- Click the toggle button to view the testnets, and pick one where you have funds.
- On the network page, you will see a search bar (1), some of the data feeds that are already active on the network under "Featured Feeds" (2), and a link to the catalog (3).
Start typing the name of a data feed in the search bar, and click it once it appears in the drop-down.
Alternatively, you can view the catalog and click the data feed there, which will be equivalent to searching the full name of the data feed.
::: info 💡 Tip
All data feeds are inactive by default.
Purchasing a plan activates it until the plan expires.
Everyone can use an active data feed.
On the network page, active feeds appear as cards, and inactive feeds appear as rows with "Activate" buttons.
If you pick a data feed that is already active on the network, you will be taken to the data feed page directly.
Otherwise, you will be taken to the activation page first.
To experience the entire flow, we recommend picking a data feed that is not active.
:::
- If you have picked a data feed that is not active, you will be taken to the activation page next.
Select the parameters, review the duration and price, and click the "Purchase" button to pay.
- Once your purchase transaction is confirmed, you can proceed to view the data feed page.
Leave this tab open, as we will come back to it later in the guide.
Congratulations! 🎉 You have successfully activated a data feed.
Let's use it in an example contract now.
## Remix example
::: info 💡 Tip
This section uses Remix IDE.
If you prefer using Hardhat, you can clone [`data-feed-reader-example`](https://github.com/api3dao/data-feed-reader-example) and follow the instructions in its README.
:::
- Go to [Remix.](https://remix.ethereum.org)
- With the "File explorer" tab selected on the left sidebar, click the hamburger button and select "Clone".
Enter `https://github.com/api3dao/data-feed-reader-example` and click "OK".
- With the "File explorer" tab selected on the left sidebar, double-click `contracts/` to expand it, then click `DataFeedReaderExample.sol`.
- With the "Solidity compiler" tab selected on the left sidebar, click "Compile DataFeedReaderExample".
- With the "Deploy & run transactions" tab selected on the left sidebar, select "Injected Provider - MetaMask" from the "Environment" drop-down.
::: info 💡 Tip
If you just purchased a plan on Api3 Market, your MetaMask wallet should be connected to the correct network.
If not, ensure your wallet is connected to the network where the data feed exists, where we'll deploy DataFeedReaderExample.
:::
- Return to the data feed page on Api3 Market and click "Integrate".
- Click the copy icon next to the displayed Api3ReaderProxyV1 address.
Please note that [OEV Rewards](/dapps/oev-rewards/) is disabled on testnets.
For information about using this selection on mainnets, refer to [this section.](/dapps/integration/index.md#integration-information)
- Return to Remix IDE.
With the "Deploy & run transactions" tab selected on the left sidebar, paste the Api3ReaderProxyV1 address into the textbox next to the "Deploy" button, then click the button.
- After the transaction is confirmed, you can call the publicly accessible functions of DataFeedReaderExample.
Under "Deployed Contracts", expand 'DATAFEEDREADEREXAMPLE AT ...', then click "readDataFeed".
You'll see the values that DataFeedReaderExample reads from Api3ReaderProxyV1.
## What next?
This guide should help you get started with a hackathon project.
For production use of Api3 data feeds, please continue to the [integration section.](/dapps/integration/)
You can also learn how to [get paid](/dapps/oev-rewards/) for using Api3 data feeds.
# Using Api3 Market (dApps → Integration)
See the [Quickstart](/dapps/quickstart/index.md) page for a basic guide on how to use [Api3 Market.](https://market.api3.org/)
This page provides further details about using it in production.
## Update parameters
Update parameters specify the conditions that trigger a data feed update.
Api3 Market supports two update parameters: [deviation threshold](#deviation-threshold) and [heartbeat interval](#heartbeat-interval).
### Deviation threshold
Deviation is the difference between the on-chain and off-chain values of a data feed.
It is measured as a percentage, and an update is initiated when the deviation exceeds the **deviation threshold**.
For example, if the deviation threshold is 1% and the on-chain value of the data feed is 100, an update is initiated when the off-chain value goes below 99 or above 101.
::: info ℹ️ Info
Note that it is not possible to guarantee a maximum deviation amount, as there is no theoretical limit to how fast the off-chain value of a data feed can change.
When we refer to a 1% deviation threshold, we mean that at the time of a deviation threshold-related update, the deviation will have exceeded 1%.
:::
Api3 Market offers the following deviation threshold options:
- 5%
- 2.5%
- 1%
- 0.5%
- 0.25%
::: info ⚠️ Warning
We assume that lower deviation thresholds are always more desirable, and thus do not validate if updates are necessary according to the update parameters.
In simpler terms, a data feed with a 1% deviation threshold can be updated even if it has only deviated by 0.5%.
For rare use cases that require different behavior, we do not recommend using Api3 data feeds.
:::
Upholding a lower deviation threshold requires more frequent data feed updates.
Consequently, you can expect higher [prices](#pricing) for lower deviation thresholds.
### Heartbeat interval
A heartbeat is a data feed update that is made to uphold a maximum period of time between two consecutive updates, which is called the **heartbeat interval**.
Api3 Market only offers a 24-hour heartbeat interval.
::: info ℹ️ Info
Similar to how the deviation threshold works, an update is expected to be initiated when the on-chain value is older than the heartbeat interval.
However, we have observed that some users include `require()` statements in their contracts to verify that the heartbeat interval is upheld.
To reduce the probability of this usage pattern causing transactions to revert, we initiate heartbeat interval-related updates 2 minutes earlier than necessary.
Note that this still does not provide a hard guarantee, and your contract should be able to handle cases where the on-chain value is older than the heartbeat interval.
:::
## Plan durations
Api3 Market offers 7-day plans on testnets and 3-month plans on mainnets.
Each purchased plan has an expiration date, and the respective update parameters will stop being upheld after that.
Let's go over a few example cases:
- BTC/USD on Ethereum is inactive.
The user purchases a 1% deviation threshold for 3 months.
The data feed will immediately activate and deactivate 3 months later.
- BTC/USD on Ethereum is active with a 1% deviation threshold, set to expire 1 month later.
The user purchases a 1% deviation threshold for 3 months (with a [discount](#discounts)).
The expiration will be extended to 3 months from now.
- BTC/USD on Ethereum is active with a 5% deviation threshold, set to expire 1 month later.
The user purchases a 1% deviation threshold for 3 months (with a [discount](#discounts)).
The data feed will switch to a 1% deviation threshold and deactivate 3 months later.
- BTC/USD on Ethereum is active with a 0.5% deviation threshold, set to expire 1 month later.
The user purchases a 1% deviation threshold for 3 months (with a [discount](#discounts)).
The data feed will continue running with a 0.5% deviation threshold for 1 month, switch to a 1% deviation threshold, run for another 2 months, and deactivate.
When plans with different deviation parameters are queued, the Api3 Market interface displays them as shown below.
::: info 💡 Tip
Once a plan has been purchased, Api3 guarantees that the [update parameters](#update-parameters) will be upheld for the [plan duration](#plan-durations).
However, it is the user's responsibility to ensure that plans are purchased to keep the data feed active as long as necessary.
You can use the "Set Reminder" button under the expiration date to avoid forgetting to renew your plans.
:::
## Pricing
The gas cost of operating a data feed is a function of:
- The expense of data feed updates
- The frequency of data feed updates
- The duration of data feed operation
We maintain a history of data feed update gas costs and update counts required to uphold the offered deviation thresholds, and estimate the future operational costs of the offered plans based on these.
::: info 💰 Financial
The prices you see on Api3 Market are the exact operational costs that we estimate (or $0.05/day, whichever is higher).
This means that it is unlikely that you will find a better bargain.
We do not plan to monetize data feed plans at any point.
Our monetization model is designed around OEV, which makes this pricing strategy sustainable.
:::
### Discounts
It is not possible to estimate the future gas cost of operating a data feed with perfect accuracy.
If we underestimate and find that the price was below the gas cost, we cover the difference.
::: info ⚠️ Warning
**On testnets only**, we stop updating if the payment runs out, even if the plan has not expired yet.
To resume updates in such cases, simply purchase a new plan.
:::
If we overestimate the price, the remainder rolls over to the next plan purchased for the same network–data feed pair, which appears as a **discount** on Api3 Market as seen below.
Similarly, when a user purchases a plan for a data feed that is already active, the remainder of the payments made for earlier purchases will appear as a discount.
::: info 💰 Financial
In some cases, the discount allows you to get the plan for free.
:::
### Gas grants
You can request a gas grant for your dApp by filling out [this form,](https://api3dao.typeform.com/to/TBTu8bJt) where you can ask us to purchase plans for you.
## Integration information
Clicking "Integrate" on a data feed page will display the information needed for a [contract integration.](/dapps/integration/contract-integration.md)
By default, you will see that "Skip OEV Rewards" is selected and an Api3ReaderProxyV1 address is displayed.
If you wish to forgo OEV Rewards, you can simply use this address in your integration.
Alternatively, you can select the "Earn OEV Rewards" option, provide the name of your dApp, and (if the proxy has not been deployed earlier) click the "Deploy Proxy" button to send a transaction.
Once the transaction goes through, the Api3ReaderProxyV1 address you should use in your integration will be displayed.
Note that this address is different from the one displayed when "Skip OEV Rewards" is selected.
::: info 💡 Tip
Can't find your dApp?
Follow the OEV Rewards [onboarding steps](/dapps/oev-rewards/index.md#how-to-get-onboard) first.
:::
## Verifying first-party sources
The term _first-party oracle_ is coined in the Api3 whitepaper and refers to an API provider that provides oracle services without depending on any middlemen.
There are three conditions to be verified to check if an oracle service is first-party:
- The operator of each individual node must also operate an independent API service as their primary business model for them to be called API providers.
- Each API provider must certify their public key and sign their data with the respective private key.
- Each API provider must make their signed data available themselves, without depending on third-party APIs, blockchains, or state channels.
Api3 provides the only first-party oracle solution.
Furthermore, we have implemented functionality for the public to be able to easily audit this.
On a data feed page, when you hover your mouse over the logo of a source, you can observe the Market frontend verifying the conditions above in real-time.
If you want to see what happens under the hood, you can click "Verified," which allows you to make an HTTP request to the "signed API" of the source, https://signed-api.coingecko.com/, and receive a response such as the following:
```json
{
"stage": "aws",
"version": "3.0.1",
"currentTimestamp": "1740583493",
"deploymentTimestamp": "1739870993",
"configHash": "0xbd159fb423d5eef7abd7947cf8ad1731f0c60cc2e093877837988907580539c9",
"certifiedAirnodes": ["0x6b56E47DccFbC82D63Df3da417d26e8B1B877f0f"]
}
```
We know that CoinGecko is a reputable API provider, they own the `coingecko.com` domain, and this response comes from that domain.
`certifiedAirnodes` is the list of addresses of the accounts that CoinGecko uses to sign its data (only `0x6b56E47DccFbC82D63Df3da417d26e8B1B877f0f` in this case), and the Market frontend confirms that the respective data feed is configured to use data signed by one of these certified accounts.
Finally, all recent data points signed by CoinGecko can be fetched directly from them through https://signed-api.coingecko.com/public/0x6b56E47DccFbC82D63Df3da417d26e8B1B877f0f.
# Contract integration (dApps → Integration)
This page provides important information on how to integrate Api3 data feeds into a contract.
Please read it in its entirety before attempting an integration.
::: info ⚠️ Warning
Api3 does not authorize any members or affiliates to provide security advice.
You are solely responsible for following the instructions on this page.
:::
## Api3ReaderProxyV1
Api3ReaderProxyV1 is a contract that is used to read a specific Api3 data feed.
For example, to read ETH/USD on Blast, one can simply call the [`read()` function](https://blastscan.io/address/0x5b0cf2b36a65a6BB085D501B971e4c102B9Cd473#readProxyContract#F17) of a respective Api3ReaderProxyV1.
You can use Api3 Market to see the Api3ReaderProxyV1 address you should use for a specific data feed, as described [here.](/dapps/integration/index.md#integration-information)
To summarize, you should use the Api3ReaderProxyV1 address that appears after selecting "Earn OEV Rewards" and entering the name of your dApp.
::: info ⚠️ Warning
To be eligible for OEV Rewards, you are required to use the Api3ReaderProxyV1 contracts belonging to your dApp.
:::
### Printing Api3ReaderProxyV1 addresses
For your convenience, Api3 representatives may deploy OEV Rewards-enabled Api3ReaderProxyV1 contracts on your behalf and provide you with a list of commands that will print their addresses.
By running these commands yourself, you can ensure that you are using the correct addresses.
::: info 💡 Tip
We try to verify our contracts on all block explorers, with varying success due to their practical limitations.
Since Api3ReaderProxyV1 is deployed deterministically, the lack of verification on a block explorer does not pose a security concern.
:::
These commands should be in the following format, where the dApp alias (assigned to you by Api3 during [registration](/dapps/oev-rewards/index.md#how-to-get-onboard)), chain ID, and dAPI names match your specific case:
```sh
npx @api3/contracts@latest print-api3readerproxyv1-address \
--dapp-alias lendle \
--chain-id 5000 \
--dapi-name ETH/USD
```
The command above prints:
```
dApp alias: lendle
chain: Mantle
dAPI name: ETH/USD
• Please confirm that https://market.api3.org/mantle/eth-usd points to an active feed.
• Your proxy address is https://mantlescan.xyz/address/0x776E79D916e49BBDb8FEe0F43fF148C2Ed3bE125
Please confirm that there is a contract deployed at this address before using it.
```
Note that if an Api3 representative has provided you with this command, you can expect the Market page to point to an active feed and the proxy to already be deployed.
Do not proceed with the integration until you confirm both conditions.
### Reading the data feed
Api3ReaderProxyV1 implements IApi3ReaderProxy, which you can import from [`@api3/contracts`](/dapps/integration/api3-contracts.md) to use in your contract.
```solidity
interface IApi3ReaderProxy {
function read() external view returns (int224 value, uint32 timestamp);
}
```
::: info 💡 Tip
Api3ReaderProxyV1 also implements Chainlink's AggregatorV2V3Interface, which enables it to be used as a drop-in replacement for Chainlink data feeds.
Refer to the [AggregatorV2V3Interface page](/dapps/integration/aggregatorv2v3interface.md) for details.
:::
#### Using `value`
`value` is an `int224`, which is the median of individual on-chain data feed `value`s that contribute to the aggregation.
::: info 💡 Tip
Note that `value` has a signed type.
However, in the context of a data feed that reports the price of an asset, non-positive values do not make sense.
It is good practice to validate against such conditions, as in `require(value > 0)`.
:::
All Api3 data feeds have 18 decimals.
For example, if ETH/USD is `2918.5652133`, `value` will read `2918565213300000000000`.
::: info ⚠️ Warning
It is extremely risky to validate the data feed value based on practical assumptions.
An example where doing so went wrong was Chainlink requiring their LUNA/USD data feed value to be at least `0.1`.
Doing so caused them to misreport by an order of magnitude during the UST depeg, and caused a dApp to suffer more than $14MM in damages.
We do not utilize such heuristics on our end, and recommend that you be very careful if you do.
:::
#### Using `timestamp`
`timestamp` is a `uint32`, which is the median of individual on-chain data feed `timestamp`s that contribute to the aggregation.
The `timestamp` of an individual data feed represents the system timestamp that the respective API provider reported when they called their API to get the value.
Its main role is to act as a nonce that prevents data feed updates from being replayed.
::: info ⚠️ Warning
`timestamp` is not the block timestamp at the time of the update.
It is the reported system (i.e., off-chain) time.
One common mistake is using `require(timestamp <= block.timestamp)`.
This check should be avoided for two reasons:
1. If `block.timestamp` lags compared to actual time, this will revert.
However, that is not a valid reason to avoid using the data feed, as doing so will cause unnecessary downtime for your contract.
2. Some L2 implementations use the timestamp of the latest block as `block.timestamp` (rather than the system time of the node) when a static call is made to the RPC endpoint.
This means that the `require()` will revert during static calls even when `block.timestamp` does not actually lag.
This prevents OEV searchers from using the intended workflow and reduces the amount of OEV Rewards you will receive in practice.
:::
In general, the only acceptable use of `timestamp` is validating whether the heartbeat interval is upheld, as in `require(timestamp + 24 hours > block.timestamp)`.
However, unless your contract design specifically relies on the data feed value being at most a day old (which is unlikely), we do not necessarily recommend this approach either.
::: info 💡 Tip
Your auditors may not be familiar with best practices in the context of Api3 data feeds.
We recommend directing them to this page.
:::
## Api3ReaderProxyV1 combinations
See the [`data-feed-proxy-combinators`](https://github.com/api3dao/data-feed-proxy-combinators) repository for various modular contracts that you can use to create combinations out of Api3ReaderProxyV1 contracts.
For example, you can combine an `ETH/USD` Api3ReaderProxyV1 contract and a `wstETH/ETH Exchange Rate` Api3ReaderProxyV1 contract to read a `wstETH/USD` value.
## Mixed oracle design
Some dApps choose to mix oracle solutions, either by refusing service if they are not in consensus, or by using one primarily and deferring to another in case of inconsistency.
In such setups, Api3 data feeds need to be treated differently due to OEV considerations.
Specifically, the vast majority of OEV is extracted during times of volatility, and allowing other oracle solutions interfere during such times may result in the loss of a significant amount of OEV revenue.
In practice, this will play out as an OEV searcher bidding a significant amount for a detected OEV opportunity, only to realize after the auction ends that the dApp now defers to a non-Api3 data feed and the OEV opportunity no longer exists.
Such ambiguity will deter OEV searchers from your dApp or cause them to bid much less to hedge their risk, reducing your [OEV Rewards](/dapps/oev-rewards/).
The golden standard is only using Api3 data feeds, in which case OEV searchers will be able to bid on OEV opportunities with full confidence, knowing that they will be able to extract if they win the auction.
If you must use Api3 data feeds as your primary source with another solution as a fallback, you should tolerate as much inconsistency as possible.
::: info 💡 Tip
We recommend that you tolerate at least 10% inconsistency.
Based on our analysis, any less will hinder OEV extraction during times of high volatility.
:::
Note that using Api3 data feeds for only some asset prices still counts as a mixed design.
Consider a lending platform that uses the ETH/USD Api3 data feed and the USDT/USD data feed from another oracle solution.
A user takes out a USDT loan with ETH collateral, and the following price action renders the position liquidatable once the ETH/USD data feed is updated.
Even if an OEV searcher detects the opportunity, they must consider that a rogue USDT/USD update by the other oracle solution may expose it to the public before they can claim it, leading them to avoid bidding a fair amount.
::: info 💰 Financial
It is up to you to maximize your OEV Rewards by integrating correctly.
Not maximizing OEV Rewards causes a loss of profits and therefore constitutes a security issue.
:::
# AggregatorV2V3Interface (dApps → Integration)
AggregatorV2V3Interface is intended for use by contracts that were originally built to use Chainlink data feeds.
All considerations from the [contract integration page](/dapps/integration/contract-integration.md) still apply.
::: info ⚠️ Warning
Api3 data feeds are aggregated from asynchronous data feeds to provide maximal availability guarantees, which means they are not updated in rounds.
As a side effect, Api3ReaderProxyV1 does not implement the round-related functionality of AggregatorV2V3Interface.
If your contract depends on round-related functionality, it would not be appropriate to use Api3ReaderProxyV1 via AggregatorV2V3Interface.
Instead, we recommend using IApi3ReaderProxy with a custom adapter that fits your specific needs.
:::
You can interact with Api3ReaderProxyV1 through AggregatorV2V3Interface if all of the following conditions apply:
- Your contract primarily relies on the current data feed value (`latestAnswer()` of AggregatorInterface or `answer` returned by `latestRoundData()` of AggregatorV3Interface).
- If your contract uses the current data feed timestamp (`latestTimestamp()` of AggregatorInterface or `updatedAt` returned by `latestRoundData()` of AggregatorV3Interface), it uses it only for a staleness check (e.g., to verify if the feed has been updated within the last heartbeat interval).
- Any other values used do not affect your contract's logic or your dApp's off-chain infrastructure.
For example, your contract may emit `roundId` in an event strictly for logging purposes.
- Your dApp's off-chain infrastructure does not depend on events defined in AggregatorInterface.
::: info 💡 Tip
Lending protocols typically satisfy these conditions.
:::
On the other hand, you should not interact with Api3ReaderProxyV1 through AggregatorV2V3Interface if any of the following conditions applies:
- Your contract depends on Chainlink feed implementation details, such as the round ID increasing with every update.
- Your contract depends on querying past updates using `getAnswer()` or `getTimestamp()` of AggregatorInterface, or `getRoundData()` of AggregatorV3Interface.
- Your dApp's off-chain infrastructure depends on events defined in AggregatorInterface.
::: info 💡 Tip
DeFi protocols such as perpetual derivative exchanges are typically vulnerable to MEV searchers performing time arbitrage.
Chainlink data feeds provide past round data primarily to address this issue, but this solution significantly degrades UX by requiring multiple transactions for certain actions.
Instead, you can simply read the latest data feed value from an Api3 data feed and receive compensation for time arbitrage value extraction through [OEV Rewards.](/dapps/oev-rewards/)
:::
If you have any doubts about interacting with Api3ReaderProxyV1 through AggregatorV2V3Interface, you can refer to the [Api3ReaderProxyV1 implementation](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/proxies/Api3ReaderProxyV1.sol) to see exactly how these functions behave.
# `@api3/contracts` (dApps → Integration)
[`@api3/contracts`](https://www.npmjs.com/package/@api3/contracts) is an npm package that provides three basic features for Api3 data feed users:
1. `@api3/contracts/interfaces/IApi3ReaderProxy.sol` is imported by contracts that call Api3ReaderProxyV1 contracts through IApi3ReaderProxy.
2. `@api3/contracts/mock/MockApi3ReaderProxy.sol` is used in tests.
3. - `computeCommunalApi3ReaderProxyV1Address()` is used to validate adresses shown by Api3 Market when ["Skip OEV Rewards"](/dapps/integration/index.md#integration-information) is selected.
- `computeDappSpecificApi3ReaderProxyV1Address()` is used to validate adresses shown by Api3 Market when ["Earn OEV Rewards"](/dapps/integration/index.md#integration-information) is selected.
For detailed examples of how to use these features, see the [`data-feed-reader-example` repository.](https://github.com/api3dao/data-feed-reader-example)
Additionally, `@api3/contracts` provides a CLI command for printing OEV Rewards-enabled (i.e., dApp-specific) Api3ReaderProxyV1 addresses, as described [here.](/dapps/integration/contract-integration.md#printing-api3readerproxyv1-addresses)
::: info ℹ️ Info
[AggregatorV2V3Interface](/dapps/integration/aggregatorv2v3interface.md) is not exported from this package, since contracts using this interface must have already imported it from elsewhere.
:::
# Security considerations (dApps → Integration)
A data feed is an on-chain service that is driven by off-chain components.
Therefore, as long as you continue to use it, there will be ongoing security considerations that you should be aware of.
::: info ℹ️ Info
Api3 data feeds have never misreported or experienced an outage.
This page merely discusses theoretical scenarios that apply to any data feed.
:::
## Smart contract risk
Imperfections in a smart contract implementation may cause it to behave unexpectedly, potentially resulting in financial losses for interacting parties—a scenario known as smart contract risk.
Like all smart contracts, Api3ReaderProxyV1 carries this inherent risk.
We propose three methods to assess the smart contract risk.
You can refer to the related [audit reports.](https://github.com/api3dao/contracts?tab=readme-ov-file#security)
A more practical approach is to refer to [our historical TVS](https://defillama.com/oracles/Api3) to understand the _battle-testedness_ of our data feeds.
Additionally, you are welcome to review the contracts behind [Api3ReaderProxyV1](/dapps/integration/contract-integration.md#api3readerproxyv1), specifically [Api3ServerV1](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1.sol) and [Api3ServerV1OevExtension](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1OevExtension.sol).
Our [contract developer docs](https://github.com/api3dao/contracts/tree/main/docs) provide additional context for understanding the design decisions behind these contracts.
## Privileged accounts
Api3ReaderProxyV1 is a [UUPS-upgradeable](https://eips.ethereum.org/EIPS/eip-1822) contract, which can be upgraded by [a 4-of-8 multisig](https://github.com/api3dao/contracts/blob/main/data/manager-multisig-metadata.json#L2) that is owned by members of the Api3 technical team.
This upgradeability feature is intended to be used only in exceptional occasions to respond to newly discovered compiler, library or contract vulnerabilities, or to migrate users to potential new versions of the contracts.
[A 4-of-4 multisig,](https://github.com/api3dao/contracts/blob/main/data/dapi-management-metadata.json#L2) which again is owned by members of the Api3 technical team, approves the root of a Merkle tree containing data feed configurations.
This means adding, removing or replacing API providers that contribute to the aggregation of data feeds requires signatures from all owners of this multisig.
::: info ℹ️ Info
Data feed source configuration depends on multiple factors including uptime, accuracy, incident response time, and qualitative considerations.
Multisig signers have access to this data and are responsible for its verification.
:::
## Data correctness
The [Api3 whitepaper](https://github.com/api3dao/api3-whitepaper) poses that all oracle data comes from API providers in practice, and the trust-minimized way to receive data from an API provider is for there to be no third-party intermediaries.
We have coined the term _first-party oracle_ to refer to this architecture, where API providers deliver oracle services without needing third parties to facilitate.
Api3 data feeds are on-chain aggregations of data feeds powered by individual first-party oracles.
Each API provider powers a single-source data feed on-chain, and the Api3 data feed is an on-chain median of these individual data feeds, which provides the strongest security guarantees (for example, compared to off-chain aggregation).
You can easily verify the first-party nature of our data feeds [directly on the data feed page in Api3 Market.](/dapps/integration/index.md#verifying-first-party-sources)
::: info ⚠️ Warning
While some oracle solutions are at peace with their third-party status, other oracle solutions such as Pyth incorrectly claim their oracles are first party.
This claim fails in two ways:
First, an API provider is a business that provides an API as a service, and most Pyth oracles are not API providers.
Second, even when a Pyth oracle is an API provider, their data is aggregated and served through Wormhole, introducing a third-party point of failure.
In general, when oracle services use an intermediary blockchain or state channel for delivery, they create a third-party system since the intermediary's consensus model will not match the aggregation model.
Consider a system where 7 API providers supply data and 100+ node operators provide aggregation and data availability—this creates two points of failure, typical of third-party oracle designs.
The secondary point of failure (the node operators) is typically weaker than the primary one (the API providers).
Consequently, when dApps use Pyth data on Ethereum, users pay Ethereum gas fees while only receiving security equivalent to [Wormhole.](https://www.google.com/search?q=wormhole+outage+downtime+"pyth")
:::
## Update parameters
Once a plan is purchased on Api3 Market, the respective data feed will maintain the advertised deviation threshold and heartbeat interval until plan expiration.
This depends on the Api3 technical team to keep the wallets that will send the update transactions funded, and maintain the infrastructure that will use these wallets to send the update transactions.
The operation is backed by dedicated monitoring personnel, automated alerts, and redundant infrastructure layers.
::: info ℹ️ Info
We have been providing oracle services as early as [2019](https://etherscan.io/txs?a=0x78e76126719715eddf107cd70f3a31dddf31f85a&p=1029), and were listed as the [best responding oracle](/assets/reputation-link.CxhU2iIj.png) among all Chainlink oracles by [`reputation.link`](https://www.google.com/search?q=%22reputation.link%22+chainlink) as of September 2020, which is when we published the [Api3 whitepaper](https://github.com/api3dao/api3-whitepaper) and requested to be removed from Chainlink data feeds.
An important driving factor for this was our insight into systemic issues that could harm users and our confidence in building a better solution.
With this understanding, we designed our architecture and operations from the ground up, leading to our current performance.
:::
## Data availability
We obtain API provider-signed data for feed updates from publicly accessible APIs (the same ones you can use to [verify first-party sources on Api3 Market.](/dapps/integration/index.md#verifying-first-party-sources))
While similar to the [Coinbase price oracle](https://www.coinbase.com/blog/introducing-the-coinbase-price-oracle), our approach involves multiple API providers using our standardized protocol, enabling aggregation.
As a result, even if we cease updating a data feed, further updates remain possible.
MEV searchers, for instance, can access these APIs to perform financially relevant updates.
Similarly, our OEV implementation uses this mechanism, ensuring OEV updates continue even if we stop updating the feed according to the update parameters.
## Oracle Extractable Value (OEV)
OEV updates provide identical guarantees to regular updates—they are on-chain aggregations of API provider-signed data—so they introduce no additional [data correctness](#data-correctness) risk.
The OEV auction mechanism allows winners to frontrun updates of an artificially delayed base feed, a tradeoff designed to benefit the dApp.
Here's how the process works.
The lifecycle of a data point consists of three phases:
1. From 0-30 seconds: OEV searchers examine the data point and place bids on potential OEV opportunities.
2. From 30-60 seconds: If valid bids exist, the auction winner can update the data feed to capture OEV opportunities.
3. After 60 seconds: The data point becomes publicly available for updates, including our update parameter-based updates.
dApps using our system will experience a 30–60 second delay in their data feed.
While this delay concerns some users, we can evaluate its impact through a simple framework:
Consider a dApp that generates `X1` revenue with its current oracle solution.
Using Api3 data feeds would generate `X2` revenue (potentially lower than `X1` due to the delay) plus `Y` in OEV Rewards.
When `X1 < X2 + Y`—which is common—Api3 feeds are the more secure choice.
::: info 💡 Tip
An overlooked fact is that traditional oracle solutions without OEV capture are inherently vulnerable.
Their users have faced [hundreds of millions of dollars](https://members.delphidigital.io/reports/api3-the-state-of-oev) in exploits over the years—exploits that were entirely preventable.
Auditors should flag dApps lacking effective OEV capture mechanisms, and dApps continuing to use vulnerable traditional solutions should be required to justify this choice.
:::
# Getting paid (dApps → OEV Rewards)
dApps that use traditional data feeds are constantly exploited by MEV bots that manipulate the order of operations around individual data feed updates.
In practice, this causes dApps to suffer significant and continuous financial losses.
As the antidote, Api3's OEV Network auctions off to OEV searchers the privilege to determine the order of operations around data feed updates.
80% of the [resulting revenue](#breaking-down-oev-rewards) is paid to the dApp in the form of OEV Rewards.
::: info 💰 Financial
Api3 provides data feeds [at cost](/dapps/integration/index#pricing) and enables dApps to benefit from OEV Rewards on top.
You might ask, "What's the catch?"
There is none; OEV Rewards come at the expense of third parties who would otherwise solely benefit from MEV.
:::
Api3 data feeds work identically to traditional data feeds, which means that you do not need to modify your contracts in any way to use them.
You can drop in Api3 data feeds to replace your current data feeds and immediately start earning OEV Rewards.
If you want to maximize your OEV Rewards, make sure to check out our [guide for best practices.](/dapps/oev-rewards/best-practices)
## How to get onboard
Use [this form](https://api3dao.typeform.com/to/FHhFIL41) to get in contact with an Api3 representative who will walk you through the following steps:
1. We register your dApp for it to show up on the Api3 Market [integration page.](/dapps/integration/index.md#integration-information)
2. You let us know which chains you operate on so we can start running the respective auctions.
::: info 💡 Tip
Don't forget to notify us if you expand to new chains later on.
:::
3. You let us know which data feeds you will use.
If a gas grant is applicable, we purchase subscriptions for you and deploy an OEV Rewards-enabled Api3ReaderProxyV1 contract for each data feed.
::: info 💡 Tip
Alternatively, you can complete this step on your own using [Api3 Market.](https://market.api3.org/)
:::
4. You let us know an address where to receive the OEV Rewards.
::: info 💡 Tip
OEV Rewards are paid in the native currency of your dApp's chain.
The receiving address must be an EOA you control or a contract capable of receiving native currency payments.
:::
5. You [integrate](/dapps/integration/contract-integration) the OEV Rewards-enabled Api3ReaderProxyV1 contracts.
At the end of each month, Api3 will make available a report and 80% of the OEV revenue in the native gas token of the network where your dApp is deployed, with the remainder retained as the protocol fee.
## Breaking down OEV Rewards
dApps receive 80% of the OEV revenue in the form of OEV Rewards.
This revenue can be broken down into three parts:
1. Proceeds of the auctions held on OEV Network
2. Revenue from OEV searching activity facilitated by Api3 to ensure a baseline level of competition in the auctions (e.g., with a 20% profit margin, which should be easily beaten by organic searchers)
3. Revenue from MEV searching activity facilitated by Api3 as a failsafe for the above
::: info ℹ️ Info
The OEV and MEV searching activity facilitated by Api3 is strictly limited to using data that is already available to the public, as documented in the [OEV searcher docs.](/oev-searchers/)
This can be audited retrospectively by referring to data on OEV Network and the chain that the dApp is on.
A side-effect of the above is that once organic searcher activity takes hold for a dApp, Api3 will no longer be able to generate searcher revenue, and the auction proceeds will constitute the entirety of the OEV Rewards.
This will result in more efficient and robust capturing of OEV, and thus is a desirable outcome for the dApp.
:::
# Best practices (dApps → OEV Rewards)
::: info ⚠️ Warning
[Mixed oracle design](/dapps/integration/contract-integration#mixed-oracle-design) is the main culprit behind lackluster OEV Rewards.
If your contract integration is faulty, the suggestions below are unlikely to help.
:::
For OEV auctions to be competitive, there must be multiple independent OEV searcher parties.
An OEV searcher is a blockchain developer with specific expertise whose time is typically quite valuable.
Therefore, for a maximum amount of OEV Rewards, searching for your dApp must be as easy as possible.
::: info 💰 Financial
Investing resources in bootstrapping OEV searcher activity may be required to maximize OEV Rewards.
:::
OEV searchers make a simple revenue–cost estimation before deciding whether to search for your dApp.
The easiest way to tilt this equation in your favor is to provide excellent documentation on how OEV searchers should interact with your dApp.
Going one step further is developing and open-sourcing an OEV bot for your dApp that anyone can use and improve upon.
An open-source bot that works well and is easy to operate will attract many users, driving searcher profit margins down and your OEV Rewards up.
Finally, you can [be your own OEV searcher](/oev-searchers/) and participate in the auctions of your dApp.
::: info 💡 Tip
If your dApp is a fork of a well-established DeFi protocol, the barrier to entry for searching your dApp will be minimal.
Since Api3 is also incentivized to maximize OEV Rewards, we will provide access to a library of example open-source OEV bots.
:::
The second way to attract OEV searchers and maximize OEV Rewards is to increase incentives.
For example, a lending platform that pays 10% of the position size as a liquidation reward will attract more attention than one that pays 5%.
Similarly, a perpetual derivative exchange that uses Api3 data feeds as intended is likely to yield a significant amount of OEV Rewards.
::: info 💰 Financial
Consider treating OEV Rewards as your main source of revenue rather than an extra source.
Do you really need to charge a protocol fee for your dApp if you're receiving sufficient OEV Rewards?
:::
# Overview (OEV Searchers)
Oracle Extractable Value (OEV) is a subset of Maximal Extractable Value (MEV) that occurs as a result of an oracle update.
The idea is that different oracle updates have different importance.
Some updates expose profitable opportunities on the market. Searchers actively compete
with each other to be the first to realize these - paying a majority of the exposed value
to block validators in the process. This dynamic is unhealthy, because the
majority of the value should be split between the dApp and the searcher that
realizes the opportunity.
OEV solves this problem by auctioning off the exclusive rights to execute the
oracle update(s), allowing searchers to atomically update the price feed(s) used
by the dApps and capture opportunities in the market. The exclusive update
rights guarantee no competition and searchers avoid paying premiums on gas fees.
With OEV, searchers announce their intent to perform oracle updates along with
the amount they are willing to pay for it. The process is facilitated by open auctions,
bound by rules enforced on-chain. The auction winner must pay the announced
amount, which in return allows them to perform the oracle update and capture
profitable opportunities.
::: info 💡 Tip
For quick reference, you can copy-paste [`llms-full.txt`](https://docs.api3.org/llms-full.txt) to your choice of AI assistant.
:::
## Practical example
Imagine an overcollateralized lending platform that uses Api3 price feeds.
Borrowers in the protocol can be liquidated with an incentive whenever their
position becomes unhealthy to ensure the protocol remains solvent. Say
liquidations can occur if the loan-to-value ratio exceeds 90%. Let's look at
what happens with the protocol's health over time.
Assume that initially there are no unhealthy positions. Many of the price feed
updates that happen are "unnecessary" because they don't expose any unhealthy
positions and the protocol remains healthy. However, after some time there is a
price drop that causes many positions using that asset as collateral to approach
the 90% liquidation threshold.
In this scenario, the next price update that causes a position to become
unhealthy is valuable. From the protocol's perspective, this affects its
solvency and presents a threat. For a searcher, this presents a
profitable opportunity. In theory, the value of the price update equals to the
profit the searcher can make.
Searcher monitors the dApp and public Api3 data sources and sees that a position will become unhealthy
using the oracle data. They announce that they want to purchase exclusive
priority for updating the price feeds and pay X in return. They win the auction,
pay X to the dApp, and execute the price feed updates and liquidation
atomically.
The concept of OEV is not limited to liquidations but can occur anywhere where
price feed updates potentially expose profitable opportunities, such as
arbitrage and many others.
## OEV distribution
To update the data feed, the auction winner must pay the bid amount they
announced during the auction. These payments constitute auction proceeds. Majority of these proceeds go back to the dApp in the form of [OEV Rewards](/dapps/oev-rewards/). Searchers, in return, get exclusive rights to capture
the OEV and get to capitalize on the remaining revenue of the opportunity.
Thus, the majority of the OEV is distributed to the dApp and the
searchers.
## How do auctions work?
Api3 uses a combination of the [OEV Network](#oev-network) and the
[OEV Auctioneer](#oev-auctioneer) to power the OEV auctions in a secure and
transparent way.
At a high level, auctions repeat continuously and indefinitely. There is a
separate auction for each dApp and each auction takes a fixed amount of time. Each time an auction ends, a new one begins and the same process
repeats.
### Bid phase
Auctions run in two phases - the bid phase and the award phase. During the
bid phase, searchers look for OEV opportunities for the particular dApp
by monitoring the off-chain data. When an opportunity is detected, they
place their bid based on its value.
It is important to understand that bids must be placed before the end of the bid phase, which establishes a cutoff period. The auction winner is able to use only price feed data with timestamp up to this cutoff period.
### Award phase
The award phase starts immediately after the end of the bid phase. All bids placed during the bid phase are evaluated and the eligible bid with highest amount is selected as winner and provided a cryptographic signature allowing them to make the price feed updates up to the cutoff period. This signature is usable only by the auction winner. A requirement for updating the price feed is paying the announced bid amount in the same transaction.
### Fulfillment
Auction winner is required to make use of the auction data and pay for the winning bid to fulfill the purpose of the auction. After paying for the auction, they are required to report the fulfillment to the auction platform with the transaction hash of the update.
The fulfillment is verified and provided the update was correct, the auction winner's collateral is released. If the fulfillment is not reported or incorrect information is submitted, the collateral is slashed. Fulfillment has a large reporting period and the auction winner is in full control of when they choose to report.
### OEV Network
The OEV Network hosts auctions transparently, ensuring that any disputes can be
resolved by analyzing the on-chain data. Searchers submit their bids on-chain,
where the winner is announced and given exclusive rights to execute the oracle
update.
### OEV Auctioneer
A key component of OEV is an off-chain auction system that processes the
auctions taking place on the OEV network. We call this system OEV Auctioneer,
and it is managed by the Api3 DAO. The integrity of OEV Auctioneer is ensured by
using the OEV Network for all important actions, such as announcing the auction
winner.
## Get started with OEV
Here are resources to help you get started with OEV:
1. Dive deeper into OEV by reading the
[OEV Litepaper](https://raw.githubusercontent.com/api3dao/oev-litepaper/main/oev-litepaper.pdf).
2. Check out the [Getting started](/oev-searchers/in-depth/) section to see how
to start searching.
3. Connect with other developers and OEV enthusiasts in our
[OEV Discord channel](https://discord.com/channels/758003776174030948/1062909222347603989).
4. Follow Api3 on [X](https://x.com/api3dao) for the latest news and updates on
OEV.
# Getting started (OEV Searchers → In Depth)
This is a good starting place for searchers. It includes the list of good dApp candidates for searching, details how OEV auctions
work and explains basic OEV searching strategy to simplify the onboarding of
existing MEV searchers to OEV.
## dApps catalog
Api3 feeds are used across many dApps, but not all are suitable for OEV searching. This catalog includes those dApps that are generating significant OEV amounts and are open for searchers to participate.
### OEV dApps
The following table includes dApps which integrated OEV proxies and are good candidates for OEV searching. The chain and dApp alias define are unique for every market and are required when implementing searcher bots.
| dApp | Chain | dApp alias |
| -------------------------------------------------------------------------------------------------------------------------------------------------- | -------------- | ----------------------------- |
| [dTRINITY](https://dtrinity.org/) | Fraxtal, Sonic | `dtrinity` |
| [INIT Capital](https://app.init.capital/?chain=5000) | Mantle | `init` |
| [Lendle](https://lendle.xyz/) | Mantle | `lendle` |
| [MachFi](https://www.machfi.xyz/) | Sonic | `mach-finance` |
| [Moonwell](https://moonwell.fi/) | Moonbeam | `moonwell` |
| [Morpho cbBTC/USDC market](https://app.morpho.org/ethereum/market/0xba3ba077d9c838696b76e29a394ae9f0d1517a372e30fd9a0fc19c516fb4c5a7/cbbtc-usdc) | Ethereum | `morpho-cbbtc-usdc-860-lltv` |
| [Morpho MVL/USDC market](https://app.morpho.org/ethereum/market/0x972b343b611a3cf2559a04bf2c0b8e45d1c69a1c1d94dc852ca6e16a924b006b/mvl-usdc) | Ethereum | `morpho-mvl-usdc-770-lltv` |
| [Morpho wbtc/USDC market](https://app.morpho.org/ethereum/market/0x704e020b95cbf452e7a30545d5f72a241c4238eebf9d1c67657fdd4a488581e0/wbtc-usdc) | Ethereum | `morpho-wbtc-usdc-860-lltv` |
| [Morpho wstETH/USDC market](https://app.morpho.org/ethereum/market/0x6d2fba32b8649d92432d036c16aa80779034b7469b63abc259b17678857f31c2/wsteth-usdc) | Ethereum | `morpho-wsteth-usdc-860-lltv` |
| [Nerite](https://www.nerite.org/) | Arbitrum | `nerite` |
| [Stout](https://stout.fi/) | Sonic | `stout` |
| [Stability](https://stability.market/) | Sonic | `stability` |
| [Takara](https://app.takaralend.com/) | Sei | `takara` |
| [Yei Finance](https://www.yei.finance/) | Sei | `yei` |
### Legacy integrations
Some dApps are still using the legacy design of oracle proxies, which do not support OEV auctions. It's expected these will migrate the OEV supported ones soon. That said, searchers can already
perform [MEV with Signed APIs](/oev-searchers/in-depth/mev-with-signed-apis)
extraction.
| dApp | Chain |
| --------------------------------------------------------------------------------------------------- | -------- |
| [Compound Finance USDe market](https://app.compound.finance/markets/usde-mantle) | Mantle |
| [Hana Finance](https://www.hana.finance/) | Taiko |
| [INIT Capital](https://app.init.capital/?chain=81457) | Blast |
| [Orbit Protocol](https://orbitlending.io/) | Blast |
| [Silo Finance RDNT market](https://v1.silo.finance/silo/0x19d3F8D09773065867e9fD11716229e73481c55A) | Ethereum |
| [TakoTako](https://www.takotako.xyz/) | Taiko |
## From MEV searching
MEV searching has a well-established community and expertise in securing health
and stability across many dApps and chains. We want to motivate this community
to join OEV searching by outlining the steps to transition from MEV to OEV.
OEV can be considered an extension of MEV that searchers can capitalize on. While traditional searching prioritizes low latency, OEV searchers can secure guaranteed profits through exclusive priority access for updating data feeds.
The following is a short list of requirements to upgrade an existing MEV
bot to participate in OEV searching:
1. Bridge funds to the OEV network
2. Deposit funds to the OevAuctionHouse contract
3. Monitor off-chain signed data for data feeds used by the dApp
4. Simulate the data feed update on-chain to determine OEV opportunities
5. Place a bid on the OEV Network
6. Wait for the auction to end
7. Provided the auction is won, use the award to update the on-chain data feed on target chain and capture
the OEV
Most of these steps require small additions to the existing MEV bot, but it is
required to understand the mental model behind OEV and our
[data feeds](/oev-searchers/in-depth/data-feeds/). Because of this, we recommend starting
with an in-between solution we call
[MEV with Signed APIs](/oev-searchers/in-depth/mev-with-signed-apis).
## OEV Dashboard
To see OEV in more detail, head out to the [OEV Dashboard](https://oev-dashboard.api3.org/) where you can see how much is leaked and recaptured across different dApps.
# Data feeds (OEV Searchers → In Depth)
Searchers need a way to monitor real-time off-chain prices to find profitable
opportunities. Traditionally, searchers have needed to buy API subscriptions
from underlying oracle sources, creating additional friction in the process.
Api3 simplifies this process by providing the same data that is used for
updating data feeds to searchers publicly and without cost.
## How data feeds work?
Let's start from the ground up. The data feed logic is dictated by the Api3ServerV1
contract. The central part of Api3 feeds is first-party oracles, relying on
cryptographic signatures verified on-chain.
Internally, we refer to our data feeds as dAPIs. This is also the terminology used across Api3 contracts. We'll be following the same terminology in this section.
### dAPI structure
The simplest primitive is a "beacon".
It consists of:
- Airnode address = The identifier of the data provider, e.g. Nodary.
- Template ID = A template represents an endpoint of the data provider API, e.g.
ETH/USD.
Together they represent a data point from a specific data provider. Beacons are
identified by beacon ID, which is the hash of their Airnode address and template ID. They can be
aggregated into "beacon sets" which are identified by the IDs of the constituent
beacons.
Both beacons and beacon sets can be used as price feeds. We use the term "data feed" to refer to
either of them. Term data feed ID is a common name for beacon ID or beacon set ID. Finally, a dAPI is simply a mapping from a human-readable data feed name
to a data feed ID.
### Updating data feed value
So far, we've referred to dAPIs and data feeds as sources of data, not
mentioning how they are kept up-to-date. Api3 feeds are permissionless and
anyone can perform an update, provided they have valid data. To update a beacon,
anyone can call `updateBeaconWithSignedData` on the Api3ServerV1 contract:
```solidity
function updateBeaconWithSignedData(
address airnode, // Airnode address of the beacon to update
bytes32 templateId, // Template ID of the beacon to update
uint256 timestamp, // The off-chain timestamp of the data
bytes calldata data, // The encoded price feed data (decoded as int256 internally)
bytes calldata signature // The signature of the fields above signed by the airnode wallet (owned by the data provider).
) external returns (bytes32 beaconId); // The beacon ID that was updated
```
Similarly, to update a beacon set, one first updates the constituent beacons and
then calls `updateBeaconSetWithBeacons`:
```solidity
function updateBeaconSetWithBeacons(
bytes32[] memory beaconIds // The beacon IDs of the constituent beacons
) external returns (bytes32 beaconSetId); // The beacon set ID that was updated
```
By updating a beacon set, we aggregate the values and timestamps of the
constituent beacons. The typical dAPI refers to a beacon set of multiple
sources. As dAPI references a particular data feed under the hood, modifying the underlying beacon set also updates the dAPI value.
### Off-chain components
There are several off-chain components that are used to keep dAPI values up to
date:
1. [Airnode feed](https://github.com/api3dao/signed-api/tree/main/packages/airnode-feed) -
Deployed by the data providers themselves, Airnode feed continuously queries
the API of the data provider, processes the response, and signs it with the
Airnode wallet. The cryptographically signed data is then pushed to Signed
APIs.
2. [Signed API](https://github.com/api3dao/signed-api/tree/main/packages/signed-api) -
Signed API accepts signed data from API providers and provides an API layer
for off-chain querying.
3. [Airseeker](https://github.com/api3dao/airseeker) - Airseeker is an Api3 push
oracle. It monitors the off-chain and on-chain data and triggers an update
when needed.
All of these tools are open-sourced for transparency.
### Update schedule
dAPIs are updated based on the configured update parameters. An update is
performed whenever a dAPI value exceeds the allowed threshold or the feed was
not updated for a particular amount time.
## OEV updates
After a dAPI is updated, the changed value is reflected across all protocols
that use the particular dAPI. We call these "base feed updates" to differentiate
them from the OEV updates, which are dApp-specific. These are made by the
searchers who win auctions on the OEV Network.
For dApps, OEV is an extension to base feeds, supported by the Api3ReaderProxyV1
contract. This contract is a simple proxy that reads the value either from the
base feed or the OEV feed, whichever is fresher.
The OEV feed is specific to the dApp, and its update only reflects the price for
the dApp that uses this proxy. This is accomplished by the proxy being tied to an
immutable dApp ID field. This allows separate auctions to be held for each dApp.
To guarantee searchers' exclusivity privilege to capture OEV, the base feed
updates are delayed. Searchers bid for real-time data that can be used to update
the OEV feed. By winning an auction, a searcher is guaranteed that the data is
fresher than the base feed.
::: info ℹ️ Info
Base feed delay is accomplished by Signed APIs being configured to serve the signed data with 60s delay and provide a real-time version of this data, usable only for the auction winner. This design choice guarantees searchers the exclusive rights for data feeds update.
:::
### OEV feed
The OEV feed is derived from the base feed by changing its beacons to OEV
beacons.
An OEV beacon is derived from the base feed beacon by hashing its template ID
using `keccak256`. This makes it possible to share the signed data for OEV
beacons freely, because they cannot be used to update the base feed. The
Api3ServerV1OevExtension contract allows them to be used only by the auction
winner who has paid the adequate amount.
::: info ℹ️ Example
Say we have the following base feed beacon:
```jsonc
"0xfe395743aff41835420d109be4bf98b93e9d9670f5539fc6392578b4626ecedf": { // Beacon ID
"airnode": "0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4",
"templateId": "0x1bb9efc88ac9d910a9edc28e8cad8959d196a551e15c9af3af21247f1605873f",
}
```
To derive the template ID of the OEV beacon, we hash its template ID:
```solidity
keccak256(abi.encodePacked(bytes32(0x1bb9efc88ac9d910a9edc28e8cad8959d196a551e15c9af3af21247f1605873f)))
// Output: 0xbc7896315bfd4b1186a05f219ec71a95def0d038617e7ae534075317866bfd1b
```
Which gives us the following OEV beacon:
```json
"0x154ca7c81eb1ed9ce151d5b6ad894c5ab79d19bee20d89eb061aaf24f788221f": { // Beacon ID
"airnode": "0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4",
"templateId": "0xbc7896315bfd4b1186a05f219ec71a95def0d038617e7ae534075317866bfd1b",
}
```
Notice that the beacon ID is different, but the Airnode address is the same.
:::
### dApp IDs
Each dApp that uses OEV feeds is assigned a unique ID, called the "dApp ID". The
granularity of auctions is at the dApp level, meaning the auction winner is able
to update any of the price feeds associated with this dApp ID. This ID is
hardcoded in the OEV proxies of the dApp.
The ID has no meaning other than to group proxies of the same dApp together.
Searchers can derive the dApp ID from the information provided in the
[OEV dApps catalog](/oev-searchers/in-depth/#oev-dapps) in two ways.
#### Programmatically
Searchers can use [unsafeComputeDappId](https://github.com/api3dao/contracts/blob/52109d0d285d3ac485a2f0ed68bd7799e75a9722/src/proxy.ts#L57) from the `@api3/contracts` package available as an NPM package.
::: info ℹ️ Example
Say we want to determine dApp ID for [dTRINITY](https://dtrinity.org/). From the OEV dapps catalog, we see the dApp alias is `dtrinity` and the chain is Fraxtal. Fraxtal has chain ID `252`. To derive the dApp ID we call `unsafeComputeDappId` with arguments `dtrinity` and `252`.
```js
const dTrinityDappId = unsafeComputeDappId('dtrinity', 252);
// 16210721173577624589952893185091679941657223823840386808143855919126917477566
```
:::
#### Using the CLI
Alternatively, searchers can use the `compute-dapp-id` command from the `@api3/contracts` package.
::: info ℹ️ Example
For the same example as above, run the following command:
```sh
npx @api3/contracts@latest compute-dapp-id \
--dapp-alias dtrinity \
--chain-id 252
```
The command will output:
```
dApp alias: dtrinity
chain: Fraxtal
• dApp ID: 16210721173577624589952893185091679941657223823840386808143855919126917477566
```
:::
### dApp sources
Searchers need to know the proxy address and the underlying dAPI name used by
the OEV proxy. The dApps have full control over what proxies they use, and searchers
should refer to their documentation or inspect the chain-state of their contracts to get an
up-to-date proxy address.
To determine the underlying beacons used by the dAPI, you can use the
AirseekerRegistry contract on the target chain. Searchers need to monitor values for these beacons with the public Signed APIs. Note
that these are the base feed beacons and the searcher is expected to derive
the OEV beacons to monitor the OEV data.
#### Data feed details encoding
The AirseekerRegistry contract uses a particular encoding for data feed details
so that the details can be persisted on-chain as a single `bytes` value. The
callers need to decode this value to get the actual data feed details.
Decoding depends on whether the feed is a beacon or a beacon set. Assume we have
encoded `dataFeedDetails` and we need to decode it. Note that the encoding
follows a similar principle.
The decoding of a beacon:
```solidity
(address airnode, bytes32 templateId) = abi.decode(
dataFeedDetails,
(address, bytes32)
);
```
The decoding of a beacon set:
```solidity
(address[] memory airnodes, bytes32[] memory templateIds) = abi.decode(
dataFeedDetails,
(address[], bytes32[])
);
```
To know which encoding to use, you can check the length of the
`dataFeedDetails`. For a single beacon, the length is always `64` bytes, because
both `address` and `bytes32` are encoded using 32 bytes. For a beacon set, the
length depends on the number of beacons encoded.
::: warning ⚠️ Airnode Mnemonic Rotation
Data providers rotate their Airnode mnemonics every 6 months as part of Api3's security practices. This results in new Airnode addresses being generated. Please refer to the [Api3 Market](https://market.api3.org) or directly on-chain data for the latest Airnode addresses.
:::
::: info ℹ️ Example
Say there is a dApp proxy that uses the `ETH/USD` dAPI. We can compute the
details for this dAPI off-chain by:
```js
const encodedDapiName = ethers.utils.formatBytes32String('ETH/USD'); // 0x4554482f55534400000000000000000000000000000000000000000000000000
const encodedDapiNameHash = ethers.utils.keccak256(encodedDapiName); // 0x9e6138f8f57d7b493a8364edb0a0ac92399dfd890eecb9121050836a1749ba42
```
To determine the data feed ID for this dAPI, we can use the
`dapiNameHashToDataFeedId` function on the Api3ServerV1 contract:
```js
const api3ServerV1 = new ethers.Contract(
api3ServerV1Address,
api3ServerV1Abi,
provider
);
const dataFeedId =
await api3ServerV1.dapiNameHashToDataFeedId(encodedDapiNameHash); // e.g. 0x28d7af9ef50bde705ccabb77f27cfa481b998a4a01eaae22825835f611bf7ffe
```
To determine the data feed details, use the `dataFeedIdToDetails` function on
the AirseekerRegistry contract:
```js
const airseekerRegistry = new ethers.Contract(
airseekerRegistryAddress,
airseekerRegistryAbi,
provider
);
const dataFeedDetails = await airseekerRegistry.dataFeedIdToDetails(dataFeedId);
```
The data feed details need to be decoded first. The following is a simplified
version of that decodes the data off-chain:
```js
const deriveBeaconId = (airnodeAddress, templateId) => {
return ethers.utils.solidityKeccak256(
['address', 'bytes32'],
[airnodeAddress, templateId]
);
};
const decodeDataFeedDetails = (dataFeed) => {
// The contract returns empty bytes if the data feed is not registered.
if (dataFeed === '0x') return null;
// This is a hex encoded string, the contract works with bytes directly
// 2 characters for the '0x' preamble + 32 * 2 hexadecimals for 32 bytes + 32 * 2 hexadecimals for 32 bytes
if (dataFeed.length === 2 + 32 * 2 + 32 * 2) {
const [airnodeAddress, templateId] = ethers.utils.defaultAbiCoder.decode(
['address', 'bytes32'],
dataFeed
);
const dataFeedId = deriveBeaconId(airnodeAddress, templateId);
return [{ beaconId: dataFeedId, airnodeAddress, templateId }];
}
const [airnodeAddresses, templateIds] = ethers.utils.defaultAbiCoder.decode(
['address[]', 'bytes32[]'],
dataFeed
);
const beacons = airnodeAddresses.map((airnodeAddress, idx) => {
const templateId = templateIds[idx];
const beaconId = deriveBeaconId(airnodeAddress, templateId);
return { beaconId, airnodeAddress, templateId };
});
return beacons;
};
```
Say the following is the output after decoding the data feed details:
```json
[
{
"beaconId": "0x853a5cc0a517489779025cc8a48e771461a0616665efd6a61424e57997e6dbed",
"airnodeAddress": "0xC9B494D3c6eA3fD42779Df9A136Db10374c98D80",
"templateId": "0x3bdd99217e0be6a0c7812aad3138bd941c2eaf60410740cac7d716d1c5e05558"
},
{
"beaconId": "0xefec8dab2bc20fcc03141d6e521148564e548046d291e116d02581aea7407533",
"airnodeAddress": "0x6b56E47DccFbC82D63Df3da417d26e8B1B877f0f",
"templateId": "0xdeda2f7938bf877d2f011aa550852d3459794e16944ea0b7513465479752ba93"
},
{
"beaconId": "0x00be0673ee8afc9a25fc12edddb7fbe293a7da8f04953171243b594c257141d7",
"airnodeAddress": "0xa924847354c551C79BAE7E75529364bA0449e51A",
"templateId": "0x9f66583540b490e11ee1b40c7b561946eceb96273489c95328c0cd290060129b"
},
{
"beaconId": "0x83a32cce0fc108005ffb0f745f58f1f730770a361a3f051fd058357d525a2182",
"airnodeAddress": "0x5791Fb78D4e37A9D0f0003199D1AE1A8C04C8d89",
"templateId": "0x0970b1e622f50950bf55b3375a849cdd8f8ecbb0ff47d4bde3cbfb225dfcc607"
},
{
"beaconId": "0x752bb8fa00e8c35657a8414884ad4ab976a56fa7d015eb7ade1d60eb15e2a895",
"airnodeAddress": "0xbC6471E88d8aFe936A45bEB8bd20a210EBEF6822",
"templateId": "0xb501fe47e4ad40fd34f5e5a685e79b991b51e2c887d2dbe35bc645ed1f390241"
},
{
"beaconId": "0x4385954e058fbe6b6a744f32a4f89d67aad099f8fb8b23e7ea8dd366ae88151d",
"airnodeAddress": "0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4",
"templateId": "0x154c34adf151cf4d91b7abe7eb6dcd193104ef2a29738ddc88020a58d6cf6183"
},
{
"beaconId": "0xf580f27c696b05c8572266e6db5cb5b12a562cac5dfb2e7c240a5ef7d845aebf",
"airnodeAddress": "0x31C7db0e12e002E071ca0FF243ec4788a8AD189F",
"templateId": "0x046e65143918e48adc0a77bada55931622531819be4a7473d80b7f906b813105"
}
]
```
:::
## Public Signed APIs
Signed APIs store the data pushed by Airnode feeds and expose them to the public
via an API. As mentioned, base feed updates are delayed, permissionless and can be
updated by anyone. The OEV feeds are real-time and can only be updated by the OEV auction winner.
Api3 runs Signed APIs and makes them publicly available. They are deployed on
AWS, ensuring maximum uptime and reliability.
Signed APIs only support querying data for a particular Airnode feed at a time. The
Airnode address is supplied as an HTTP path parameter. The endpoint is cached
and can be called repeatedly. However, excessive call frequency is restricted by
rate limiting or full access denial.
### Base feed endpoints
The following are the base feed endpoints that are publicly available:
1. `https://signed-api.api3.org/public/` - The official Api3
Signed APIs used by the push oracle to update the base feeds.
For example, see the
[Api3 response for Nodary Airnode feed](https://signed-api.api3.org/public/0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4).
### OEV endpoints
The following are the OEV endpoints that are publicly available:
1. `https://signed-api.api3.org/public-oev/`
For example, see the
[Api3 response for Nodary Airnode feed](https://signed-api.api3.org/public-oev/0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4).
### Response
The response of the Signed API is a JSON object with the following fields:
1. `count` - The number of signed data entries.
2. `data` - An object with the signed data entries. The keys are the beacon IDs
and the values are the signed data objects for the particular beacon(s).
For example:
```json
{
"count": 2,
"data": {
"0xcdaf3ecba9e3f1457b64b1dd33dd6dbd5d3a0d43dbcb6b94fbf755ca8a64f1c2": {
"airnode": "0x31C7db0e12e002E071ca0FF243ec4788a8AD189F",
"encodedValue": "0x0000000000000000000000000000000000000000000000000f710eec75e16680",
"signature": "0x5d382d6636f6b87642db580586bac7f57609f47d30e133dbb6bedede233a6d58065cb4aefbe2d2db1bd61ee9734a8671c05a5f2f79a0192ef491662ba3e390ac1c",
"templateId": "0x174bd80b61ec8451784391df43c8c4ffc4ae82216a65cc15107bfdf4c29f6ca1",
"timestamp": "1727085105"
},
"0x4048c53a7e6d4b857fb04bd4f496691e526f1de8f38880469ec834bc46021cd4": {
"airnode": "0x31C7db0e12e002E071ca0FF243ec4788a8AD189F",
"encodedValue": "0x0000000000000000000000000000000000000000000000000210a4cfc6940000",
"signature": "0x00b84c978f9bab8639a8931990aede93ce34b8f9564ced755499bac503a39d7e7dad882dd1be77954bbbf152b436912204a29a1260283dda863cf489f631a17b1c",
"templateId": "0xee8d0cab5281c59547d4ae9021121df9aec759d457c51b905296610fbef58bed",
"timestamp": "1727085103"
}
}
}
```
# MEV with Signed APIs (OEV Searchers → In Depth)
An intermediate step towards OEV searching is to extend MEV bots to utilize the
public
[base feed endpoints](/oev-searchers/in-depth/data-feeds/#base-feed-endpoints). This data is delayed compared to data in the [OEV endpoints](/oev-searchers/in-depth/data-feeds/#oev-endpoints), so it's expected searchers won't be able to extract much value this way. That said, it is a good backup in case OEV auctions are paused or encountering unexpected technical issues. It also the only way to extract value for [legacy integrations](/oev-searchers/in-depth/#legacy-integrations).
The existing MEV bot can utilize the off-chain open-source data and make a base
feed update on-chain whenever there is OEV to be captured. Refer to
[updating data feed value](/oev-searchers/in-depth/data-feeds/#updating-data-feed-value)
section for more details.
One advantage of using this data is that searchers can easily simulate the data
feed update (which is permissionless for base feeds) to determine OEV
opportunities more easily. This is a direct improvement over monitoring data
source values and predicting the next oracle update.
::: info 💰 Financial
This improvement on its own provides a major competitive advantage over existing MEV competition and leads to a significant increase in profits. That said, this strategy will not work when the competition is doing OEV searching.
:::
## Monitor signed data
First, searchers need to have a list of data feeds used by the dApp and
[obtain its beacons](/oev-searchers/in-depth/data-feeds/#dapp-sources). Note that
this can be cached because beacons change only when the
underlying base feed dAPI changes, which happens rarely, only when a dAPI is reconfigured.
Once the list of base feed beacons is known, searchers should periodically call
the public
[base feed endpoints](/oev-searchers/in-depth/data-feeds/#base-feed-endpoints) to get
the real-time values for the base feed beacons used by the dApp. This data may
be used immediately to look for OEV opportunities.
## Simulate a data feed update
Assuming a searcher called the Signed APIs and has valid data to update the base
feed, they can use them to simulate the data feed update on-chain followed by a
call to check for OEV opportunities.
The code below demonstrates how this process can be implemented in JavaScript
with the `ethers` library for an imaginary liquidation protocol. Note that this
code makes use of variables that are not defined in the context of the code snippet for brevity. Their
purpose can be understood from the context. The snippet makes use of a well-known [Multicall3 contract](https://www.multicall3.com/).
```javascript
const beaconsIds = []; // Assume the data feed is a beacon set with these beacons
const dataFeedSignedData = []; // Assume we have some signed data to update
// 1. Create the calldata for updating the base feed beacons
const dataFeedUpdateCalldata = dataFeedSignedData.map((signedData) => ({
target: api3ServerAddress,
allowFailure: true,
callData: api3ServerV1.interface.encodeFunctionData(
'updateBeaconWithSignedData',
[airnode, templateId, timestamp, encodedValue, signature]
),
}));
dataFeedUpdateCalldata.push({
target: api3ServerAddress,
allowFailure: true,
callData: api3ServerV1.interface.encodeFunctionData(
'updateBeaconSetWithBeacons',
[beaconsIds.map((beaconsId) => beaconId)]
),
});
// 2. Create calldata to simulate liquidation opportunity
const liquidationOpportunityCalldata = {
target: liquidatorContract,
callData: liquidatorContract.interface.encodeFunctionData('liquidate', [
borrower,
]),
allowFailure: false,
};
// 3. Merge the calldata for updating the feeds and capturing the liquidation
const calldata = [...dataFeedUpdateCalldata, liquidationOpportunityCalldata];
// 4. Execute the staticcall multicall, using a standard Multicall3 contract
const result = await multicall3.aggregate3.staticCall(calls);
```
## Capture MEV
If a searcher successfully simulates a profitable MEV opportunity, they can use
the same data feed calldata and submit the transaction instead of using a
staticcall.
Note that the signed data for base feeds is delayed to ensure OEV searchers have
exclusive priority for OEV extraction.
## Reference implementation
- [Example OEV Compound bot](https://github.com/api3dao/oev-v1-compound-bot/tree/mev-with-signed-apis) - You can also inspect the
[changes](https://github.com/api3dao/oev-v1-compound-bot/compare/mev...mev-with-signed-apis)
needed to add the MEV with Signed APIs functionality to an existing MEV bot.
# OEV Network (OEV Searchers → In Depth)

The OEV Network operates as a standard Arbitrum Nitro L2 optimistic-rollup. The
system ensures transparency and allows verification of the auction process. It allows searchers to place bids for the exclusive opportunity to
update a dApp's data feeds for a short period of time.
By hosting auctions on-chain, we address two big issues:
1. Scalability - The OEV network hosts auctions across different dApps across
multiple chains. The system needs to scale up with demand, especially during
periods of market volatility where the activity is the highest. This is a
long-solved problem in blockchains through gas fees.
2. Transparency - Auctions are awarded via an off-chain system, called OEV
Auctioneer, so it is important to be able to reason about the correctness of
auction outcomes. Blockchains are the perfect tool for this, as all the transactions and data
is public and verifiable.
To participate in auctions, searchers need to have a sufficient amount of ETH
bridged to the OEV network and interact with the
[OevAuctionHouse](#oevauctionhouse) contract.
## Using the OEV Network
The OEV Network can be added as a custom network to any EVM compatible wallet.
| Details | Value |
| ------------------ | ------------------------------ |
| Network | OEV Network |
| Chain ID | 4913 |
| RPC URL (HTTP) | https://oev.rpc.api3.org/http |
| RPC URL (WS) | https://oev.rpc.api3.org/ws |
| Symbol | ETH |
| Block Explorer URL | https://oev.explorer.api3.org/ |
| Bridge URL | https://oev.bridge.api3.org/ |
## Properties
Here are some of the key properties of the OEV Network:
1. Block time - Under high load, the block time of the network can be as fast as
250ms. Note that the OEV Network only produces blocks when there are
transactions.
2. Gas fees - The gas fees are paid in ETH, and because the network is an
optimistic L2 rollup, the gas fees are low and get cheaper with the increased number of transactions.
3. Using Anytrust - By using AnyTrust DAC, the OEV Network achieves further cost
reduction.
See [Arbitrum Nitro details](https://docs.caldera.xyz/about/nitro#benefits) and
[AnyTrust details](https://docs.caldera.xyz/about/nitro#anytrust) for more
information.
## Bridging ETH
Use the [OEV Network bridge](https://oev.bridge.api3.org/) to bridge your ETH to
the OEV Network. Bridging is only possible from ETH mainnet.

Clicking on `Transfer Tokens` will automatically add the OEV Network to your
Metamask wallet. To bridge, confirm the transaction in your wallet and wait for
confirmation. After the transaction is confirmed, you should see your ETH on the
OEV Network.
## Api3ServerV1
The implementation of the audited Api3ServerV1 contract is publicly available
[here](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1.sol).
The Api3ServerV1 contract powers data feeds on the OEV Network, which are used in the
OevAuctionHouse contract to compute collateral and protocol fee from the bid
amount. Note that this chain is not officially listed on the Api3 market because
the OEV Network is primarily intended to be used for OEV auctions.
## OevAuctionHouse
The implementation of the audited OevAuctionHouse contract is publicly available
[here](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/OevAuctionHouse.sol).
The OevAuctionHouse contract is a general purpose auction platform designed to
work together with an off-chain component (OEV Auctioneer) that auctions off
data in a transparent and retrospectively verifiable manner.
To support OEV auctions in the least privileged way, the contract defines a few
special roles allowed to interact with the contract in an authorized way. These
roles are:
1. Proxy setter - This role allows the caller to change the dAPI proxy used for
collateral and protocol fee conversion to the native OEV network currency.
2. Auctioneer - This role allows the caller to award winning bids, and confirm
or contradict fulfillments.
3. Withdrawer - This role allows the caller to withdraw the protocol fee and
slashed amount collected by the contract.
The interactions with this contract include:
1. Searchers depositing collateral
2. Searchers withdrawing collateral
3. Searchers placing or expediting bids
4. Auctioneer resolving auction winner
5. Auctioneer confirming or contradicting fulfillment
6. Api3 DAO updating protocol and collateral fee
7. Api3 DAO updating the price feed proxies
Tech-savvy users can refer to the contract's source for details.
### Depositing collateral
To be eligible to win OEV auctions, searchers need to have enough collateral
deposited in the OevAuctionHouse contract. See
[bid eligibility](/oev-searchers/in-depth/oev-auctioneer#bid-eligibility)
section for more details.
We recommend using the same hot wallet for the bot on the OEV network (to
participate in auctions) and the target chain (to capture the OEV). To deposit
funds, you can use either the `deposit` or `depositForBidder` functions. The
latter allows you to deposit the collateral on behalf of another address. Both
functions are `payable` and expect a non-zero amount to be sent with the
transaction.
```solidity
function deposit() external payable
returns (uint256 bidderBalance); // The bidder balance that was deposited
```
```solidity
function depositForBidder(
address bidder // The address of the bidder to deposit on behalf of
) external payable returns (uint256 bidderBalance); // The bidder balance that was deposited
```
For an advanced usage where the bidder is a contract, refer to
[bidding contract](/oev-searchers/in-depth/oev-searching#bidding-contract)
section.
### Withdrawing collateral
Withdrawal of deposited collateral is implemented as a two-way process to
prevent denial of service by frontrunning the award transaction by withdrawing
the collateral.
To withdraw the deposited collateral from the OevAuctionHouse contract, the
searcher needs to do the following:
1. Call the `initiateWithdrawal` function on the OevAuctionHouse contract.
```solidity
function initiateWithdrawal()
external
returns (uint256 earliestWithdrawalTimestamp); // The timestamp after which the withdrawal can be performed
```
2. Wait for the withdrawal period to pass. The period is 15 seconds.
3. Call the `withdraw` function on the OevAuctionHouse contract. You need to
specify the recipient and amount to be withdrawn.
```solidity
function withdraw(
address payable recipient, // The address of the recipient of the withdrawal
uint256 amount // The amount to be withdrawn
) external;
```
### Collateral and protocol fee
For a searcher to win an auction, they are required to have enough ETH deposited
in the OevAuctionHouse contract. Refer to
[bid eligibility](/oev-searchers/in-depth/oev-auctioneer#bid-eligibility)
section for details.
The collateral and protocol fee rates are configurable parameters within the
OevAuctionHouse contract and are configured by the Api3 DAO. These values are
set in "basis points", which are 1/100th of a percentage point. For example, a
value of 1000 is equivalent to 10%. The current values are set to the following:
| Parameter | Value |
| ------------------------ | ----- |
| collateralInBasisPoints | 1000 |
| protocolFeeInBasisPoints | 0 |
The collateral and protocol fee are calculated using the price feed values at
the time of the bid placement. However, the collateral is reserved at award
time. This allows the bidder to place multiple bids for different dApps, even if
their collateral doesn't allow them to win all of them, providing greater
flexibility.
If the auction winner pays for the bid on the OEV Network and reports the
fulfillment, their collateral is released and the protocol fee is deducted. If
the auction winner doesn't pay for the award or fails to report the fulfillment,
their collateral is slashed.
### Placing a bid
There are two ways to place a bid, either by calling `placeBidWithExpiration` or
by `placeBid`. The latter is merely a convenience function that places a bid
with maximum expiration timestamp.
```solidity
function placeBidWithExpiration(
bytes32 bidTopic, // The bid topic is an identifier of the auction
uint256 chainId, // The chain ID of the dApp
uint256 bidAmount, // The amount the searcher is willing to pay for the auction
bytes calldata bidDetails, // Bid details according to the off-chain system specification
uint256 maxCollateralAmount, // The maximum collateral amount the searcher is willing to pay
uint256 maxProtocolFeeAmount, // The maximum protocol fee amount the searcher is willing to pay
uint32 expirationTimestamp // The expiration timestamp of the bid
) external returns (
uint104 collateralAmount, // The collateral amount required for the bid
uint104 protocolFeeAmount // The protocol fee required for the bid
);
```
Or the similar `placeBid` function:
```solidity
function placeBid(
bytes32 bidTopic, // The bid topic is an identifier of the auction
uint256 chainId, // The chain ID of the dApp
uint256 bidAmount, // The amount the searcher is willing to pay for the auction
bytes calldata bidDetails, // Bid details according to the off-chain system specification
uint256 maxCollateralAmount, // The maximum collateral amount the searcher is willing to pay
uint256 maxProtocolFeeAmount // The maximum protocol fee amount the searcher is willing to pay
)
external
returns (
uint32 expirationTimestamp, // The timestamp after which the bid becomes expired
uint104 collateralAmount, // The collateral amount required for the bid
uint104 protocolFeeAmount // The protocol fee required for the bid
);
```
The OevAuctionHouse contract is designed in a generic way. To fully understand
how to use these functions, we need to understand how
[OEV Auctioneer](/oev-searchers/in-depth/oev-auctioneer) works. Refer to
[placing a bid](/oev-searchers/in-depth/oev-searching#placing-a-bid) section for
more details.
### Expediting a bid
Once a bid is placed, it can no longer be cancelled - only expedited. This is to
prevent griefing by repeatedly placing a bid and cancelling it shortly after.
The contract defines a constant `MINIMUM_BID_LIFETIME` set to 15 seconds.
Searchers can use `expediteBidExpiration` or `expediteBidExpirationMaximally`
for this purpose.
```solidity
function expediteBidExpiration(
bytes32 bidTopic, // The bid topic is an identifier of the auction
bytes32 bidDetailsHash, // The keccak256 hash of the bid details
uint32 expirationTimestamp // The new expiration timestamp of the bid
) external;
```
or
```solidity
function expediteBidExpirationMaximally(
bytes32 bidTopic, // The bid topic is an identifier of the auction
bytes32 bidDetailsHash // The keccak256 hash of the bid details
) external returns (uint32 expirationTimestamp); // The updated expiration timestamp of the bid
```
The OevAuctionHouse contract is designed in a generic way. To fully understand
how to use these functions, we need to understand how
[OEV Auctioneer](/oev-searchers/in-depth/oev-auctioneer) works. Refer to the
[Expediting a bid](/oev-searchers/in-depth/oev-searching#expediting-a-bid)
section for more details.
## Deployed contracts
These are the relevant contracts deployed on the OEV Network:
| Contract name | Address |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Api3ServerV1 | [0x709944a48cAf83535e43471680fDA4905FB3920a](https://oev.explorer.api3.org/address/0x709944a48cAf83535e43471680fDA4905FB3920a) |
| OevAuctionHouse | [0x34f13A5C0AD750d212267bcBc230c87AEFD35CC5](https://oev.explorer.api3.org/address/0x34f13A5C0AD750d212267bcBc230c87AEFD35CC5) |
# OEV Auctioneer (OEV Searchers → In Depth)
OEV Auctioneer is the off-chain system managed by the Api3 DAO to process
auctions hosted on the OEV network. The honesty of OEV Auctioneer
can be verified on-chain because the logic is based solely on the
[OevAuctionHouse](/oev-searchers/in-depth/oev-network/#oevauctionhouse) contract
state.
OEV Auctioneer has two main responsibilities:
1. Resolve auctions and award the winner
2. Confirm or contradict fulfillments
Each dApp that uses OEV feeds is served by an Auctioneer instance. Internally,
Api3 DAO may run multiple Auctioneers as a form of horizontal scaling to ensure
auctions can be processed in a timely manner.
## How it works?
Auctioneer processes logs emitted by the OevAuctionHouse contract and responds
back by interacting with the same contract. Auctioneer is deployed on AWS with
well-established security. It has an Auctioneer wallet that is given the rights
to resolve the auctions and confirm/contradict fulfillments.
The only cross-chain communication happens during fulfillment verification - all
other operations are performed solely on OEV Network. This minimizes latency and improves the resiliency.
## Enforced conventions
Auctioneer enforces a few conventions. These are important for searchers to
understand and comply with in order to successfully participate in auctions.
### Constants
| Name | Value | Description |
| ------------------------------------- | ----- | -------------------------------------------------------------------------------------------------- |
| AUCTION_LENGTH_SECONDS | 30 | How long an auction lasts. |
| OEV_AUCTIONS_MAJOR_VERSION | 1 | Increased when we release any breaking change relevant to OEV auctions. |
| COLLATERAL_REQUIREMENT_BUFFER_PERCENT | 5 | The additional percentage of the bidder's collateral to mitigate against price changes. |
| BID_PHASE_LENGTH_SECONDS | 25 | The length of the bid phase during which searchers can place their bids. |
| REPORT_FULFILLMENT_PERIOD_SECONDS | 86400 | The fulfillment period, during which the auction winner is able to report payment for the OEV bid. |
| MINIMUM_BID_EXPIRING_SECONDS | 15 | The minimum expiring time for a bid to be considered eligible for award. |
| PLACED_BIDS_BLOCK_RANGE | 300 | The number of blocks queried for placed bids during award phase. |
### Auction offset
Auctions repeat indefinitely and take a fixed amount of time. The first auction
starts at the UNIX timestamp 0 (midnight UTC on 1st of January 1970) plus an
offset based on the dApp ID.
```solidity
uint256(keccak256(abi.encodePacked(uint256(dAppId)))) % AUCTION_LENGTH_SECONDS;
```
::: info ℹ️ Example
Say there is a dApp with ID `13` and `AUCTION_LENGTH_SECONDS=30`
- When we encode and hash the dApp ID, we get
`0xd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb5`.
- When we convert it to a decimal number, we get
`97569884605916225051403212656556507955018248777258318895762758024193532305077n`.
- When we modulo the number by `30`, we get `17`.
So the first auction starts at UNIX timestamp `17` and repeats every 30s. The
second auction starts at timestamp `47`, the third at `77`, and so on...
:::
### Bid topic
Auctioneer uses the following convention for the bid topic:
```solidity
keccak256(
abi.encodePacked(
uint256(majorVersion),
uint256(dappId),
uint32(auctionLength),
uint32(signedDataTimestampCutoff)
)
);
```
Let's break down the components of the bid topic:
1. `majorVersion` - The major version of OEV Auctions. Any change resulting in
auction behavior changing, such as changes in auction rules or off-chain
protocol specs, is denoted by this major version being incremented. Refer to
the current value of `OEV_AUCTIONS_MAJOR_VERSION` constant.
2. `dappId` - The dApp ID for which the auction is being held.
3. `auctionLength` - The length of the auction. This parameter must be set to
`AUCTION_LENGTH_SECONDS`. It is one of the most important parameters, so
we're explicitly including it in the bid topic to highlight its importance.
4. `signedDataTimestampCutoff` - The cutoff timestamp of the signed data. The auction winner is permitted to only use signed data with timestamps smaller than or equal to this. It is equal to the end of the bid phase of the
auction.
::: info ℹ️ Info
Auctions repeat continuously and indefinitely. To calculate the
`signedDataTimestampCutoff` that is to be specified in the bid topic, one needs
to calculate the `startTimestamp` of the next auction. This depends on the auction
offset, `BID_PHASE_LENGTH_SECONDS` and the current time.
For example, dApp with ID `13` has an auction offset of `17`. With
`AUCTION_LENGTH_SECONDS=30` and `BID_PHASE_LENGTH_SECONDS=25` this gives the
following sequence of auctions:
| `startTimestamp` | `signedDataTimestampCutoff` | End of award phase |
| ---------------- | --------------------------- | ------------------ |
| 17 | 42 | 47 |
| 47 | 72 | 77 |
| 77 | 102 | 107 |
and so on...
:::
### Bid details
The bid details follow this convention:
```solidity
abi.encode(
address(updateSenderAddress),
bytes32(nonce)
);
```
The arguments are:
1. `updateSenderAddress` - The address that is going to pay for the OEV bid and
update the data feed, if the bid wins the auction.
2. `nonce` - A random nonce to prevent bid ID conflicts.
### Award details
The award details contain a signature that the auction winner uses to pay the
OEV bid, which allows them to update the price feeds.
### Fulfillment details
The fulfillment details are a single `bytes32` value that represents the
transaction hash on the target chain in which the auction winner paid for the
OEV bid.
## Bid eligibility
Auctions are open for everyone. Searchers interact with the OevAuctionHouse
contract when placing a bid, which enforces a few restrictions. Apart from the
on-chain restrictions, Auctioneer adds a few more:
1. Ignore all bids that expired or are expiring within the next
`MINIMUM_BID_EXPIRING_SECONDS` period. This ensures that the awarded bid will
still be active when the transaction is mined.
2. Ensure the bidder has enough collateral to cover the bid amount along with
extra `COLLATERAL_REQUIREMENT_BUFFER_PERCENT` percent to account for price
fluctuations. This ensures that enough collateral can be reserved at award
time.
3. Ensure the bidder has not initiated a withdrawal. This prevents withdrawing
the deposit just before the bid award. Note that it does not matter if the
bid was placed before the withdrawal - if there is a withdrawal initiated,
all bidder's bids are ignored.
Auctioneer fetches the required information from the OevAuctionHouse contract.
In a rare case when Auctioneer fails to fetch eligibility for a bidder, it will
abort awarding the current auction.
::: info ⚠️ Warning
If a bidder places multiple bids across different dApps in quick succession,
with only enough collateral to cover a subset of the bids, then Auctioneer may
attempt to award a bid when the bidder does not have enough collateral.
Let's say we have:
- two dApps (D1 and D2) whose award phases start one second from each other
- a bidder with only enough collateral to cover one bid for either dApp
If the bidder places 2 winning bids (B1 and B2), one for each dApp, Auctioneer
can end up executing this sequence:
- run auction for dApp D1 and fetch the bidder's current collateral
- run auction for dApp D2 and fetch the bidder's current collateral
- award bid B1 for dApp D1
- award bid B2 for dApp D2, but the transaction fails because the bidder no
longer has enough collateral after bid B1 was awarded
In this scenario, Auctioneer will not try to award another bidder, as the
awarded signature will already have been exposed in the reverting award
transaction.
:::
## Auction resolution
Each auction is split into two phases:
1. Bid phase - During this phase, searchers are free to submit their bids.
This phase takes `BID_PHASE_LENGTH_SECONDS`.
2. Award phase - During this phase, Auctioneer determines and awards the winner.
Bids placed during this period are ignored.
As soon as the bid phase is over, Auctioneer attempts to resolve the auction
as soon as possible. The following happens under the hood:
1. Compute the bid topic for the current auction
2. Fetch the current block on the OEV Network
3. Fetch the bids for the bid topic from the last `PLACED_BIDS_BLOCK_RANGE`
4. Discard all ineligible bids
5. Select the bidder with the highest bid amount. In case there are multiple
eligible bids with the same amount, the bidder with the earliest bid is
selected
6. Prepare and submit the award for the auction winner on the OEV Network
Under rare circumstances, when Auctioneer is unable to fetch the block or the
logs from the OEV Network, the auction will be aborted and no winner is chosen.
Similarly, if the auction award transaction fails, there will be no retry,
because the award signature was already exposed publicly.
### Bid guarantees
Auctioneer guarantees that any bid placed during the bid phase will be
processed. The timestamp of the placed bid is determined by the block timestamp
in which the transaction is included. Searchers need to be mindful of that and
consider practical limitations like the OEV Network block time and make sure their bids are placed in time.
::: info ⚠️ Warning
Auctioneer may also include bids placed before or slightly after the
bid phase. This is because Auctioneer fetches the logs from the OEV Network
some time in the award phase. It fetches logs from a sufficient block range with
some buffer to ensure the full bid phase is included. This behavior might
change in time and searchers should not rely on it.
:::
## Processing fulfillments
After the auction winner is awarded, they are expected to fulfill their duties
by paying for the awarded OEV bid. After they've made the transaction on the
target chain, they are expected to report the fulfillment back to the OEV
Network to get their collateral released. Auction winners are advised to wait a
sufficient time for the transaction to reach enough finality on the target
chain.
Auctioneer periodically queries the OEV Network logs for such events and performs the following operations:
1. Fetch all logs regarding fulfillments for a sufficient time period -
AwardedBid, ReportedFulfillment, ConfirmedFulfillment and
ContradictedFulfillment.
2. Contradict all AwardedBid events that are `REPORT_FULFILLMENT_PERIOD_SECONDS`
old without a matching reported fulfillment. Make no action for other
AwardedBid events that are within the fulfillment period.
3. For all ReportedFulfillment events without a matching ConfirmedFulfillment or
ContradictedFulfillment, fetch the PlacedBid event to determine which chain
the bid was for.
4. Verify that the reported fulfillment is valid. It must be paid through the
correct contract, with the correct amount and on the correct target chain.
In case there is a failure during any of the steps above, the Auctioneer tries
to process the fulfillment later. Its utmost priority is to avoid slashing
honest searchers. That said, once the Auctioneer disproves the fulfillment, it
will promptly slash.
::: info ℹ️ Info
Note that the auction winner may choose not to update the price feed when they
pay for the awarded bid. This is an allowed way to withhold the updates because
the auction winner is losing money by being slashed, making it financially
infeasible. As a note, the data feed security remains unchanged because it will
be eventually updated by an Api3 push oracle when the deviation exceeds its
threshold.
:::
## Auctioneer addresses
OEV Auctioneers use dedicated wallets to award auctions and process
fulfillments. These addresses are granted the respective privileges on the
OevAuctionHouse contract on the OEV Network and Api3ServerV1OevExtension
contract on all target chains.
Internally, Api3 DAO uses multiple wallets for resolving auctions and processing
fulfillments. The table below displays the addresses of currently whitelisted
Auctioneer wallets.
| Address | Description |
| ------------------------------------------ | --------------------- |
| 0x0350178E8E8731415287E6DbE1f0746A46510868 | Auction resolver |
| 0xfaf0490A34036a3FE2b740545D67b81b8d3ADfB8 | Fulfillment processor |
## Maintenance
Auctioneer is maintained by the Api3 DAO, which is responsible for its uptime
and reliable auction processing. In case of a planned migration or maintenance,
there will be an announcement shared in advance. It's expected that maintenance
periods will be very rare and short.
::: info 💡 Tip
In fact, ever since we launched OEV Network, there was not a single period of time that resulted in a downtime.
:::
# OEV searching (OEV Searchers → In Depth)
We assume that a searcher has an existing MEV bot and is familiar with the OEV
Network and OEV Auctioneer. Let's glue these concepts together and detail the
steps to start OEV searching.
## Monitoring signed data
Searchers need to have a list of data feeds used by the dApp and
[obtain its beacons](/oev-searchers/in-depth/data-feeds/#dapp-sources). However,
these are the beacons of the base feed. For each of these beacons, the searcher
must derive the OEV counterpart to obtain the
[OEV beacon](/oev-searchers/in-depth/data-feeds/#oev-feed).
Once the list of OEV beacons is known, searchers should periodically call the
public [OEV endpoints](/oev-searchers/in-depth/data-feeds/#oev-endpoints) to get the
real-time values for the OEV beacons used by the dApp. It's necessary to persist
these values for a brief period of time - in case they win the auction and need
to update the data feed.
OEV auctions provide exclusivity guarantees only for signed data with timestamps
from within the bid phase. Note that using older signed data is permitted, but it's likely that such data is already exposed to the public via [base feed endpoints](/oev-searchers/in-depth/data-feeds/#base-feed-endpoints), so their use is discouraged.
## Simulating a data feed update
Compared to the base feed updates, OEV updates are permissioned - allowing only
the auction winner to update the data feed. This makes the OEV update on-chain
simulation trickier. We acknowledge this as a very important part of OEV
extraction, so we built this into the protocol.
This works via `simulateDappOevDataFeedUpdate` and `simulateExternalCall`
functions, which can be called only with `address(0)`. The only way to
impersonate a zero address is during a staticcall simulation. The intended usage
is to do a multicall that simulates some data feed update(s) and then makes an
arbitrary number of external calls.
### On-chain details
To simulate a data feed update, call the `simulateDappOevDataFeedUpdate` function with sender `address(0)`.
```solidity
function simulateDappOevDataFeedUpdate(
uint256 dappId, // The ID of the dApp that the searcher wants to update
bytes[] calldata signedData // The ABI encoded signed data used for updating the data feeds
)
external
returns (
bytes32 baseDataFeedId, // The data feed ID that was updated
int224 updatedValue, // The aggregated value of the update
uint32 updatedTimestamp // The aggregated timestamp of the update
);
```
The ABI encoded signed data are expected to be decoded to the following fields:
- `address airnode` - The address of the Airnode wallet.
- `bytes32 templateId` - The template ID of the base feed beacon - **not** the
template ID of the OEV beacon.
- `uint256 timestamp` - The timestamp of the data.
- `bytes memory data` - The encoded value.
- `bytes memory signature` - The signature for this signed data - signed for the
base feed beacon.
::: info ⚠️ Warning
It might be a bit surprising to pass the template ID of the base feed beacon,
because the data and the signature are supplied for the OEV beacon. However, the
contract needs to know both. While hashing the base feed template ID to obtain
the template ID of the OEV beacon is possible - "un-hashing" the base feed
template ID from the OEV template ID is not.
Say the searcher wants to update the value of base feed beacon with template ID
`0x1bb9efc88ac9d910a9edc28e8cad8959d196a551e15c9af3af21247f1605873f` and they
want to use the following signed data for the OEV beacon:
```json
"0x154ca7c81eb1ed9ce151d5b6ad894c5ab79d19bee20d89eb061aaf24f788221f": {
"airnode": "0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4",
"encodedValue": "0x000000000000000000000000000000000000000000000000003f9c9ba19d0d78",
"signature": "0xf5f454722652215823cb868fd53b7a0c63090dff46e65ba7cdd5fb120df3a520522b80b1fa41f2599c429daa0e48c4f42f60f25b41dab3a8a9be1d2547ebe9811b",
"templateId": "0xbc7896315bfd4b1186a05f219ec71a95def0d038617e7ae534075317866bfd1b",
"timestamp": "1726474901"
}
```
they would encode the signed data as follows:
```solidity
abi.encode(
address(0xc52EeA00154B4fF1EbbF8Ba39FDe37F1AC3B9Fd4),
bytes32(0x1bb9efc88ac9d910a9edc28e8cad8959d196a551e15c9af3af21247f1605873f),
uint256(1726474901),
hex"000000000000000000000000000000000000000000000000003f9c9ba19d0d78",
hex"f5f454722652215823cb868fd53b7a0c63090dff46e65ba7cdd5fb120df3a520522b80b1fa41f2599c429daa0e48c4f42f60f25b41dab3a8a9be1d2547ebe9811b"
)
```
:::
### Searcher bot snippet
The following is an example code snippet demonstrating a relevant searcher bot implementation in JavaScript using the ethers library.
```js
const signedDataCollection = [...] // Assume we have the signed data for the beacons.
// 1. Create the calldata for the dApp OEV data feed updates.
const dAppOevDataFeedUpdateCalldata = []
for (const signedData of signedDataCollection) {
dAppOevDataFeedUpdateCalldata.push(
api3ServerV1OevExtension.interface.encodeFunctionData(
'simulateDappOevDataFeedUpdate',
[dAppId, signedData]
)
);
}
// 2. Create the calldata for the external calls.
const externalCallsCalldata = [...] // E.g. Liquidation calls.
// 3. Impersonate the zero address and simulate the data feed update.
const api3ServerV1OevExtensionImpersonated = api3ServerV1OevExtension.connect(
ethers.constants.AddressZero
);
// 4. Simulate the data feed update and external calls.
const simulationResult = await api3ServerV1OevExtensionImpersonated.multicall.staticcall(
...dAppOevDataFeedUpdateCalldata,
...externalCallsCalldata
);
```
## Placing a bid
After a profitable OEV opportunity is identified, the searcher needs to place a
bid in the auction. There are two ways to
[place a bid](/oev-searchers/in-depth/oev-network/#placing-a-bid), but the
idiomatic way is to call `placeBidWithExpiration`.
It accepts the following parameters:
| Argument | Type | Description |
| -------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| bidTopic | bytes32 | The [bid topic](/oev-searchers/in-depth/oev-auctioneer#bid-topic) of the current auction. |
| chainId | uint256 | The chain ID of the target chain. |
| bidAmount | uint256 | The amount of the bid in the native currency of the target chain. At award time, a respective percentage fo this amount is reserved as collateral and the winner is expected to pay the full bid amount on the target chain. |
| bidDetails | bytes | The [bid details](/oev-searchers/in-depth/oev-auctioneer#bid-details) of the bid. |
| maxCollateralAmount | uint256 | The maximum collateral amount that the bidder is willing to lock up. This is to prevent unwanted slippage in case of a large price change before the transaction is mined. |
| maxProtocolFeeAmount | uint256 | The maximum protocol fee amount that the bidder is willing to pay. This is to prevent unwanted slippage in case of a large price change before the transaction is mined. |
| expirationTimestamp | uint32 | The timestamp until which the bid is valid. The timestamp is checked against the `block.timestamp` at the bid placement time. Minimum is 15 seconds and maximum 24 hours. |
The most intuitive way to place the bid is to follow the recommendations above
and provide a percentage of the profit as the bid amount. Note that the searcher
needs to be mindful of all the gas costs on both the target chain and OEV
Network, the paid bid amount, external risks due to liquidity changes on target chain and the respective collateral and protocol fee on OEV Network.
For a bid to be valid, it needs to use the correct arguments, most importantly the bid topic, which identifies the auction. For the bid
to be considered, the place bid transaction needs to be mined during the bid
phase. Searchers should be mindful of the block time on the OEV Network to make
sure their transaction is mined in time.
## Expediting a bid
Because OEV Auctions are short-lived and the minimum bid lifetime is 15 seconds,
there is little reason to place long-lived bids. However, in rare cases when a
bid is placed by mistake, one can expedite it manually to prevent potential
issues.
There are two ways to
[expedite a bid](/oev-searchers/in-depth/oev-network/#expediting-a-bid), but the
recommended way is to call `expediteBidExpirationMaximally`.
It accepts the following parameters:
| Argument | Type | Description |
| -------------- | ------- | --------------------------------------------------------------------------------------------- |
| bidTopic | bytes32 | The [bid topic](/oev-searchers/in-depth/oev-auctioneer#bid-topic) of the current auction. |
| bidDetailsHash | bytes32 | The hash of the [bid details](/oev-searchers/in-depth/oev-auctioneer#bid-details) of the bid. |
## Waiting for auction award
Immediately after the bid phase is over, Auctioneer enters the award phase,
determines the auction winner, and submits the `awardBid` transaction, which
emits an AwardedBid event. This event indexes the three most important
arguments:
- `bidder` - The auction winner
- `bidTopic` - The bid topic of the auction
- `bidId` - The bid ID of the auction
Searchers can create an event filter to query for all their awarded bids by
filtering for particular bidder(s), or they could create an event filter for a
specific auction or simply query for their bid. The most idiomatic way is to
poll the AwardedBid with a particular bid topic. After the event is received,
searchers can check the bidder to see if they are the winner or not. It's
recommended to use a small polling interval to obtain the result as soon as
possible.
Auctioneer should in practice award the bid during the award phase, but
searchers are recommended to poll longer. If Auctioneer does not respond even
within the next bid phase, there is likely something wrong. Whether the
issue is caused by Auctioneer or the searcher can be determined by looking at
the OEV Network. If the issue was caused by Auctioneer, the searcher can
[open a dispute](/oev-searchers/in-depth/oev-searching#handling-disputes).
::: info 💡 Tip
Searchers can monitor the auction in real-time and can determine the auction
winner themselves (or even attempt to increase their bid).
:::
## Capturing OEV
After the bid is awarded, the searcher needs to do the following:
1. Pay for the awarded bid
2. Update any of the dApp's data feed(s)
3. Capture any OEV opportunities exposed by the data feed update(s)
It's expected that searchers perform all of these steps atomically. However, the
contract allows searchers to repeat steps 2 and 3 as many times as they want.
However, each update must increase the timestamp of the OEV beacon(s).
The OEV capabilities are enabled by the
[Api3ServerV1OevExtension](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1OevExtension.sol).
This contract allows the auction winner to pay for the winning bid and update
the data feed values.
### Paying for the OEV bid
Paying for the OEV bid presents a problem. The searcher does not have funds
upfront - they only receive these once they capture OEV. This challenge has a
workaround - let the searcher take a loan. However, searchers often need to take a loan to
capture the OEV opportunity. This presents a problem because protocols often implement
reentrancy guards, preventing nested loans. The alternative is to compute
the loan amount to account for both OEV opportunity and the bid payment.
We expected this to degrade the UX, so we implemented the OEV payment in a way
that allows capturing the OEV opportunity before paying for the OEV bid amount. This works
similarly to taking a loan. The searcher calls `payOevBid` function, which
allows the `msg.sender` to update the dApp data feeds and calls the
`onOevBidPayment` callback. After the callback is executed, the function
verifies that the contract's balance increased by at least the corresponding bid
amount. In the `onOevBidPayment` callback, the searcher can capture the OEV,
swap the revenue to native currency, and send the adequate bid amount to
Api3ServerV1OevExtension contract.
This is the signature of the `payOevBid` function:
```solidity
function payOevBid(
uint256 dappId, // The ID of the dApp that the searcher wants to update (the same value that was used in bid topic)
uint256 bidAmount, // The bid amount that is to be paid for winning the auction
uint32 signedDataTimestampCutoff, // The signed data timestamp cutoff period (the same value that was used in bid topic)
bytes calldata signature, // The signature that the auction winner received via award details
bytes calldata data // Data that will be passed through the callback
) external;
```
The signature is crafted for a specific dApp ID and signed data timestamp
cutoff. If the searcher provides incorrect values, the signature verification
will fail, causing the transaction to revert. If the signature is valid, the
contract allows the sender to update the data feed(s). Due to exclusivity
guarantees, the winner is guaranteed to be the only one who can update the feed
with data from within the bid phase of the respective auction.
### Api3ServerV1OevExtensionOevBidPayer
The `onOevBidPayment` function is a required for searchers to implement. For simplicity, Api3 provides an interface searchers can use in their contracts.
```solidity
/// @title Interface that OEV bid payers (i.e., contracts that call
/// `payOevBid()` of Api3ServerV1OevExtension) must implement
interface IApi3ServerV1OevExtensionOevBidPayer {
/// @notice Called back by Api3ServerV1OevExtension after an OEV bid payer
/// has called `payOevBid()` of Api3ServerV1OevExtension. During the
/// callback, the OEV bid payer will be allowed to update the OEV feeds
/// of the respective dApp. Before returning, the OEV bid payer must ensure
/// that at least the bid amount has been sent to Api3ServerV1OevExtension.
/// The returndata must start with the keccak256 hash of
/// "Api3ServerV1OevExtensionOevBidPayer.onOevBidPayment".
/// @param bidAmount Bid amount
/// @param data Data that is passed through the callback
/// @return oevBidPaymentCallbackSuccess OEV bid payment callback success
/// code
function onOevBidPayment(
uint256 bidAmount,
bytes calldata data
) external returns (bytes32 oevBidPaymentCallbackSuccess);
}
```
### Updating the data feed
To update the data feed values for a dApp, you can use `updateDappOevDataFeed` function which has the same function signature as `simulateDappOevDataFeedUpdate`. Refer to [Simulating a data feed update](#simulating-a-data-feed-update) section for details.
::: info 💡 Tip
The auction winner can update the data feed multiple times and in multiple
transactions. However, the contract enforces tight security measures. The
timestamp of the signed data for the OEV beacon must be greater than or equal to
the timestamp of the base feed beacon. The data feed value after aggregating OEV
beacons must change the base feed - either increase the timestamp or change the
aggregated value. This enforces time monotonicity at the contract level, making
sure OEV updates provide only the freshest data.
:::
## Bidding contract
The bidder can be either an EOA or a contract. The former is simpler but has
certain drawbacks:
1. The EOA has full control over the deposit in the OevAuctionHouse contract
2. The collateral liquidity is fragmented across multiple EOAs
Both of these downsides can be mitigated by a role-based bidding contract. One
role for withdrawing the funds and the other giving bidding permissions. There
are a few important considerations to keep in mind when designing such contract:
1. The OevAuctionsHouse expects the same account to call `reportFulfillment`.
This means the bidding contract needs to be reporting fulfillments as well.
2. Both `initiateWithdrawal`, `withdraw`, and `cancelWithdrawal` need to be
called by the same address. The bidding contract needs to be allowed to call
all of these. Note that withdrawal cancellation may be omitted if the
contract doesn't need to have this capability. Access to these functions
should be restricted. For example, a malicious actor who has access to these may
call `initiateWithdrawal` and make the Auctioneer disregard the
respective bids, or call `cancelWithdrawal` whenever a withdrawal is
initiated to prevent the funds from ever being withdrawn.
3. The withdrawal recipient is specified in the `withdraw` call. Make sure the
recipient is payable and the funds will not remain locked.
## Handling disputes
In case of a dispute, the OEV Network is considered the source of truth and can
be used to resolve it. This may include Auctioneer awarding the wrong bidder or
being inconsistent with its pre-announced rules.
Note that any dispute that cannot be proven or disproved on-chain is
non-applicable. This may include searchers' complaints about RPC connections or
similar off-chain problems.
To open a dispute, head to the
[OEV Discord channel](https://discord.com/channels/758003776174030948/1062909222347603989)
and create a post with the description of the dispute.
## Additional resources
- [Example OEV Compound bot](https://github.com/api3dao/oev-v1-compound-bot) - You can also inspect the
[changes](https://github.com/api3dao/oev-v1-compound-bot/compare/mev-with-signed-apis...oev)
needed to add the OEV functionality to an existing bot supporting [MEV with Signed APIs](/oev-searchers/in-depth/mev-with-signed-apis).
- [Auctions overview on the OEV Network](https://oev-dashboard.api3.org/#oev-network) is a great tool to inspect the auctions.
# Glossary (OEV Searchers)
Below are brief descriptions of commonly used terms within the documentation.
### Airnode
[Airnode](https://github.com/api3dao/airnode) is a
[first-party oracle](#first-party-oracles) node designed to be operated by
[API providers](#api-provider). For [dAPIs](#dapi) specifically, an
[Airnode feed](#airnode-feed) is used instead.
The key concept is that an API provider uses an
[Airnode wallet](#airnode-wallet) to sign its data. In the context of smart
contracts, `airnode` refers to the [Airnode address](#airnode-address) of this
wallet.
### Airnode ABI
To decode a bytes string that was encoded with contract ABI, one needs to know
the schema used while encoding.
[Airnode ABI](https://github.com/api3dao/airnode/tree/master/packages/airnode-abi)
is a specification built on contract ABI to allow encoding without knowing the
schema.
### Airnode address
All [API providers](#api-provider) [sign their data](#signed-data) with an EOA
wallet. The address of this wallet is announced by the respective API provider
in the DNS records of the base URL of their API.
### Airnode feed
[Airnode feed](https://github.com/api3dao/signed-api/tree/main/packages/airnode-feed)
is an iteration on [Airnode](#airnode) that is optimized to power data feeds.
Airnode feeds are hosted by [API providers](#api-provider) themselves and are
identified by the respective [Airnode address](#airnode-address). The
[wallet](#airnode-wallet) of this account is used to cryptographically
[sign the data](#signed-data) to prove the validity of the data.
### Airnode wallet
A secret wallet only known to the [API provider](#api-provider) who deploys the
[Airnode](#airnode) or [Airnode feed](#airnode-feed) used to sign its data.
### Airseeker
[Airseeker](https://github.com/api3dao/airseeker) is an application that
periodically fetches [signed data](#signed-data) from [Signed APIs](#signed-api)
to update [data feeds](#data-feed) whenever the conditions specified by the
[update parameters](#update-parameters) are satisfied.
### AirseekerRegistry contract
[AirseekerRegistry contract](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/AirseekerRegistry.sol)
serves as an on-chain configuration file for [Airseeker](#airseeker). It
provides a source of truth for [dAPIs](#dapi) and can be used to obtain which
[data feed](#data-feed) a dAPI points to and what its sources are.
### API provider
An API provider is a business that has productized their services in the form of
an API.
### Api3 Market
[Api3 Market](https://market.api3.org/) is a [dApp](#dapp) where users can
purchase [dAPI](#dapi) plans, which get reflected on-chain immediately.
### Api3ServerV1 contract
[Api3ServerV1 contract](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1.sol)
is the main contract for [dAPIs](#dapi). It is used by [Airseekers](#airseeker)
to update [base feeds](#base-feed).
### Api3ServerV1OevExtension contract
[Api3ServerV1OevExtension contract](https://github.com/api3dao/contracts/blob/main/contracts/api3-server-v1/Api3ServerV1OevExtension.sol)
is an extension of the [Api3ServerV1 contract](#api3serverv1-contract). It is
used by [searchers](#searcher) for [OEV feed](#oev-feed) updates.
### Auction
Auction, in the context of OEV auctions, is an order flow auction (OFA) providing exclusive priority for updating data feeds for particular dApp to capture OEV.
### Auctioneer
Short term for [OEV Auctioneer](#oev-auctioneer).
### Award details
The award details provide the auction winner the exclusive priority to update the price feeds for a particular dApp. The award details format is enforced by [Auctioneer](#auctioneer).
### Award phase
Award phase is the second phase of an [OEV auction](#oev-auction) where
[OEV Auctioneer](#oev-auctioneer) resolves the auction and awards the winner. It
is preceded by the [bid phase](#bid-phase).
### Base feed
In the context of OEV extraction, the base feed refers to a
[data feed](#data-feed) behind a particular [dAPI](#dapi). An update of this
data feed is reflected across all [OEV proxies](#oev-proxy) that read from this
dAPI.
### Beacon
A beacon is a single-source [data feed](#data-feed). A beacon is identified by
the respective [Airnode address](#airnode-address) and [template](#template) ID.
```solidity
beaconId = keccak256(abi.encodePacked(airnode, templateId));
```
### Beacon set
A beacon set is an on-chain aggregation of [beacons](#beacon). A beacon set is
identified by the hash of the constituting beacon IDs.
```solidity
beaconSetId = keccak256(abi.encode(beaconIds));
```
### Bid
[Searchers](#searcher) place bids on [OEV Network](#oev-network) to participate
in [OEV auctions](#oev-auction) to obtain exclusive rights to capture
[OEV](#oev).
### Bid details
Required part of a [bid](#bid) providing the details of the bid. The bid details format is enforced by [Auctioneer](#auctioneer).
### Bid amount
The amount a [searcher](#searcher) is willing to pay for winning the
[OEV auction](#oev-auction).
### Bid phase
Bid phase is the first phase of an [OEV auction](#oev-auction) where
[searchers](#searcher) are supposed to place their [bids](#bid). It is followed
by the [award phase](#award-phase).
### Bid topic
A [bid](#bid) is placed for a particular auction, which is identified by the combination of a bid topic and chain ID. The bid topic format is enforced by [Auctioneer](#auctioneer).
### Collateral
Also referred to as collateral amount.
When [Auctioneer](#auctioneer) awards a bid, the OevAuctionHouse contract
reserves a portion of the [bid amount](#bid-amount) as collateral. This
collateral, with the exception of [protocol fee](#protocol-fee), is released
once the Auctioneer confirms the fulfillment reported by the auction winner
after they've paid the bid amount.
### dAPI
The
[Api3 whitepaper](https://github.com/api3dao/api3-whitepaper/blob/master/api3-whitepaper.pdf)
definition of a dAPI is a [first-party oracle](#first-party-oracles)-based data
feed that is managed decentrally.
In practice, a dAPI is mapped to a particular [data feed](#data-feed). The
mapping is managed by Api3 DAO.
### dApp
An application that uses smart contracts. Usually referred to as a source of
[OEV](#oev). Each dApp eligible for [OEV Rewards](#oev-rewards) has a
[dApp ID](#dapp-id) assigned and uses its own [OEV proxies](#oev-proxy).
### dApp ID
Api3 holds separate [OEV auctions](#oev-auction) for different [dApps](#dapp) to
keep their [rewards](#oev-rewards) isolated. In this scheme, dApps are
identified by IDs that are assigned by Api3 DAO.
### Data feed
The common term used to refer to a [beacon](#beacon) or a
[beacon set](#beacon-set). Each [data feed](#data-feed) has a
[base version](#base-feed) that lives in
[Api3ServerV1 contract](#api3serverv1-contract), and an [OEV version](#oev-feed)
that lives in [Api3ServerV1OevExtension](#api3serverv1oevextension-contract).
Data feeds are powered by [Airseekers](#airseeker).
### Deviation
Deviation is the difference between the on-chain value of a
[data feed](#data-feed) and its off-chain value based on the data served by
[Signed APIs](#signed-api). It is measured as a percentage value, and an update
needs to be made when the value exceeds the deviation threshold. A deviation
reference value is used as the reference value according to which the percentage
value will be calculated.
### Endpoint
In the context of data feeds, an endpoint represents a distinct type of oracle
service provided by an [Airnode feed](#airnode-feed), which can be parameterized
by [Airnode ABI](#airnode-abi)-encoded parameters.
An endpoint is identified by the respective
[OIS](https://github.com/api3dao/ois) title and endpoint name.
```solidity
endpointId = keccak256(abi.encode(oisTitle, endpointName));
```
### First-party oracles
An [API provider](#api-provider) that provides oracle services without the use
of any middlemen is a first-party oracle.
Our first-party oracles are powered by [Airnode feeds](#airnode-feed).
### Fulfillment
The [searcher](#searcher) that has won an [OEV auction](#oev-auction) is
expected to pay their [bid amount](#bid-amount). This payment is referred to as
a fulfillment in the context of [OevAuctionHouse](#oev-auction-house).
### Fulfillment details
Fulfillment details are required part of [fulfillment](#fulfillment) and their format is enforced by [Auctioneer](#auctioneer).
### Heartbeat
A heartbeat is a [data feed](#data-feed) update that was made to uphold a
maximum period of time between two consecutive updates, which is called the
heartbeat interval.
### MEV
Maximal extractable value (MEV) is a superset of [OEV](#oev) that can be
extracted by including, excluding or reordering any interaction.
### OEV
Oracle extractable value (OEV) is a subset of [MEV](#mev) that can be extracted
by guaranteeing a specific relative order of oracle updates and related
interactions within a transaction.
Api3 monetizes its [dAPI](#dapi) services by holding
[OEV auctions](#oev-auction) and paying [OEV Rewards](#oev-rewards) to the
respective [dApps](#dapp). This is both a net gain for the dApps (which
otherwise would have bled these funds to [MEV](#mev) bots and validators) and a
fair and scalable business model for Api3 DAO.
### OEV auction
Api3 periodically holds [OEV](#oev) auctions on [OEV Network](#oev-network)
where [searchers](#searcher) [bid](#bid) to receive exclusive update rights to
update the data feeds for a specific [dApp](#dapp) for a limited amount of time.
### OEV Auctioneer
OEV Auctioneer, or simply Auctioneer, is the off-chain component powering the
[OEV Auctions](#oev-auction).
### OEV beacon
Each [base feed](#base-feed) beacon has a corresponding OEV beacon, which is
derived from the original one by hashing the [template](#template) ID using
`keccak256`. These beacons are needed for [OEV searchers](#searcher) to query
[Signed APIs](#signed-api) for their real-time values.
### OEV feed
In the context of OEV extraction, the OEV feed refers to a
[data feed](#data-feed) used by [OEV proxy](#oev-proxy). This feed is proxy
specific and can be updated by a searcher who won the
[OEV auction](#oev-auction).
### OEV updates
OEV updates are data feed updates performed by searchers after winning the auction with the intention of capturing OEV. These updates are an extension of our [base feed](#base-feed) updates.
### OEV Network
OEV Network is an Arbitrum Nitro L2. Its chain ID is 4913 and it uses ETH as the
gas token. Its purpose is to hold [OEV auctions](#oev-auction) in a transparent
and retrospectively verifiable way.
### OEV proceeds
OEV proceeds refer to auction proceeds, which is the amount searchers pay for auctions. Vast majority of OEV proceeds goes back to the dApp in the form of [OEV Rewards](#oev-rewards), while the rest is kept as protocol fee for Api3.
### OEV proxy
An OEV proxy is a proxy contract that reads a value from both
[base feed](#base-feed) and [OEV feed](#oev-feed) and prefers the fresher out of
the two. Our implementation is called `Api3ReaderProxyV1` and partially supports
Chainlink's AggregatorV2V3Interface for convenience.
### OEV Rewards
OEV Rewards refer to the amount paid to the dApp. These rewards constitute the majority portion of the [OEV proceeds](#oev-proceeds).
### Protocol fee
When [Auctioneer](#auctioneer) confirms a [fulfillment](#fulfillment) and
releases the [collateral amount](#collateral), it deducts a protocol fee which
is kept by Api3 DAO. The protocol fee is currently set to 0.
### Searcher
A searcher is an entity that searches for profitable MEV or OEV opportunities in
the market. While searching is typically performed by automated bots, we choose
to refer to searchers as people.
### Signed API
A
[Signed API](https://github.com/api3dao/signed-api/tree/main/packages/signed-api)
receives signed data from [Airnode feeds](#airnode-feed) and serves it to the
public through an API. For example, an [Airseeker](#airseeker) depends on Signed
APIs to update [data feeds](#data-feed).
### Signed data
Refers to the data signed by [Airnode feeds](#airnode-feed), served by
[Signed APIs](#signed-api) and used by [Airseekers](#airseeker) to update
[data feeds](#data-feed).
### Target chain
Target chain is the chain where the
[Api3ServerV1 contract](#api3serverv1-contract),
[Api3ServerV1OevExtension contract](#api3serverv1oevextension-contract) and the
dApp contracts are deployed. This is the chain where the MEV/OEV extraction
happens.
### Template
In the context of [data feeds](#data-feed), a template represents an
[endpoint](#endpoint) and some [Airnode ABI](#airnode-abi)-encoded parameters. A
template is identified by the respective endpoint ID and Airnode ABI-encoded
parameters.
```solidity
templateId = keccak256(abi.encode(endpointID, parameters));
```
### Update parameters
Parameters that specify when an [Airseeker](#airseeker) should update a
[data feed](#data-feed). Typically, there are two aspects that require an
update:
- [Deviation](#deviation): Defined by the deviation threshold and deviation
reference
- [Heartbeat](#heartbeat): Defined by the heartbeat interval