Internal Optimistic Oracle
Using the Optimistic Oracle to verify arbitrary data type results proposed through an external contract
The Internal Optimistic Oracle
The Internal Optimistic Oracle (IOO) implements the Optimistic Oracle's (OO) internal escalation game logic for price requests and price proposals within another contract. The disputes are escalated through the Optimistic Oracle V2 to UMA's Data Verification Mechanism.
The IOO contract is meant to be utilized as a type of OO that permits customized escalation game logic and custom data structures. This pattern provides the most gas efficient route to building an OO integration by removing the need to perform cross-contract calls in all cases except for when a dispute is raised via the internal escalation game.
The IOO is intended to be the simplest implementation possible, allowing it to serve as a starting point for any project that can benefit from these functionalities.
The following table shows how the Internal Optimistic Oracle differs from the Optimistic Oracle V2 and why using the Internal Optimistic Oracle could be useful:
Optimistic Oracle V2 (OO) | Internal Optimistic Oracle (IOO) |
---|---|
Using the OO from an external contract incurs gas costs that, while not excessive, might be reduced with customized implementations like the IOO. | Lower gas costs because price requests are processed locally. |
The requested data types from the OO are always | Customized data structures: the data requested from the oracle can be of any type ( |
The OO design is agnostic and adaptable to every use case, but adding more advanced functionality to price requests, such as defining who can propose values, requires some extra work. | Additional customization is allowed: in the example implementation, a price request is coupled with the |
Disputes are resolved in the DVM through a vote of UMA token holders. | Disputes are resolved in the DVM through a vote of UMA token holders. |
In this simple implementation, we highlight these functionalities, however, if you wish to go further, consider the following:
The data type of the "price" (
proposedPrice
) could be any: structs, arrays, bytes. In the example, we choseuint256
, but you can modify it to suit your requirements. (Note: For historical reasons, the data is referred to as a "price" throughout UMA's code, but can be any type of data, not just data related to asset prices.)Any further logic could be added to the requests and proposals, such as permitting only addresses on a whitelist to submit answers (
proposePrice
function).
Development environment and tests
Clone repository and Install dependencies
Clone the UMA dev-quickstart repository and install the dependencies. To install dependencies, you will need to install the long-term support version of Nodejs, currently Nodejs v16, and yarn. You can then install dependencies by running yarn with no arguments:
Compiling your contracts
We will need to run the following command to compile the contracts and make the Typescript interfaces so that they are easy to work with:
Contract Implementation
The contract discussed in this tutorial can be found at dev-quickstart/contracts/InternalOptimisticOracle.sol
(here) within the repo.
Contract creation and initialization
The constructor of the Internal Optimistic Oracle contract takes three parameters:
_finderAddress
finder contract used to get addresses of other UMA contracts.
_currency
the collateral token used to pay fees.
_timerAddress
is used only when running unit tests locally to simulate advancement of time. For all the public networks (including testnets) zero address should be used.
As part of initialization, the oo
variable is set to the address of the OptimisticOracleV2
implementation as discovered through the getImplementationAddress
method in the Finder contract. This address will vary depending on which chain this contract is deployed to.
Requesting a price
The following function allows you to request a price internally in the IOO. For simplicity the price identifier has been hardcoded to YES_OR_NO_QUERY
. This imposes a set of rules on how the assertion is formulated and formatted, which are specified in UMIP-107.
The requestPrice
function takes the following arguments:
timestamp
timestamp used to identify the request, usually the current timestamp.ancillaryData
the byte-converted question that we want to ask (e.g. 'q: "What uint256 are we looking for?"'
)reward
the amount of thecurrency
defined in the constructor to pay to the proposer on settlementliveness
time period during which the proposal can be disputed
Note: The requestPrice
function requires the caller to approve the IOO to spend the reward
amount of the currency
.
Proposing a price
Once a price has been requested, it can be proposed.
These are the arguments that proposePrice
receives:
timestamp
timestamp used to identify the priceRequest, usually the current timestamp.ancillaryData
the byte-converted question that we want to ask.proposedPrice
the price proposed to the request
Note: The proposePrice
function requires the caller to approve the IOO to spend the bond + finalFee
amount of the currency
.
Note: Because of the _getId
function definition, the requester and the proposer must be the same person.
Disputing a price
Once a price has been proposed it can be disputed by calling disputePrice
, this function takes the following parameters:
timestamp
timestamp used to identify the price request, usually the current timestamp.ancillaryData
the byte-converted question that has been asked initially and already answered by the proposer
Note: The disputePrice
function requires the caller to approve the IOO to spend the bond + finalFee
amount of the currency
.
Note: Because of the _getId
function definition, the requester, proposer and disputer must be the same person.
Tests and deployment
All the unit tests covering the functionality described above are available here. To execute all of them, run:
Before deploying the contract check the comments on available environment variables in the deployment script.
In the case of the Görli testnet, the defaults would use the Finder instance that references the Mock Oracle implementation for resolving DVM requests. This exposes a pushPrice
method to be used for simulating a resolved answer in case of disputed proposals. Also, the default Görli deployment would use the already whitelisted TestnetERC20
currency that can be minted by anyone using its allocateTo
method.
To deploy the Internal Optimistic Oracle contract on Görli, run:
Optionally, you can verify the deployed contract on Etherscan:
Interacting with deployed contract
The following section provide instructions on how to interact with the deployed contract from the Hardhat console, though one can also use it for guidance for interacting through another interface (e.g. Remix or Etherscan).
Start Hardhat console with:
Initial setup
Get the libraries and connect to the necessary contracts that we are going to use. In this tutorial we are using TestnetERC20
as the currency
as described in the deployment script.
Request a price and propose a price without disputes
First we need to mint the reward
amount and approve the IOO to pull them. This amount of currency
tokens will be used to pay the proposer of the price request we are going to create.
Request the price:
Then we can propose a price to answer the question. The proposer needs to post a bond equal to the request.bond + finalFee
in order to do this so let's mint and approve the tokens as before:
And propose the price:
Now we need to wait 10 seconds for the liveness period to be over and settle and get the price to verify the correct answer:
Request a price and propose a price with a dispute
After requesting and proposing a price we can dispute the price to escalate it to the DVM to be resolved by UMA voters. In this example we will be proposing a correct answer and disputing it, asking the DVM to determine the answer. The first steps are the same as before where we request a price and propose an answer:
Then we can dispute the price:
As we are in a test environment, we can simulate the vote that would normally occur by pushing a price to the mockOracle:
At this point, we can call settleAndGetPrice
to settle the accepted price request and then confirm that the result matches the proposed price:
Last updated