This is a two part series that is designed to guide you through the necessary preparations & processes for using the Tellor oracle. The first part walks a user through actions for the security of your code / integration, and the second is actions to help make sure the data flowing into your contracts is properly checked & monitored.
Part 1 – Development
Although using Tellor can technically be done completely permissionlessly, we highly recommend you ask for help & advice and share the code of your Tellor integration with us, but at a minimum you should communicate to the team what oracle data is needed, the desired frequency, and how the feeds will be funded. This helps the Tellor team & reporters to better understand your needs. There are two ways to approach this, you can make an issue in the dataSpecs repo, or by reach out in the Tellor discord.
That said let’s take a look the 3 best practices on code side of things:
Best practice #1 – Dispute Buffer
When a Tellor data reporter submits some data, it’s dangerous to immediately use that value in your protocol as the thing that secures the data are the incentives built around a dispute mechanic. That is, the first individual to correctly dispute bad data gets a great reward and the reporter who submitted the bad data incurs a great penalty. So you could say Tellor oracle data has probabilistic finality – the more time that goes by without a dispute taking place, the more likely it is to be final (good). This is the purpose of the dispute buffer, a user-specified length of time to allow for a dispute to potentially take place.
Example code using Tellor with dispute buffer:
...
uint256 public constant DISPUTE_BUFFER = 20 minutes;
...
function getBitcoinPrice()
public
returns (
uint256 _value,
uint256 timestamp
)
{
(bytes memory _data, uint256 _timestamp) = getDataBefore(btcQueryId, block.timestamp - DISPUTE_BUFFER);
Best Practice #2 – Add a staleness check
This one is use-case specific as certain data types like random numbers, or a simple question and answer, don’t necessarily get stale the same way a spotPrice might. For example, if Liquity Protocol started using 3 day old eth/usd prices, their protocol would break. This staleness check best practice is a reminder to not assume tellor data will automatically be updated frequently all the time. Here’s a 12 hour example:
uint256 public constant STALENESS_AGE = 12 hours; ... if (block.timestamp - _timestamp > STALENESS_AGE) revert StalePrice(_value, _timestamp);
Best Practice #3 – Prevent a “back-in-time” attack
In the event where a Tellor value is disputed, the disputed value is removed & previous values remain. A potential attacker could use disputes to go “back in time” to find a desired value. By caching the most recent data value and its timestamp and checking against it, this attack vector is prevented. Here’s how that might look:
if (_timestamp > lastStoredTimestamp) {
// if the new value is newer than the last stored value, update the cache
lastStoredTimestamp = _timestamp;
lastStoredPrice = _value;
} else {
// if the new value is older than the last stored value, use the cached value
_value = lastStoredPrice;
_timestamp = lastStoredTimestamp;
}
Reference Contract
Now that you’ve gotten the overview please refer to this reference https://github.com/tellor-io/best-practices-user. In it you’ll find an example contract that fully utilizes these best practices with comments.
Once these 3 best practices have been implemented into how you use Tellor – we recommend you share your code with us to review and provide feedback! After that it’s time to ensure everything is properly supported and monitored, learn how in Part 2: Maintenance.