# Welcome to UMA

A decentralized truth machine

**An optimistic oracle (OO) that can record any verifiable truth or data onto a blockchain** :crystal\_ball:

UMA is an optimistic oracle and dispute arbitration system that securely allows for arbitrary types of data to be brought onchain. UMA’s oracle system provides verified onchain data for projects including:

1. Crosschain bridge
2. Prediction markets
3. Insurance protocols
4. Custom derivatives

UMA's optimistic oracle (OO) is designed to be modular and extensible with focus on building real-world use cases. Two main versions of the optimistic oracle (OO) are currently live: **OOv2** and **OOv3**. Choosing the right one depends on your use case.

<figure><img src="/files/dnakuUKrOT8ogtHOtIEZ" alt=""><figcaption></figcaption></figure>

***

## Core Concepts

Learn the fundamentals of UMA's optimistic oracle.

<table data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th><th data-hidden data-card-target data-type="content-ref"></th></tr></thead><tbody><tr><td><strong>Optimistic Oracle V2</strong></td><td>Build prediction markets and insurance protocols</td><td><a href="/files/6xjupgt3wWYqgMWkP6Cx">/files/6xjupgt3wWYqgMWkP6Cx</a></td><td><a href="/pages/OWci4kZl4u0xRwWKxkc9">/pages/OWci4kZl4u0xRwWKxkc9</a></td></tr><tr><td><strong>Optimistic Oracle V3</strong></td><td>Build data asserters and escalation managers</td><td><a href="/files/BQQ119uOOdEUey6lizd0">/files/BQQ119uOOdEUey6lizd0</a></td><td><a href="/pages/583R7lqheo8kKOua7xGY">/pages/583R7lqheo8kKOua7xGY</a></td></tr><tr><td><strong>Voting Walkthrough</strong></td><td>Learn how to stake tokens, vote, and claim rewards</td><td><a href="/files/nA4iV04pAa4cMASC5BzZ">/files/nA4iV04pAa4cMASC5BzZ</a></td><td><a href="/pages/lSP1eZYyjTICnKULXgvD">/pages/lSP1eZYyjTICnKULXgvD</a></td></tr></tbody></table>

### Correct Oracle for Your Use Case

UMA offers OOv2 and OOv3. Based on your idea and product, let's learn which one fits your use case best:

|                                              OOv2                                              |                                                   OOv3                                                  |
| :--------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: |
|   Integrations must submit data requests that can then be answered by third-party proposers.   | Integrations submit assertions, where they propose data to their own request along with its parameters. |
| The specific parameters for proposers and disputers to follow must be included in the request. |                        The challenge period and dispute process remains the same.                       |
|  Primary use cases: Prediction markets, sports betting applications, and insurance protocols.  |       Primary use cases: Crosschain infrastructure, content moderation, transaction verification.       |

We understand that your product needs can be complex and would require a larger discussion on which optimistic oracle version to use. Feel free to reach out to us [here](/resources/links#links) and get help.

***

## Developer Quickstart

Having decided the correct optimistic oracle for your use case, let's now dive deeper into how you can ship apps powered by verifiable onchain truth using UMA's optimistic oracle.

{% tabs %}
{% tab title="OOv2" %}
OOv2 prioritizes protocols that need third-parties to propose answers to data requests.

Here are some app guides to help you build on UMA OOv2:

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><strong>Prediction Market</strong></td><td>Use OOv2 to identify and settle real-world events in prediction markets.</td><td><a href="/files/tBmnr33b5iGsHzjRQr74">/files/tBmnr33b5iGsHzjRQr74</a></td></tr><tr><td><strong>Insurance</strong></td><td>Use OOv2 to verify and resolve claims in insurance applications.</td><td><a href="/files/8RVtbndzTruSqjjCGKRC">/files/8RVtbndzTruSqjjCGKRC</a></td></tr></tbody></table>
{% endtab %}

{% tab title="OOv3" %}
OOv3 prioritizes protocols that simply needs asserted data to be verified.

Here are some app guides to help you build on UMA OOv3:

<table data-card-size="large" data-view="cards"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><strong>Data Asserter</strong></td><td>Use OOv3 to asserts arbitrary offchain data onchain through flexible assertions</td><td><a href="/files/dzkWNADBShDRTgNgGlBx">/files/dzkWNADBShDRTgNgGlBx</a></td></tr><tr><td><strong>Escalation Managers</strong></td><td>Use OOv3 to enable modular functionality via custom escalation managers</td><td><a href="/files/v9m48ISNtDTCegMNEGXp">/files/v9m48ISNtDTCegMNEGXp</a></td></tr></tbody></table>
{% endtab %}
{% endtabs %}

***

## Community

The UMA token secures UMA’s optimistic oracle through decentralized governance and economic guarantees against corruption. Token holders vote on upgrades, price requests, and disputes, earning rewards for honest participation.

**UMA Improvement Proposals (UMIPs)** are design documents used to propose changes to the UMA ecosystem. They are an important part of UMA's governance processes.

The **UMA DAO** accepts proposals for onchain actions that require tokenholders approval. An example would be a request for funding from the UMA treasury.

<table data-view="cards" data-full-width="false"><thead><tr><th></th><th></th><th data-hidden data-card-cover data-type="files"></th></tr></thead><tbody><tr><td><strong>Governance</strong></td><td>Learn about UMA token, UMIPs and voting on UMA's DVM</td><td><a href="/files/ywxBtZyaItjewsjsbq3B">/files/ywxBtZyaItjewsjsbq3B</a></td></tr><tr><td><strong>UMIP Process</strong></td><td>Learn how UMIPs propose changes to the UMA Ecosystem</td><td><a href="/files/96ekBtqZ3b5OB3MId9nl">/files/96ekBtqZ3b5OB3MId9nl</a></td></tr><tr><td><strong>DAO Proposals</strong></td><td>Learn how you can submit onchain proposals to the UMA Ecosystem</td><td><a href="/files/UbCYBzzxgCaguMFU1cJf">/files/UbCYBzzxgCaguMFU1cJf</a></td></tr></tbody></table>

***

## Resources and Support

We request all community members and developers to [join our discord server](https://discord.umaproject.org/) and gain instant help on any issues they face while voting or submitting proposals to UMA.

You can [find more support resources here](/resources/network-addresses).

The security of our protocol is extremely crucial. If you notice any bugs in the protocol, [please report them here](/resources/audit-and-bug-bounty-programs#submissions). You can also find details of our [audits here](/resources/audit-and-bug-bounty-programs).


# FAQs

General questions about UMA OO

## General Questions

<details>

<summary><strong>What is UMA?</strong></summary>

UMA is a decentralized protocol designed to verify real-world information onchain in a trustless and secure manner. It consists of an **optimistic oracle (OO)** and a **data verification mechanism (DVM)** to help apps trustlessly verify real-world data using economic guarantees and community resolution.

</details>

<details>

<summary><strong>Why does UMA matter?</strong></summary>

Decentralized applications need trustworthy data to function. However, blockchains and smart contracts are inherently disconnected from real-world data. UMA bridges this gap by providing a scalable, secure, and cost-effective way to verify offchain (and onchain) information without relying on a centralized oracle.

</details>

<details>

<summary><strong>How does UMA compare to Chainlink?</strong></summary>

Chainlink streams data onchain at regular intervals. UMA is a request-based, dispute-driven system that verifies data only when needed, offering cost savings and support for broader data types.

</details>

<details>

<summary><strong>What makes UMA different from other oracles?</strong></summary>

Most oracles can only process purely objective data (e.g. price feeds). UMA is uniquely built to process and verify objective *and* intersubjective data (e.g. sports scores, election outcomes, cultural events, etc.).

UMA’s OO enables smart contracts to verify intersubjective claims such as “Did the Eagles win the Super Bowl?”, “Did Donald Trump say ‘Tesla’ in the debate last night?” or “Did xyz proposal pass?" All these claims are verified using a decentralized dispute resolution system backed by economic guarantees.

Additionally, rather than pushing data onchain by default, UMA uses an optimistic system where proposed data is assumed correct unless challenged. This design allows for fast verification through the OO, while still enabling secure dispute resolution through the DVM. If a dispute is raised, a decentralized network of UMA token stakers vote on the correct answer using offchain information. This layered structure is fast when there’s consensus and robust when there’s conflict, making UMA more flexible, cost-effective, and secure than traditional oracles.

</details>

<details>

<summary><strong>Can UMA verify data about real-world events?</strong></summary>

Yes. UMA can verify outcomes of real-world events like elections, sports results, weather outcomes, or legal rulings inasmuch as there's public, credible evidence that voters can independently verify.

</details>

***

## How UMA Works

<details>

<summary><strong>What is the optimistic oracle?</strong></summary>

UMA’s [optimistic oracle (OO)](https://blog.uma.xyz/articles/what-is-umas-optimistic-oracle) is a decentralized verification protocol that lets anyone propose data onchain. If no one disputes the data within a predefined liveness period, it's considered verified. If disputed, the claim goes to a community vote via the DVM.

<figure><img src="/files/iiLdDw12uwfTyVkkfOku" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><strong>What is the DVM?</strong></summary>

The [Data Verification Mechanism (DVM)](https://docs.uma.xyz/protocol-overview/dvm-2.0) is UMA’s dispute resolution system. It uses a commit-reveal voting process where UMA tokenholders vote on the correct answer to a disputed data request. Honest voters earn rewards; incorrect or absent voters are penalized.

</details>

<details>

<summary><strong>How long does it take to verify data with UMA’s system?</strong></summary>

If undisputed, data is verified after the liveness period, which is configurable and should be set based on your use case. Currently, integrations set their liveness period between 2 hours to 2 days. If undisputed, the data is verified after the liveness period. In the rare case of a dispute, the DVM vote resolves the case within 2-4 days.

</details>

<details>

<summary><strong>What is the economic model behind UMA?</strong></summary>

UMA relies on the principle of economic guarantees. Economic incentives are in place for every participant, from proposers, disputers, and voters—to ensure that everyone behaves honestly. This economic model ensures that corrupting the protocol (e.g., by submitting false data or voting dishonestly) is more expensive than the potential profit from doing so.

</details>

<details>

<summary><strong>Can I trust UMA without understanding how it works?</strong></summary>

Yes, UMA is designed to be trustless. The economic game theory ensures that data is highly likely to be accurate, even if you don’t personally verify each step.

</details>

***

## UMA Participants (Proposers, Disputers, and Voters)

### Proposing Data

<details>

<summary><strong>What kinds of data can be proposed?</strong></summary>

Any peice of information that can be verified with public evidence or consensus: asset prices, sporting event outcomes, governance results, hack confirmations, crosschain transfers, and more.

</details>

<details>

<summary><strong>How do I propose data?</strong></summary>

Use the [UMA OO dApp](https://oracle.uma.xyz/propose) to submit data to the OO. Under the “Propose” tab, you can view the full list of live queries and choose which one you want to propose an answer to. You’ll need to post a bond and specify details for the request. If undisputed during the liveness window, the data is finalized and published onchain.

<figure><img src="/files/FkGHOOpobeFV9O6Fr5cU" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><strong>What are the incentives for honest behavior as a proposer?</strong></summary>

When a proposer is correct, they receive a posted financial reward. However, when a proposer is incorrect, they lose their bond. This encourages honest participation. Dishonest participation can result in financial losses.

</details>

<details>

<summary><strong>What are the risks of participating as a proposer?</strong></summary>

If you propose incorrectly or too early, you risk losing your bond.

</details>

<details>

<summary><strong>Can I automate participation as a proposer?</strong></summary>

Yes. Bots can be written to monitor for proposals based on predefined logic. UMA encourages automation for higher participation, but does not provide these services.

</details>

### Disputing Data

<details>

<summary><strong>How do I dispute incorrect data?</strong></summary>

Submit a dispute through the [UMA OO dApp](https://oracle.uma.xyz/) within the liveness period. Under the “Verify” tab, you can view the full list of live proposals currently in their challenge period and choose which one you want to dispute. You'll need to post a bond and the request will escalate to the DVM for resolution.

<figure><img src="/files/aTFPkD1Ds2mhgiGZRZQj" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><strong>What are the incentives for honest behavior as a disputer?</strong></summary>

When a disputer is correct, they receive half of the proposer’s bond. However, when a disputer is incorrect, they lose their bond. This encourages honest participation. Dishonest participation can result in financial losses.

</details>

<details>

<summary><strong>What are the risks of participating as a disputer?</strong></summary>

If you dispute incorrectly, you risk losing your bond.

</details>

<details>

<summary><strong>Can I automate participation as a disputer?</strong></summary>

Yes. Bots can be written to monitor for proposals and auto-dispute based on predefined logic. UMA encourages automation for higher participation, but does not provide these services.

</details>

### Voting

<details>

<summary><strong>How do I vote on disputes?</strong></summary>

To vote on disputes and earn rewards, you must stake $UMA in the [UMA Voter dApp](https://vote.uma.xyz/). From there, you can commit and reveal your votes. You cal also view upcoming votes, your $UMA rewards, and voting history.

<figure><img src="/files/LOEXM392DjDTZLAkl4fg" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><strong>What is the minimum UMA token amount needed to vote?</strong></summary>

There is no minimum. Your influence is proportional to the amount of tokens staked, but participation is open and inclusive.

{% hint style="info" %}
You need to stake a minimum of 1000 $UMA to receive voting gas rebates.
{% endhint %}

</details>

<details>

<summary><strong>How does DVM voting work?</strong></summary>

DVM voting uses a commit-reveal system:

1. **Commit**: Submit a hashed vote (24 hour window).
2. **Reveal**: Later, reveal your vote to show your answer (24 hour window).
3. **Resolve**: Rewards and slashing is based on voting accuracy.

Votes are shielded, meaning that voters cannot see each other's votes until after the reveal phase starts.

</details>

<details>

<summary><strong>How do voting rewards work?</strong></summary>

Rewards are paid in $UMA tokens. When you stake $UMA in the [UMA Voter dApp](https://vote.uma.xyz/), you start earning rewards automatically. Your staked tokens receive a share of UMA emissions, displayed as an APY in the dApp (\~17%).

Disputes come through the dApp in voting rounds, in which you are responsible for participating. If you vote incorrectly or miss a vote, you lose 0.1% of your staked $UMA per incorrect vote. If you vote accurately, you earn extra rewards in proportion to your staked $UMA. These rewards are collected from inaccurate and absent voters, and are redistributed to accurate voters at the end of each voting round.

**Note**: You must successfully commit and reveal your vote on time in order for your vote to count.

*For more info: read our full* [*Voting FAQ*](https://blog.uma.xyz/articles/uma-voting-faq-vote-and-earn-like-a-pro)*.*

</details>

<details>

<summary><strong>How is the correct answer decided in UMA’s voting system</strong></summary>

In UMA’s DVM, the correct vote is determined by majority consensus. During the voting process, $UMA token stakers participate in a shielded commit-reveal scheme to cast their votes on disputed assertions. After the reveal period, token-weighted votes are tallied, and if any single voting option reaches the consensus threshold (currently 65% of staked UMA), it is considered the correct outcome. This reflects the collective judgment of informed participants, incentivized to vote accurately through rewards and penalties.

Note that UMA uses the **Schelling Point** principle: the assumption that rational, well-informed voters will independently converge on the same answer. There’s no single source of truth, just the consensus of honest participants acting in their own best interest.

</details>

<details>

<summary><strong>How much can I earn for voting correctly?</strong></summary>

Voter APYs typically range between 16% and 21%, depending on emissions and participation. If you restake your rewards, you can capitalize on compound interest. Additionally, $UMA penalties from inaccurate and absent voters are redistributed as extra rewards to accurate voters at the end of each round.

</details>

<details>

<summary><strong>What happens if I vote incorrectly?</strong></summary>

Your staked $UMA tokens are slashed whenever you vote incorrectly. You lose approximately 0.1% of your staked amount of $UMA per vote.

</details>

<details>

<summary><strong>What happens if I miss a vote?</strong></summary>

Your staked $UMA tokens are slashed whenever you miss a vote. You lose approximately 0.1% of your staked amount of $UMA per vote

</details>

<details>

<summary><strong>What is P4?</strong></summary>

P4 is the voting option for when a proposal is submitted too early.

Many proposals get disputed for being too early. For example, someone may submit the Eagles as the winner of the Super Bowl five minutes *before* the game ends. Even if the Eagles end up winning, they weren’t the winners at the time the proposal was submitted, therefore, the proposal is too early. In these situations, you should vote P4.

Always make sure to check the proposal timestamp and take it into consideration when voting.

*Learn more about P4* [*here*](https://blog.uma.xyz/articles/what-is-p4)*.*

<figure><img src="/files/hZdk7C1HH1M9RK9kk7zz" alt=""><figcaption></figcaption></figure>

</details>

<details>

<summary><strong>Voting gas is expensive. Do I get a refund?</strong></summary>

Yes! As long as you are staking a minimum of 1000 $UMA, you will receive an ETH gas rebate at the end of each month paid in full, gwei for gwei. See more details [here](https://docs.uma.xyz/using-uma/voting-walkthrough/voting-gas-rebates).

</details>

<details>

<summary><strong>Are there community discussions when voting on disputes?</strong></summary>

Yes! You can find live discussions in the [UMA Voter dApp](https://vote.uma.xyz/) and in the [UMA Discord server](https://discord.com/invite/39GFUAk5bA) in the #evidence-rationale channel.

</details>

<details>

<summary><strong>What are the incentives for honest behavior as a voter?</strong></summary>

Incorrect voters are slashed, losing 0.1% of their staked $UMA per incorrect vote. Correct voters receive the slashed $UMA as a reward (in addition to their emissions APY). Therefore voters are incentivized to vote with the majority. Since votes are committed in secret, voters cannot copy other voters, and instead vote for the best answer where other voters will congregate.

</details>

<details>

<summary><strong>What prevents a consensus of stakers corrupting an oracle result?</strong></summary>

Simply put, economic incentives. All voters have staked $UMA with a one week lock up. If they corrupt an oracle result, the value of their staked $UMA will fall drastically before the can unstake and sell. This aligns voters with the long-term success of UMA and the integrity of the Optimistic Oracle.

</details>

<details>

<summary><strong>What are the risks of voting?</strong></summary>

If you vote incorrectly or miss voting rounds, you lose approximately 0.1% of your staked amount of $UMA per vote.

</details>

<details>

<summary><strong>Can I automate participation as a voter?</strong></summary>

Yes. Bots can be written to cast votes based on predefined logic. UMA encourages automation for higher participation, but does not provide these services.

</details>

***

## Technical Questions

<details>

<summary><strong>What is intersubjective data</strong></summary>

Intersubjective data is information that may not be purely objective, but is still publicly verifiable and widely agreed upon by informed observers. It’s the sweet spot between “hard facts” and “personal opinion.” Things like election outcomes, sports results, or whether a protocol got hacked fall into this category.

</details>

<details>

<summary><strong>What is a liveness period, and how is it determined?</strong></summary>

The liveness period, also known as the “challenge period”, is a window of time after a data proposal during which someone can raise a dispute. This duration is configurable and often depends on the use case’s sensitivity or urgency.

The most common liveness period ranges from **15 minutes to 2 days**. Many prediction markets, like Polymarket, use this configuration. Across Protocol also uses a 2-hour liveness period because it best suits its crosschain interop use case.

</details>

<details>

<summary><strong>What chains does UMA support?</strong></summary>

For the most up-to-date list of supported networks, including testnets and partially supported chains, you can find UMA's [network information here](/resources/network-addresses).

</details>

***

## Security

<details>

<summary><strong>How secure is UMA’s economic model?</strong></summary>

UMA’s security comes from game theory, not just code. The protocol is designed so that honest behavior is always the most profitable strategy and dishonest actions are economically irrational.

At the core of this is UMA’s use of the **Schelling Point principle**: rational voters, acting independently, are incentivized to report the truth because they know others will too. Those who vote correctly earn rewards. Those who vote incorrectly or don’t vote at all are penalized via slashing.

For an attacker to corrupt UMA’s oracle, they’d need to acquire **65% of all UMA tokens**, then convince a majority of that voting power to agree on a false outcome. Not only would this be prohibitively expensive, but it would also damage the value of the token they control.

This structure creates a powerful economic feedback loop:

* Truthful participation is rewarded.
* Dishonesty is costly.
* Corruption is uneconomical.

That’s how UMA turns incentive design into a security system at scale.

</details>

<details>

<summary><strong>How secure is the DVM?</strong></summary>

To corrupt the DVM, an attacker would need to acquire 65% of all UMA tokens and convince a majority of voting power to support a dishonest resolution. This would be extremely costly, and would cost far more than the potential upside of submitting incorrect data.

</details>

<details>

<summary><strong>What security audits has UMA undergone?</strong></summary>

At UMA, we take security seriously. The protocol has undergone multiple audits by reputable firms. Notably, OpenZeppelin conducted a comprehensive audit of UMA's Across V2 contracts, providing insights and recommendations to enhance security. Additionally, UMA maintains a bug bounty program to encourage responsible disclosure of vulnerabilities. Detailed information about UMA's security audits and bug bounty program can be found in the.

</details>

<details>

<summary><strong>How does UMA handle potential oracle manipulations or attacks?</strong></summary>

UMA employs a combination of economic incentives and community consensus to mitigate the risk of oracle manipulation:

* Participants proposing data are required to post bonds, which they forfeit if their data is successfully disputed. On the other hand, they earn rewards for proposing accurate data.
* Disputers must also post bonds, which they forfeit if their dispute is deemed inaccurate by the DVM. On the other hand, they earn rewards for disputing inaccurate proposals.
* Voters stake $UMA, which can be slashed if they vote inaccurately or are absent for voting rounds. On the other hand, they earn staking rewards for participating and voting accurately.
* $UMA stakers participate in securing the DVM by voting on oracle resolutions. While small slashing and reward mechanisms exist (\~0.1% of stake), the real security comes from the economic incentive to protect the system. If the DVM were corrupted, UMA’s value would likely collapse, meaning attackers would lose far more than they could gain.

This economic structure creates a financial deterrent against dishonest behavior. The system is built in such a way where honesty is profitable and lying is expensive.

</details>

***

## Operational Edge Cases

<details>

<summary><strong>What happens if a vote in the DVM is inconclusive or tied?</strong></summary>

In the rare event of an inconclusive or tied vote within the DVM, the dispute is rolled over into the next voting round.

A rolled vote happens when a dispute doesn’t get resolved in the current voting round and gets pushed to the next one. This only happens if the vote doesn’t meet two quorum thresholds: **GAT** and **SPAT**.

* **GAT (God Awful Threshold)** is a fixed minimum amount of UMA that must vote for the result to count. In DVM 2.0, that number is 5 million UMA.
* **SPAT (Schelling Point Activation Threshold)** is a new rule in DVM 2.0. It requires at least 65% of staked $UMA to vote *and* agree on the same outcome.

If either threshold isn’t met, the vote rolls into the next round. It's designed this way to make sure there's both broad participation and meaningful agreement before a dispute is finalized.​

</details>

<details>

<summary><strong>How does UMA handle network congestion or high gas periods?</strong></summary>

UMA is built to handle network congestion by operating across multiple L2s like Arbitrum and Optimism, reducing gas fees. On top of that, upgrades like **UMIP-185** improve efficiency by letting proposers use hashes or pointers instead of submitting full data onchain, cutting gas costs further. UMA also offers gas rebates for voters, so participation stays affordable even in high-fee environments.

</details>

***

## Governance

<details>

<summary><strong>How is UMA governed?</strong></summary>

UMA is governed by its community of $UMA tokenholders.

Major protocol upgrades, parameter changes, and funding decisions are proposed, discussed, and voted on by the UMA community, primarily through offchain Snapshot votes and confirmed onchain when needed.

Proposal discussions are hosted on [UMA’s Discourse page](https://discourse.uma.xyz/top?period=all).

</details>

<details>

<summary><strong>How can I participate in UMA governance?</strong></summary>

To participate, hold $UMA, stake it to vote, and take part in governance proposals through the [UMA Voter dApp](https://vote.uma.xyz/) and [UMA’s Snapshot Space](https://snapshot.box/#/s:uma.eth). Important proposals are discussed in the UMA Discord and forum before voting begins.

</details>

<details>

<summary><strong>What kinds of decisions are made through governance?</strong></summary>

Governance decisions include protocol upgrades, parameter changes (such as inflation rates or bond sizes), budget allocations, grants, and strategic initiatives for UMA’s growth and development.

</details>

<details>

<summary><strong>How does UMA handle proposals to change protocol parameters?</strong></summary>

Changes to UMA’s core protocol, such as inflation rates, contract upgrades, or bond sizes, are governed directly by $UMA tokenholders. Proposals are typically discussed in the community forum and Discord, then voted on through the [UMA Voter dApp](https://vote.uma.xyz/), which uses the same secure commit-reveal process as oracle dispute resolution.

While Snapshot can be used for informal temperature checks, it is **not** where binding decisions happen. Final decisions are made through onchain voting via the DVM, ensuring that changes are trustlessly approved by the UMA community.

</details>

<details>

<summary><strong>What is the difference between dispute resolutions and governance votes on the Voter Dapp?</strong></summary>

Unlike dispute resolutions, voters who vote against the consensus on governance votes are not slashed. If a voter does not submit a vote on a governance vote, they are still slashed. This allows voters to vote their personal opinions without the economic disincentive of slashing.

</details>

<details>

<summary><strong>What is the difference between protocol governance and oSnap execution?</strong></summary>

Protocol governance refers to changing the UMA protocol itself (e.g. inflation rate, contract upgrades, etc.). oSnap is a tool built with the OO and used to execute DAO votes trustlessly. oSnap can be used by any DAO; the UMA DAO and many other notable DAOs currently uses oSnap.

</details>

<details>

<summary><strong>What is the role of the DVM in UMA governance?</strong></summary>

The DVM is the venue where **all binding governance votes take place**. It’s the same secure, commit-reveal voting system used to resolve oracle disputes, except instead of resolving data proposals, it’s used to approve or reject protocol upgrades, parameter changes, and funding decisions.

In UMA, the DVM is more than a dispute resolution system. It’s the foundation of onchain governance. Every major decision made by the DAO goes through the DVM, giving $UMA tokenholders full control over the protocol’s future.

</details>

***

## Tokenomics

<details>

<summary><strong>What is the $UMA token used for?</strong></summary>

$UMA is the core utility and governance token of the protocol. It’s used to stake, vote, and secure the oracle. Tokenholders help verify data, resolve disputes, and steer the protocol’s direction by participating in governance. In short: UMA aligns incentives around truth.

The value of the $UMA token is directly tied to its role in maintaining honest oracle outcomes and decentralized governance.

</details>

<details>

<summary><strong>What is the total supply and inflation schedule of the UMA token?</strong></summary>

The supply of $UMA isn’t fixed. It’s dynamic and governed by the DAO. The protocol uses controlled inflation to incentivize participation and long-term security. The current inflation rate is set at **0.05% of the total token supply**, distributed to active, correct voters in the DVM.

*Learn more about the $UMA token supply* [*here*](https://blog.uma.xyz/articles/uma-token-supply-disclosure)*.*

</details>

<details>

<summary><strong>How are new UMA tokens distributed?</strong></summary>

Token emissions are controlled by governance. UMA emissions are used to provide staking rewards to stakers who vote on disputes to secure the protocol.

</details>

<details>

<summary><strong>Where can I track UMA's token metrics?</strong></summary>

The current supply of $UMA and emission rates are defined onchain and can be found [here](https://etherscan.io/address/0x7b292034084A41B9D441B71b6E3557Edd0463fa8#code)**.**

</details>

***

## Community and Ecosystem

<details>

<summary><strong>How can I contribute to UMA beyond voting?</strong></summary>

There are tons of ways to get involved! You can build integrations, write code, create content, improve the docs, join governance discussions, or just hang out in the [UMA Discord](https://discord.gg/uma) and vibe with the community. Whether you're technical or not, there's space for you to contribute.

</details>

<details>

<summary><strong>What community resources exist?</strong></summary>

Here’s where you can plug into UMA’s community and stay up to date:

* [UMA Discord](https://discord.gg/uma) – Join conversations, ask questions, or just vibe.
* [UMA Discourse](https://discourse.uma.xyz/) – Home of governance discussions and funding proposals.
* [UMA Snapshot Space](https://snapshot.box/#/s:uma.eth) – Where tokenholders vote on proposals.
* [UMA Docs](https://docs.uma.xyz/) – Developer documentation, guides, and protocol deep dives.
* [UMA Blog](https://blog.uma.xyz/) – Insights, announcements, and ecosystem updates.

</details>


# How does UMA work?

UMA is an optimistic oracle that resolves wide-ranging requests for information in a scalable and reliable way. The system has two components: the optimistic oracle and a dispute resolution mechanism called the DVM. The optimistic oracle resolves the vast majority of requests, currently 99.8%, quickly and cheaply, and escalates the others to be resolved through dispute resolution.

<figure><img src="/files/1qxnbA1kKoi8XgMXjn2c" alt=""><figcaption></figcaption></figure>

### Optimistic Oracle

The optimistic oracle resolves discrete requests for information using proposers and disputers who are economically incentivized to participate honestly.

#### Requests

Discrete data requests are posted to the oracle and include:

* Detailed instructions on how the request should be resolved at some time in the future. For example: “This request will resolve YES if John Doe wins the election, or NO if John Doe does not win.”
* A proposer reward for correctly proposing the request
* A bond amount that proposers must risk to propose the request
* A minimum challenge period duration during which pending proposals can be disputed

#### Proposals

Third-party proposers review open requests and propose resolutions when their resolution criteria are satisfied. Each request can only be proposed once. A proposal includes:

* A proposed resolution to the request, such as YES
* A proposer bond that is refundable if the proposal is deemed correct

The proposer reward incentivizes proposers to participate. The proposer bond incentivizes proposers to post only correct proposals.

Proposals are considered pending for a challenge period during which anyone can dispute the proposal. If no dispute is sent during the challenge period, the proposal is settled as correct, and the proposer receives the reward and the refunded proposer bond.

#### Disputes

Disputers review proposals during the challenge period for correctness. If they find an incorrect proposal, they submit a dispute that includes:

* An assertion that the proposal is incorrect
* A dispute bond that is refundable if the proposal is deemed incorrect

Each proposal can only be disputed once. Correct disputes are rewarded with a portion of the forfeited proposer bond. This incentivizes disputers to review proposals. The dispute bond incentivizes disputers to make only correct disputes.

Disputes are forwarded to the DVM for dispute resolution. After the DVM has resolved a dispute, the proposal and dispute bonds are settled.

### Dispute Resolution via DVM

The Data Verification Mechanism, or DVM, resolves disputes sent to it by UMA’s optimistic oracle. The DVM is a Schelling point mechanism where UMA stakers resolve disputes in return for staking rewards. Stakers commit secret votes during a 24-hour commit period and reveal them in the following 24-hour reveal period.

Disputes resolve when a minimum 65% majority of staked UMA is cast in favor of a single outcome. Stakers who did not vote, or who voted against the majority, are slashed, with the slashed amount redistributed to the majority voters. Slashing and rewards incentivize stakers to vote for the most correct answer. Casting votes in secret prevents lazy voters from copying other voters and dishonest voters from coordinating on an incorrect result.

Unstaking UMA requires waiting a one-week period before the tokens are released. If the DVM were successfully corrupted by 65% of stake voting against reality, the UMA token would depreciate significantly in the following week, resulting in a loss for the attackers. This creates a cost of corruption that disincentivizes corrupting the oracle.


# How does UMA resolve prediction markets?

The flexibility of UMA makes it a natural fit for resolving prediction markets which serve a wide range of markets with different resolution requirements

The major prediction markets that UMA serves integrate UMA in the following way:

#### Market Lifecycle

An oracle request including the market rules is made at the time of market creation, the request proceeds through the oracle lifecycle as described in [How does UMA work?](/protocol-overview/how-does-umas-oracle-work), and the market resolves as per that request unless one of the below cases occurs:

* if the first request for a given market is disputed, a 2nd request is made with the same rules. The settlement of the prediction market then ignores the 1st request and only resolves as per the 2nd request. If the 2nd request is disputed, the market will resolve as per the 2nd request. This acts as a form of escalation and prevents minor one-time proposer or disputer errors from slowing down the market resolution process with dispute resolution.
* if a market's 2nd or greater oracle request resolves as TOO EARLY (aka P4, learn more [here](https://blog.uma.xyz/articles/what-is-p4)), a subsequent oracle request is created and the market stays open and resolves as per the latest request. &#x20;

#### Rule Clarifications and Updates

The market rules specify how the integration can provide updates or clarifications to the rules in between market creation and settlement

#### Proposer Whitelist

Requests use the [ManagedOptimisticOracleV2](/developers/managedoptimisticoraclev2) to specify a proposer whitelist of addresses that are able to propose the request. Learn more at [Default Proposer Whitelist](/using-uma/default-proposer-whitelist).

#### Challenge Period Extensions

Requests that use the [ManagedOptimisticOracleV2](/developers/managedoptimisticoraclev2) specify a minimum challenge period. Risk Labs operates an automated proposal review system. Flagged proposals have an extended challenge period that allows additional time for all UMA participants to review and potentially dispute the proposal. The challenge period extension is not a fixed length and is ended by Risk Labs market review team.&#x20;

This feature allows challenge periods to be right sized to the proposal. For example, an unflagged sports game proposal can safely resolve after a short minimum challenge period (e.g. 15 minutes) and complex or subjective geopolitics proposal can be extended past the default 2 hour minimum challenge period.


# Default Proposer Whitelist

Risk Labs manages the default proposer whitelist for the ManagedOptimisticOracleV2 contract. The current whitelist can be viewed [here](https://polygonscan.com/address/0x9f35885ce8f67a942d7b2f4fbf937987da08c463#readContract#F1) and is subject to Risk Labs' discretion and changes to the below guidelines.

Note: the whitelist is not used to restrict proposals to known or trusted actors. The whitelist is used to only allow proposers with a history of accurate proposals. This keeps UMA's optimistic system efficient and prevents incorrect proposals from slowing down UMA settlements.&#x20;

### Current Proposer Whitelist Criteria

Proposer addresses must meet the two below criteria over the last 6 months:

* 5 or more Polymarket or Predict.fun proposals on OptimisticOracleV2 or ManagedOptimisticOracleV2
* Proposal accuracy greater than 95% in the last 6 months.

Proposers who base their proposal timing on other proposer's transactions in the public mempool (i.e. front run) will be removed from the whitelist.

### How do I get on the whitelist?

By proposing [OOV2 Polymarket requests](https://oracle.uma.xyz/propose?oracleType=Optimistic+Oracle+V2\&project=Polymarket) that are not whitelisted to meet the criteria for the next update.

### Who is on the whitelist? How many address are on the whitelist?

The current whitelist can be viewed [here](https://polygonscan.com/address/0x9f35885ce8f67a942d7b2f4fbf937987da08c463#readContract#F1)

### When will the whitelist be updated?

Updates are based on data pulled on the 2nd of each month for the preceding 6 months (e.g. June 2nd to December 2nd). The whitelist is updated within the following week.

### Are addresses removed from the whitelist?

Yes, if a whitelisted address no longer meets the criteria of the most recent update it will be removed from the whitelist.


# Proposing via the Oracle Dapp

Viewing, proposing and disputing ManagedOptimisticOracleV2 requests on the Oracle Dapp is the same as other OptimisticOracleV2 requests except for the following differences:

* If your connected address is not on the request's proposer whitelist, the propose button on the request's sidebar will be disabled.

<figure><img src="/files/S6EXkcYXKx7wHnyV96J6" alt=""><figcaption></figcaption></figure>

* The request's proposer whitelist is displayed under the "More Information" heading at the bottom of the request sidebar.

<figure><img src="/files/sjf0z6qBtd66BZfiNrwG" alt=""><figcaption></figcaption></figure>

* ManagedOptimisticOracleV2 requests are tagged "Managed Optimistic Oracle V2" on the Oracle Dapp.

<figure><img src="/files/XIaCuDLmNZ1WuYOTlEGo" alt=""><figcaption></figcaption></figure>


# Proposing Programmatically

Proposing and disputing ManagedOptimisticOracleV2 requests is largely the same as OptimisticOracleV2 requests with the following differences:

#### Adding the ManagedOptimisticOracleV2 Addresses

Programmatic proposers and disputers will be used to listening for OptimisticOracleV2 events and sending proposing and disputing transactions to these addresses. To add support to new ManagedOptimisticOracleV2 deployments, programmatic proposers and disputers will have to add these new deployment addresses to their bot configs.

The ManagedOptimisticOracleV2 deployment can be found [here](https://polygonscan.com/address/0x2C0367a9DB231dDeBd88a94b4f6461a6e47C58B1).

#### Viewing a Request's Proposer Whitelist

ManagedOptimisticOracleV2 requests have defined proposer whitelists. The whitelist for a given request can be found by calling the [`getProposerWhitelistWithEnforcementStatus`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol#L381C14-L381C55) function with the request's `requester` , `identifier` , and `ancillaryData`. The function will return an `isEnforced` boolean that defines whether a whitelist is enforced and an address array of `allowedProposers` . If `isEnforced` is true, then only `allowedProposers` may propose the request. If `isEnforced` is false, any address will be able to propose.

When a new request is created on ManagedOptimisticOracleV2 it will default to the `defaultProposerWhitelist` unless overridden by the `requestManager` . This default whitelist can be viewed with the following steps:

1. Call the [`defaultProposerWhitelist`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol#L94) view function on a deployed ManagedOptimisticOracleV2 contract to get the address of the default whitelist.
2. Call [`getWhitelist`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/common/implementation/AddressWhitelist.sol#L79) on the default whitelist address to view all address on the whitelist. Alternatively, call [`isOnWhitelist`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/common/implementation/AddressWhitelist.sol#L67) to check if a given address is on the whitelist.

#### Proposing on ManagedOptimisticOracleV2

Proposing on ManagedOptimisticOracleV2 can be done by calling the same [`proposePrice`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L382) or [`proposePriceFor`](https://github.com/UMAprotocol/managed-oracle/blob/fc03083eca91c880efa8918c6d9532af9362f00d/src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol#L327) functions with the same arguments as proposing on OptimisticOracleV2. Proposal transactions from addresses that are not whitelisted will revert with the following error messages, `"Sender not whitelisted"` or, `"Proposer not whitelisted"`.

#### Listening for ManagedOptimisticOracleV2 Events

The emitted events that are relevant to oracle participants (`RequestPrice, ProposePrice and DisputePrice, SettlePrice`) are unchanged from the OptimisticOracleV2 contract. These events can be queried via RPCs or by using our [subgraph](https://github.com/UMAprotocol/subgraphs/blob/master/README.md#managed-optimistic-oracle-v2-events-and-calls) that indexes ManagedOptimisticOracleV2 events.


# Disputing Via the Oracle Dapp

1. Go to the [Optimistic Oracle dApp](https://oracle.umaproject.org/), if you are disputing proposals on a live network like Ethereum, Optimism, or Polygon, or go to the [Testnet dApp](https://testnet.oracle.umaproject.org/) if you are disputing test data on Görli. Disputes will be resolved by the [DVM](https://docs.umaproject.org/protocol-overview/how-does-umas-oracle-work) if you are on a live network, or by a mock oracle if you are on Görli (see Resolving Disputes).
2. Locate proposals under the Proposals tab for outstanding proposals that are within the challenge window and can be disputed.
3. Click on the proposal you want to dispute.
4. Click the 'Connect wallet' button in the top right corner and go through the steps to connect your wallet. Confirm you are on the same network as the proposal.
5. Before disputing, confirm the details of the request and ancillary data to ensure the proposal is actually incorrect. You may also want to check the instructions in the [UMIP](https://docs.umaproject.org/resources/approved-price-identifiers) for the identifier.
6. Click the 'Dispute Proposal' button.
7. Confirm the transaction details through your wallet provider. After confirming, the proposal will be disputed.
8. If you are on a live network, the dispute will escalate to the DVM on Ethereum for resolution. If you are on Görli, you will need to manually resolve the dispute through the [Mock Oracle](https://goerli.etherscan.io/address/0x20570e9e27920ac5e2601e0bef7244deff7f0b28#code).


# Voting

How to stake tokens, vote, and claim rewards

This tutorial describes how to vote using the [UMA voter dApp](https://vote.uma.xyz/). For context, each voting period is 48hrs. Voting takes 3 steps:

* Commit Vote: the first 24hrs of a voting period allows you to encrypt and commit your vote
* Reveal vote: the second 24hrs of a voting period allows you to decrypt and reveal your vote. Votes are tallied by a DVM smart contract at the end of the reveal period.
* Claim rewards: staking rewards are constantly accrued. You can claim + restake or just claim them to your wallet whenever you like.

The purpose behind using the [Commit/Reveal scheme](https://www.gitcoin.co/blog/commit-reveal-scheme-on-ethereum) is that it allows votes to remain private.

### Instructions:

1. Go to <http://vote.uma.xyz>.
2. Connect your wallet.
3. In order to vote, you must stake your tokens. Click "Stake/Unstake" then follow the instructions in the module. Staking your tokens does three things:
   * Your tokens will immediately start to earn a prorated portion of the $UMA token emissions, which is reflected as an APY on the dapp.
   * You will be able to vote with those tokens
   * You will have a 7-day cooldown period in order to unstake those tokens. During that period, your tokens will not be earning any rewards or be eligible for voting.

<figure><img src="/files/tH4vUuwifKirb00p6f2M" alt=""><figcaption><p>Indicate that you understand the cooldown period, approve, then stake your $UMA</p></figcaption></figure>

4. You can only vote when there is a live vote(s), and during the **Commit period.**
5. Choose your vote(s) from the dropdown menu, then click "Commit vote" and send the transaction.
6. Each voting round consists of a 24-hour commit period, followed by a 24-hour reveal period. **In order for your vote(s) to count, you need to return and reveal your vote(s) during the reveal period.**
7. During the reveal period, return to the Dapp and choose "Reveal" to reveal your vote(s).

Once the dapp shows your vote as "Revealed," you are done with the voting process. The vote(s) will be finalized when the reveal period ends, at which point you will be able to see if you voted correctly. If you did, you will receive a bonus. If you voted inaccurately or failed to vote, you will have a penalty applied to your stake. This penalty amount is redistributed to accurate voters.

**Protocol Parameters (adjustable by governance)**

Emissions: .18 $UMA/second

Missed vote penalty: 0.1% of staked balance

Withdraw cooldown period: 7 days

### Claim rewards

Emission rewards begin to accrue automatically when you stake $UMA. You may claim these rewards to your wallet, or claim-and-stake to your staked balance.

Rewards associated with voting successfully are automatically added to your staked balance after the voting period ends.<br>


# Voter Guide

Voting best practices and warnings

* Read through the vote Description under the Details tab. Carefully review how the vote should resolve and if provided, the timing of when the request can resolve and any listed sources.
* Review the Timestamp under the Details tab. This is the point in time at which the vote should be evaluated.
* The vote Discussion tabs lists comments from the UMA Discord with arguments, resources, and suggested votes that may be helpful. **However, some commenters may try to influence the vote for their own profit. Always ​independently verify​ any claims before relying on them.**
* **For prediction market votes, it is helpful to review the market odds over time to sanity check your vote.** The market odds after an event has expired are a reflection of how bettors with skin in the game think a market should resolve. You should not vote based on the market odds, but if you are planning on voting against strong market odds you may want to do extra research to ensure your vote is correct.


# Voting Gas Rebates

Risk Labs rebates the Ethereum gas fees voters pay to vote to encourage more participation in securing the Optimistic Oracle. Voters with ≥ 1,000 UMA staked are eligible.

**Voter eligibility:**

* Voters must have at least 1000 UMA staked at the start of a commit phase for that round’s commit and reveal transactions to be eligible.
* Starting June 15th, 2026 delegates that are part of a voting pool will not be considered for gas rebates.
  * *Rationale*: Risk Labs operates the gas rebate program to encourage more users to participate in voting to resolve disputes. Therefore delegates that are operated on behalf of a voting pool do not meet the intent of the program.

**Rebate Payments**

* Rebates are calculated for one calendar month and are sent out within the first half of the following month. Announcements are made in the UMA Discord at the time each rebate is sent out.
* Rebates are made in ETH.
* Commit or reveal transactions sent via a delegate will be rebated to to the delegate address.

**Transaction eligibility:**

* Rebates cover commit and reveal transactions.
  * Committed votes must be revealed to be rebated.
  * If a voter commits more than once on a single vote, only the first commit will be rebated.
* Rebates do not cover token approval, stake/unstake, or rewards claiming transactions.

**What is covered:**

Rebates are calculated according to the two components of Ethereum gas fees:

* Base Fee: a network fee that is determined by block congestion. The Base Fee is calculated using a base price (gwei per gas) that is the same for all transactions within a given block.
  * Rebate coverage: full Base Fee with no cap.
* Priority Fee: an extra tip based on the max priority fee price set by the transaction sender. This tip determines how quickly a transaction is included onchain.
  * Rebate coverage: **Priority fees will only be rebated up to a price cap of 0.001 gwei per gas.** Eligible transactions that use a higher priority fee price will only be rebated up to 0.001 gwei per gas.\
    Note: the voting dapp suggests a priority fee of 0.001 gwei when sending a commit or reveal transaction to your wallet. Most wallets use this suggested fee as a default. Please review your wallet's gas fee settings when voting.

**Reviewing your wallet's Priority Fee price:**

*Note: the below tutorial uses the desktop Metamask browser extension. UX will vary by wallet.*

* When reviewing a commit or reveal transaction sent to your wallet by the voter dapp, your wallet should default to gas prices suggested by the voter dapp. This is shown by the red markup in the below screenshot. These suggestions will set the priority fee to 0.001 gwei per gas which will be fully rebated.
* If you want to change these settings or verify the actual gas settings used, click the edit symbol (yellow markup in screenshot below).

<figure><img src="/files/3kg0vRkHemDJ0ZrNlV5x" alt=""><figcaption></figcaption></figure>

* This will display different preset gas fee options (see screenshot below). Hover over the tooltip to see the gas price details for each option. In the below screenshot, the site suggested gas fee settings use a Priority Fee price of 0.001 gwei per gas.
* Click "Advanced" to set your own custom gas settings.

<figure><img src="/files/KyrKFfrTAKEvMUgitwfE" alt=""><figcaption></figcaption></figure>

* Here you can set custom gas prices. To be rebated for all gas spent, ensure the Priority Fee is not greater than 0.001 gwei.

<figure><img src="/files/6dXKgOpBUatyOjZUtoxj" alt=""><figcaption></figcaption></figure>


# Discord Summaries

LLM generated Discord discussion summaries warnings and information

The Voterdapp contains a "Discussion Summary" tab with an LLM summary of Discord comments on the vote. The summary removes duplicate arguments across all comments to give voters an overview of all arguments which is useful for long or spammed comment threads. The summary does not fact check comments, add additional arguments, or give voting recommendations.

Voters should always verify all arguments included in the summaries with their own research. The raw comments are also provided in the "Discord Comments" tab so voters can review them against the summary in case of LLM error.


# DVM 2.0

The UMA DVM is the arbitrator for the Optimistic Oracle and other UMA ecosystem contracts. It facilitates dispute resolution wherein UMA token holders vote in a commit reveal schelling point mechanism. For an overview of how the DVM see [here. ](https://docs.umaproject.org/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism)The DVM has been rebuilt with the new iteration being released in Q1 of 2023.

At a high level, the upgrade adds a new staking and slashing mechanism wherein voters earn a pro-rata share of emissions for staking and are slashed for voting wrong (or not voting). This upgrade also reworks a number of other DVM contracts such as the governor, proposer contract and a new designated voting contract. Finally, this upgrade adds a new emergency recovery mechanism to the DVM that enables admin proposals to bypass the DVM's schelling point oracle system in the event of an emergency. More detail on the individual changes are broken down in the sections that follow.

This doc page outlines some of the changes between DVM 1.0 and DVM 2.0 and some other relevant implementation details.

### Mechanism changes & UMA tokenomics update

The DVM2.0 introduces three key changes to how the UMA token interacts with DVM system:

1. Voters now must **stake UMA in the DVM** to participate in the schelling point and to receive rewards.
2. Voters now **earn a pro-rata share of a fixed emission rate** simply for staking their tokens. Staking rewards are emitted at a constant rate per block. This means you can work out the overall UMA inflation over time and stakers can easily work out their APY for staking in the system. In comparison to the previous inflation system, the total inflation rate is no longer a factor of the number of votes held by the DVM. The pro-rata share of tokens received by stakers is independent of their voting performance. The emissions rate is set through UMA governance.
3. Voters' staked balances are also now **susceptible to slashing**. The slashing mechanism redistributes tokens from inactive and wrong voters to the stakers who voted correctly. This hardens the schelling point by adding a more punitive cost function to being wrong. Governance votes are treated as a special price request category where the slashing mechanism is not applied.

### Vote Delegation

First class vote delegation support enabling a 1 to 1 relationship between the delegator and delegate wallets. This lets a voter delegate from a secure cold storage wallet to a hot wallet. The hot wallet can commit, reveal and claim and re-stake their rewards but cannot access the underlying stake. If desired, more complex delegation systems (pooled UMA staking with delegate voting etc) can be built on top of this externally to the core UMA contracts.

### Emergency recovery system

The DVM 2.0 now has an emergency recovery mechanism where bonded emergency proposals can be executed to short-circuiting and bypass the normal voting mechanism. This enables the DVM to recover from any kind of internal failure mode that could occur (breakage in the commit reveal system, contract issues or other) through a permissionless upgrade flow.

### Other system wide changes

There are a number of other changes made, including:

1. Governance proposals now include ancillary data enabling better identifying information to be passed along to voters.
2. Price requests now contain a unique identifier, enabling easier tracking and support in front ends.
3. A number of gas optimizations were made throughout the protocol.

### Implementation details

The sections below contain some details on nuanced implementation details of the DVM2.0 system.

#### Staking APY

Staking rewards (and the associated APY) consists of two discrete components: a) rewards from being **staked** and b) balance changes from **slashing**. The net staking APY you receive considers both of these values. Let's consider them separately.

**Staking rewards** are a pro-rata share of a fixed emission rate, calculated on a per block basis. The rewards you are entitled to, from the last time you claimed, are calculated as follows:

$$
stakingRewards = \frac{stakedDuration*stake*emissionsPerBlock}{cumulativeStake}
$$

**Slashing rewards & penalties** at the time of DVM 2.0 launch are designed to be as simple as possible, with the ability to update them at a later point through the use of a Slashing Library. At launch, slashing penalties will be approximately equal to the rewards one would have earned for an estimated stake duration. This has been targeted based off historical DVM 1.0 vote frequency, and means that someone who stakes but does not participate in any votes (or gets all votes wrong) should have their slashed stake and accumulated rewards cancel out for a 0% APY. This amounts to losing 0.1% of your staked amount per vote that is incorrect or missed. Note that each voting round (48 hour commit-reveal cycle) can have multiple votes in it (for example multiple price requests or governance actions). The amount you earn for voting correctly is the voters pro-rata share of the slashing of the incorrect voters. This can be calculated per voter per round as follows:

$$
slashPerVoterPerRound = \sum stake \* numberOfIncorrectVotes \* 0.001
$$

Then, the total amount slashed per round is the sum of all voters slash for that round:

$$
totalSlashPerRound = \sum slashPerVoterPerRound
$$

A voter, who was correct, will then earn a pro-rater share of the totalSlashPerRound as:

$$
positiveSlashForCorrectVotePerRound=\frac{totalSlashPerRound\*stake}{cumlativeCorrectVoteStake}
$$

A staker's net APY is therefore the sum of their `stakingRewards` and `positiveSlashForCorrectVotePerRound` annualized over a 1 year period.

#### Rolled votes

Under some situations votes can "roll". We define a rolled vote as a vote that was not resolved in one vote round and was moved into the subsequent voting round due to not successfully reaching the two types of needed quorums. These quorums are known as GAT (God Awful Threshold) and SPAT (Schelling Point Activation Threshold).

The GAT within the DVM 2.0 is a constant amount of UMA that must vote on any given vote for it to not roll. In the DVM 1.0, this was set as a percentage of circulating UMA. At the time of DVM 2.0 initial deployment, the required GAT per vote is a constant 5 million UMA.

The SPAT is a new concept from the DVM 2.0. It is a percentage of staked tokens that must vote and agree in order for a vote to not roll. The required SPAT is 65% currently of staked tokens.

If a vote does not satisfy both of these constraints, it will roll.

#### Vote Deletion

There are certain situations in the DVM 2.0 where voters may want to delete votes. A good example of this would be in the case of spam deletion, where needless price requests are submitted in order by an attacker to try to create some sort of desired slashing conditions.

In the DVM 2.0, votes are deleted once they have rolled a certain number of times. So if voters choose to not vote on the resolution of price requests, and either the GAT or SPAT are not met for a set number of voting rounds, those price requests will become deletable. At the time of DVM 2.0 initial deployment, the number of times a vote is rolled before being deleted is 4.


# DVM 2.0 FAQ

Frequently asked questions about the new UMA DVM 2.0

### What kind of APY can I expect to receive for staking?

To learn more about how exact APY is calculated, you can refer [here](/using-uma/voting-walkthrough/dvm-2.0#staking-apy).

At a high level though, all UMA voters on average will receive an APY determined by the annual UMA emissions amount divided by the average total UMA staked over the year.

Before the DVM 2.0 upgrade, approximately 18-20mm UMA was voting on average for each vote. If this holds relatively constant, it means that average UMA voter APYs will be approximately 28-32%. This can of course fluctuate on an individual voter basis dependent on voter performance.

### How do I maximize my APY?

Maximizing your APY should be simple. To maximize APY you should:

* Remain staked within the system.
* Claim and restake your rewards at times when it makes sense based off balancing your increased stake’s additional future rewards received against gas costs of claiming and restaking.
* Vote consistently and carefully. Incorrect or absent voter stakes are slashed and distributed to correct / participating voters after each voting round.

### What is the current UMA voter rewards emission rate?

The UMA emission rate is currently 0.155 UMA/second. This will be distributed pro-rata to all stakers within the UMA system on a per second basis. The UMA emissions rate is controlled by UMA governance and can be updated at any time by a DVM vote.

### What is the unstake timer? Why does this exist?

The unstake timer is a set amount of time that a staker must wait before they can execute a request to unstake from the UMA system. With the initial DVM 2.0 upgrade, this unstake timer was set to 7 days.

The unstake timer exists to make the UMA voting system more hardened against attempted manipulation attacks. UMA has always had an assumption that if a malicious attacker could manage to either vote with or bribe a majority of voters to vote incorrectly on a dispute, the UMA token would go to zero in value. This is known as the UMA cost of corruption.

In practice, this is probably not entirely true as markets are not entirely efficient. Attackers could likely dump voted with tokens for some amount of value after a successful attack. Having a required 7 hold period more strongly enforces that UMA’s cost of corruption is enforced by the market if a manipulation attempt is successful.

### What kind of things does the DVM vote on?

The Optimistic Oracle & DVM can provide all kinds of different data, from crypto prices, to sports, to verifying if a bridging transaction was valid, with new use cases being added all the time, so its hard to anticipate precisely what you might be asked to vote on, however all questions will be asking something about the state of the world at a particular time, indicated by the timestamp.

### How should I determine what I should vote?

There are two primary data sources to assist a voter in determining what value they should vote with.

First, the UMIP that the DVM request references. This will tell you general information about how that question should be answered. The second place to check is the ancillary data of the question. This will provide more context for the question and is likely to include a source, which a link that will take you to a place where that data is likely to be published.

Disputes are also discussed at length in the [UMA Discord](https://discord.com/invite/jsb9XQJ) #voting-chat and #evidence-rationale channels.

### Why do I have to commit and reveal votes?

The commit/reveal cycle prevents people from seeing how others have voted by encrypting all initial votes and only decrypting them once all votes are cast. The oracle is built on Schelling Point theory, which indicates that the most likely consensus of non-colluding participants is the truth. By obscuring how other people have voted, this ensures that each tokenholder votes independently.

### Can I vote with tokens that haven’t been staked?

No. Only tokens that have been staked can be voted with. If you have already initiated the unstaking process for any or all of your coins, they also will not receive rewards or be able to be voted with. However if you have unstaked coins when a vote starts, you can stake your coins any time up to the end of the commit period, and they will be eligible to vote with.

### Can I vote while I am unstaking?

Unstaked tokens cannot vote. This includes tokens that are in the “cooldown period” after an unstake request has been submitted.

### Can I cancel an unstake request while it is pending?

You cannot. Unstake requests cannot be canceled once submitted. If you once again wish for those tokens to be staked, you can wait for the unstake period to finish, claim your tokens and restake them.

### Can I get someone else to vote on my behalf?

It is possible to delegate your staked UMA in one wallet to a different wallet, allowing voters to keep their UMA on a cold wallet and vote with hot wallet, however there is a 1-1 relationship between voting wallets and staking wallets, therefore it is not possible for one person to vote on behalf of multiple other people.

### Do I still get gas rebates?

Yes! Risk Labs will still be continuing with the existing voting gas rebate program.

### I still don't know how this works - where can I get help?

Jump into our [discord](https://discord.com/invite/jsb9XQJ) and ask questions about anything that you are unsure of.


# Governance

The UMA token is primarily a governance token used to contribute to UMA protocol decisions, such as voting on UMA Improvement Proposals (UMIPs), price requests, and disputes made to UMA's Data Verification Mechanism (DVM).

### Governing the UMA ecosystem <a href="#governing-the-uma-ecosystem" id="governing-the-uma-ecosystem"></a>

The UMA token is an integral part of the UMA ecosystem as it guarantees the economic security of UMA smart contracts and its oracle system. The objective of the UMA token is to enable the optimistic oracle to remain secure utilizing a fully decentralized and permissionless method.

UMA's DVM is designed with an economic guarantee around the cost it would take to corrupt the oracle and the profit someone would receive. The DVM ensures the price to obtain 65% of UMA tokens is greater than the profit from corrupting the DVM, as measured by the collateral stored in UMA's financial contracts. This is achieved through an inflationary reward (currently 0.05% of total network token supply), distributed pro-rata by stake to voters who participate and vote correctly. As long as there is an honest majority, voters will vote correctly.

As the total value of collateral locked in UMA grows, the UMA token is required to increase in value to ensure the security of the DVM. To ensure this inequality holds, the DVM may charge fees to financial contracts which the DVM would use to buy UMA tokens.

### Voting <a href="#voting" id="voting"></a>

The UMA voting process requires tokenholders to commit and reveal their votes in two separate stages. Each stage is open for 24 hours, so each voting period is 48 hours.

* UMA Tokenholders can discuss their votes in the #voting channel of the [UMA Discord](https://discord.umaproject.org/) before voting
* UMA tokenholders can use the [Voter dApp](https://vote.umaproject.org/) to vote on protocol decisions

Examples of governance proposals include:

* Approving new [price identifiers](/resources/approved-price-identifiers) and [collateral currencies](/resources/approved-collateral-types)
* Price requests and disputes
* Upgrading the core DVM protocol and / or modify DVM parameters
* Registering and de-registering contract templates
* Shutting down contract instantiations (in rare circumstances)


# The UMIP Process

UMA Improvement Proposals (UMIPs) are design documents used to propose changes to the UMA ecosystem. UMIPs are intended to be the primary mechanism for proposing new features, collecting community input on an issue, and for documenting the design decisions that have gone into the UMA protocol.

UMIPs are a convenient way to track the progress of an implementation. Examples of common UMIPS include adding a new [price identifier](/resources/approved-price-identifiers) or [collateral currency](/resources/approved-collateral-types) to be supported by the DVM. UMIPs are presented to UMA tokenholders for voting through the voting dapp to determine whether they will be accepted or rejected.

UMIPs need to provide a concise technical specification of the feature and a rationale for the feature. They are modeled after [EIPs](https://eips.ethereum.org/) and [ZEIPs](https://blog.0xproject.com/0x-protocol-governance-voting-walkthrough-and-faq-3becfd57a370). See here for an [EIP template](https://blog.0xproject.com/0x-protocol-governance-voting-walkthrough-and-faq-3becfd57a370) and [ZEIP template](https://github.com/0xProject/ZEIPs/blob/master/ISSUE_TEMPLATE.md).


# DAO Proposals

The UMA DAO accepts proposals for on-chain actions that require tokenholders approval. An example would be a request for funding from the UMA treasury. The steps to complete a proposal:

**Step 1: Post to Discourse**

The first step is making a post in the UMA discourse under [non-technical UMIPs](https://discourse.umaproject.org/c/uma-protocol/non-technical-umips/41). The post should outline the key components of a proposal and give the community a chance to submit feedback and recommend changes.

An example submission is [Polymarket's Liquidity Mining Program](https://discourse.umaproject.org/t/revised-funding-request-for-liquidity-mining-program-extension-from-polymarket/1716).

**Step 2: Snapshot Vote**

When the proposal appears ready, a Snapshot vote can be created by the proposer with a 5 day close. Should it receive majority support at the end of 5 days, it will then pass to snapshot for an indicative vote prior to being put to an on-chain vote.

Proposals that require movement of treasury funds require a 4,000 UMA bond to be posted which is returned if the proposal is successful in an on-chain vote of tokenholders.

RiskLabs, the foundation which established UMA, has indicated its willingness to handle the on-chain proposal and cover the bond payment for proposals which attract majority support on a snapshot poll of tokenholders.

Here is the snapshot for [Polymarket's Liquidity Mining Program](https://discourse.umaproject.org/t/revised-funding-request-for-liquidity-mining-program-extension-from-polymarket/1716).

**Step 3: On-chain Vote**

After a Snapshot vote has successfully passed, an on-chain vote is held to transfer the funds. If the vote is successful, the funds are transferred to the recipient wallet address.


# Polymarket

How to verify Polymarket Proposals

#### What is the YES\_OR\_NO\_QUERY?

The YES\_OR\_NO\_QUERY (UMIP can be read [here](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md)) is a price identifier intended for plaintext binary questions. Requesters encode their questions in ancillary data. For a full overview of how ancillary data is formatted, refer [here](https://github.com/UMAprotocol/UMIPs/blob/master/price-identifier-template.md#ancillary-data-specifications).

The YES\_OR\_NO\_QUERY is most typically used by Polymarket today to resolve the outcome of information markets. This can come in the form of asking for the outcome of sports games, a crypto or nft price at a specific point in time or world events among many other things.

Note that this price identifier is often in the form of YES or NO, but could also be in the form of THIS or THAT - ie “did the Lakers or Clippers win the game last night” can work even though it is not really a YES or NO question. The example used in the rest of this document will illustrate how this can be done.

#### Components of Ancillary Data

From the UMIP:

When converted from bytes to UTF-8, the ancillary data should be a dictionary object containing q (question), p1, p2, p3 and p4 keys and values. p4 is optional and will only apply in certain situations.

Example:

q: title: French Open Final: Djokovic vs. Ruud, description: This market will resolve to "Djokovic" if Novak Djokovic wins the final, or to "Ruud" if Casper Ruud wins the final. res\_data: p1: 0, p2: 1, p3: 0.5. Where p1 corresponds to Ruud, p2 to Djokovic, p3 to unknown/50-50,initializer:91430cad2d3975766499717fa0d66a78d814e5c5

The UMIP goes on to note default values for p1, p2, p3 and p4.

* p1 is usually used for “NO” values and defaults to \`0\` if not explicitly assigned
* p2 is used for “YES” values and defaults to \`1\` if not explicitly assigned
* p3 is for “UNKNOWN” or “CANNOT BE DETERMINED” and defaults to \`0.5\` if not explicitly assigned.
* p4 is for situations where the question is expected to eventually be able to be evaluated, but cannot be at this time. An example would be if the outcome to a sports game was asked for, but the game has not yet happened or finished. This will be referred to as the “magic number” and defaults to the minimum int256 value or before e18 scaling: -57896044618658097711785492504343953926634992332820282019728.792003956564819968

For additional context on when the magic number or p3 are expected to be returned, from the UMIP:

p4 is intended to be used for situations where it is not a given that the price request (or contract settlement) should even occur yet. An example of this would be the UMA event-based expiry LSP. A request to settle an event-based expiry LSP can be submitted at any time but if the question can not be resolved yet it should be ignored.

The default p4 value is the minimum int256 value and is used as a "magic number" to indicate that an event-based expiry request is invalid and the contract should continue as normal. For example, if the question is related to a basketball game on January 6th and a settlement request comes in on January 5th, the question can not be resolved yet, and voters should return the p4 value with the magic number to reject the settlement request. This value also moves the decimal place 18 spaces to the left, due to the default behavior of the UMA voting interface to scale input values to 18 decimals. After scaling by the interface, the value will be -57896044618658097711785492504343953926634992332820282019728792003956564819968

Notice that a p3 value would never be returned earlier than the final price request time noted in the ancillary data or the requesting contract's expiration timestamp and a p4 value would never be returned after that point. Consider an unresolvable question like, "Was the weather nice on January 6th, 2022?" If the question was asked on January 7th, 2022, you would return the p3 value. If the same question was asked on January 5th, 2022, you would return the p4 value.

#### Scaling values

Also noted in the UMIP is the fact that return values are not scaled when referred to in the UMIP or present in ancillary data.

This simply means that if the ancillary data contains “p2:1”, this should really be returned on-chain as 1e18 (not 1 wei). So 1000000000000000000 instead of 1.

All price identifier values are treated this way in the UMA system. The UMA voter dapp and Optimistic Oracle proposer interface both perform this scaling automatically for UMA voters or proposers.

#### Understanding Question Data

When verifying a proposal, it is important to assess the question data and arrive at their own conclusion for what the return value should be. Here is the example above in the oracle UI:

![](https://lh5.googleusercontent.com/Cvtvs57QTWYnrutclqexZHRvZZXiwn1_mIS8cB8mg87x_mTZ98YJQh2F0E1vxJZd23bB-e0jqyzyvuqCrRfOM1MKEga8MNLFUKTahQ8QQC3ON2UWdlWRceoeyu47_gG0BWreae9KF1OhDdLMb9HKHaU)

Key data in the proposal is the identifier, timestamp, and the ancillaryData.

The YES\_OR\_NO\_QUERY identifier tells us which “pricing methodology” we should be referring to. This is best understood by reading the corresponding UMIP. For all price identifiers, the UMIP can be looked up [here](https://docs.umaproject.org/uma-tokenholders/approved-price-identifiers) by price identifier name.

The proposed time tells us the timestamp a value was proposed and therefore should be evaluated.

The Additional Text Data above represents the ancillary data and contains the plaintext version of the binary question that a proposer should be evaluating. It is important to read the full text data as it could also contain pricing specifications or data sources that should be used.

In the example, I can see that the question is asking if Novak Djokovic or Casper Ruud wins the French Open Final. It does not specify a data source so I can use a wide array of publicly available information to determine what I think the correct answer should be. Referring to ESPN scores:

![](https://lh5.googleusercontent.com/lHC2mPmAO3JmIBQKSa9NHcVK5aWrpKjqYiwUzZUn1QGRULH2Xo6sZ_EgUKj0vcNyTZcofkYKGNP1WTyXUInmOB0ZNUMhQcVzo-TztwR3iwaLsv1znssum3KYrVZo8N_OKA6X5t38ZicXM4EAh8Rz0Ds)

I see that Djokovic won and I should continue to read the ancillary data to determine which return value should be used in this scenario.

Ancillary data question:

This market will resolve to "Djokovic" if Novak Djokovic wins the final, or to "Ruud" if Casper Ruud wins the final.

Resolution key:

res\_data: p1: 0, p2: 1, p3: 0.5.\
Where p1 corresponds to Ruud, p2 to Djokovic, p3 to unknown/50-50

Since Djokovic won and p2 corresponds to 1, the proposed value should be p2:1.

Voters/proposers should typically evaluate all price requests independent of the party that has requested the data. But as a validation tool, the requestor can sometimes provide information that will help us verify the values that we are about to propose. As an example, this question was from <https://polymarket.com/>.

This is one simple example of evaluating a YES\_OR\_NO\_QUERY, but almost all questions follow the same format.


# Across

How to verify Across Bundles

**Step 1: Install Required Dependencies:**

git:

* Documentation: <https://github.com/git-guides/install-git>

yarn:

* Documentation: <https://classic.yarnpkg.com/lang/en/docs/install/#mac-stable>

node.js

* Documentation: <https://nodejs.org/en/download>
* Run the following command to check what version of node you are running: `node -v`\
  Make sure it is greater than 16.18.0.

redis:

* Documentation: <https://redis.io/docs/getting-started/installation/>
* Note: Follow the [relayer-v2 README](https://github.com/across-protocol/relayer-v2/tree/master) instructions

ts-node

* You can use the following to install globally: `npm install -g ts-node`

Nvm (optional)

* Documentation: <https://github.com/nvm-sh/nvm#installing-and-updating>
* Note: Optional, but nvm allows you to easily install and switch between node versions

VS Code (optional)

* Highly recommend using a code editor like VS code.

**Step 2: Clone the relayer-v2 repo:**

* If using VS code, open the terminal and run the following command:

```
git clone https://github.com/across-protocol/relayer-v2
```

**Step 3: Run Redis**

Open a terminal window and run:

```
redis-server
From the README, run the three commands:
cd relayer-v2
yarn install
yarn build
```

**Step 4: Environment Variables**

* Change the .env.example file to .env
* In line 8 and 9, uncomment either the MNEMONIC or PRIVATE\_KEY and input a private key that has no money on it.
  * I used <https://vanity-eth.tk/> to create a private key to use for the demo but again, do not use one that has any money on it as it is an unnecessary risk.
* Sign up for an Infura account <https://www.infura.io/> and click ‘Create New API Key’. After you create the key, you should see a list of endpoints for each network.
* In the .env file, update the following env variables to the URL for your infura account. The underscore number at the end of the variable represents the chain ID. So RPC\_PROVIDER\_ALCHEMY\_1 is mainnet.
  * You can use <https://chainlist.org/> to find the ID by network.
  * Include the NODE\_URL\_324 for zksync below even though it hasn’t been added to the .env.example file yet:

```
RPC_PROVIDER_ALCHEMY_1=https://eth-mainnet.g.alchemy.com/v2/{API_KEY}
RPC_PROVIDER_ALCHEMY_10=https://opt-mainnet.g.alchemy.com/v2/{API_KEY}
RPC_PROVIDER_ALCHEMY_137=https://polygon-mainnet.g.alchemy.com/v2/{API_KEY}
RPC_PROVIDER_ALCHEMY_42161=https://arb-mainnet.g.alchemy.com/v2/{API_KEY}
NODE_URL_324=https://mainnet.era.zksync.io
```

**Step 5: Run the script**

The below is an example, however the two changes that need to be made based on the request are:

* REQUEST\_TIME=CHANGE\_THIS\_TO\_THE\_TIMESTAMP
* I used privateKey for the below, however, if you used mnemonic in your env variables, it should be changed to –wallet mnemonic

Note: The first time you run this, it is going to take a long time.

REQUEST\_TIME=1692110627 ts-node ./src/scripts/validateRootBundle.ts --wallet privateKey

When the script finishes running, check the Validation results as shown below. The below shows an example of an invalid bundle as it shows “valid”: false:

```
{
  "at": "RootBundleValidator",
  "message": "Validation results",
  "rootBundle": {
    "poolRebalanceRoot": "0x7005c47a8e476a2551d5325a6c53cd5b71f308b2ac670742ad7cceb8078a193f",
    "relayerRefundRoot": "0x9edefc2fc000e71fa1ea1c6ce666a0f8cb2d6da422c45a372585da5e3b738713",
    "slowRelayRoot": "0x0000000000000000000000000000000000000000000000000000000000000000",
    "proposer": "0xf7bAc63fc7CEaCf0589F25454Ecf5C2ce904997c",
    "unclaimedPoolRebalanceLeafCount": 5,
    "challengePeriodEndTimestamp": 1692117191,
    "bundleEvaluationBlockNumbers": [
      17920824,
      108255396,
      46338509,
      977154,
      121659934,
      11305323
    ],
    "proposalBlockNumber": 17920860
  },
  "valid": false,
  "invalidReason": "Disputed pending root bundle:\n.....
// removed the rest of the invalidReason for the example
```

**Appendix**

For additional documentation on Across root bundles, visit:

<https://docs.across.to/v/developer-docs/how-across-works/overview/disputing-root-bundles> <https://docs.across.to/v/developer-docs/how-across-works/overview/validating-root-bundles>


# ManagedOptimisticOracleV2

The [ManagedOptimisticOracleV2](https://github.com/UMAprotocol/managed-oracle/blob/master/src/optimistic-oracle-v2/implementation/ManagedOptimisticOracleV2.sol) contract is an updated version of the OptimisticOracleV2 that is permissioned to specific requesters and allows for requests to whitelist who can propose them.


# Optimistic Oracle v2

This section showcases different design patterns for building contracts that integrate with the UMA Optimistic Oracle (OO). These include:

* A [simple deposit box](/developers/optimistic-oracle/solidity-examples) to showcase the basic OO request lifecycle.
* An [event based prediction market](/developers/optimistic-oracle/in-depth-tutorial-event-based-prediction-market). In this example, settlement requests are submitted at the time of contract deployment, and the OO proposer network is used as a decentralized keeper service that identifies when settlement events happen and propose the outcomes.
* An ["internal Optimistic Oracle"](/developers/optimistic-oracle/internal-optimistic-oracle), where an integrating contract handles proposals and state management, and only uses the OO when proposal verification is needed or a dispute is filed.


# Using Blacklisting Tokens as Currency

Using a token that allows backlisting (e.g. USDC) as your [requestPrice](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L120) `currency` opens up a griefing vector that integrators should be aware of.

The [`proposePriceFor`](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L304) and [`disputePriceFor`](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L382) functions allow the caller to set any address as the `proposer` and `disputer` that will receive payouts if their proposal/dispute is correct upon settlement. If a bad actor calls both [`proposePriceFor`](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L304) and [`disputePriceFor`](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L382) and specifies a blacklisted address for repayment, the [settle](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L498) function will revert and cause the request to be frozen unless the address is unblacklisted. This costs the malicious user 2 bonds and does not result in any gain, but freezing the request could cause issues for the integrator.

To avoid this, integrations that use tokens with blacklisting functionality should ensure that their admins can call a function that ignores the frozen request and creates a new OOV2 request. In this way they can prevent a frozen request from having any negative consequences to their protocol.


# Quick Start

0 to 1 Optimistic Oracle integration by example.

The primary integration point into the UMA ecosystem is the Optimistic Oracle (OO). The OO is an ***oracle for arbitrary off-chain data*** which leverages an interactive escalation game between *requesters*, *proposers* and *disputers* and is secured by *economic incentives*.

This getting started tutorial will show you how to go from 0 to 1 with the Optimistic Oracle by executing the simplest possible request flow. Later in the docs you can find more information on [how the Optimistic Oracle works](/protocol-overview/how-does-umas-oracle-work#optimistic-oracle) and dig deeper into its mechanism and [more sophisticated code examples](/developers/optimistic-oracle/in-depth-tutorial-event-based-prediction-market).

{% hint style="info" %}
**If you prefer, you can also watch the following video tutorial in which we follow the step-by-step instructions described below. Please note, the Optimistic Oracle now supports Sepolia testnet.**
{% endhint %}

{% embed url="<https://www.youtube.com/watch?v=tyi6PnlIHw8&t=26s>" %}

### **A minimum viable Optimistic Oracle integration**

You will be working through a simple smart contract that asks the oracle the question: `Q:Did the temperature on the 25th of July 2022 in Manhattan NY exceed 35c? A:1 for yes. 0 for no.` After submitting the request, you will propose a solution using the UMA Optimistic Oracle UI.

Once through liveness, you will fetch the price from the smart contract. This shows the full lifecycle of an Optimistic Oracle data request on the Sepolia testnet, interacting with an actual Optimistic Oracle contract deployment, without needing to write any code or clone any repos. It should give you the basic intuition as to how the Optimistic Oracle works without too much overhead and is a great starting point before digging deeper. Let's get started!

### **Prerequisites**

To complete this tutorial you will nee&#x64;**:**

1. Metamask installed in a Chromium based browser (such as [Google Chrome](https://www.google.com/chrome/)) If you don't have it already, you can get Metamask [here](https://metamask.io/).
2. A wallet set up in Metamask.
3. [Sepolia](https://sepolia.dev/) test ETH to send test transactions. You can get Sepolia ETH from one of these faucets: [Infura](https://www.infura.io/faucet/sepolia), [Alchemy,](https://www.alchemy.com/faucets/ethereum-sepolia) [PoW Faucet](https://sepolia-faucet.pk910.de/), or [LearnWeb3](https://learnweb3.io/faucets/sepolia/),

### Requesting data

First, we will work through the basic flow for *asking the oracle for a piece of data*. In this example, we are asking the Oracle for information on the weather but the request could be much more complex to could power any kind of smart contract system requiring data. See [approved price identifies](/resources/approved-price-identifiers) and the sample projects for more inspiration on what is possible with the OO.

The contract used in this tutorial is meant to be a simple data request flow. The contract exposes a simple `requestData` function which asks the OO a simple question about the weather.

1. [Go to this example contract on Remix](https://remix.ethereum.org/#version=soljson-v0.8.16+commit.07a7930e.js\&optimize=false\&runs=200\&gist=fba5d2812d940759f4f7585741b529a4). This gives you the minimum data request and retrieval flow possible. We'll work through the code in the sections that follow.
2. Click on "gist-fba..." to see the files in the gist, and click `OO_GettingStarted.sol` to open to Solidity file.
3. In the far left hand menu, click the link to deploy and run transactions (which looks like the Ethereum logo and a right arrow).
4. In the "Environment" dropdown, choose "Injected Provider," and connect to your wallet. **Make sure you are connected to Sepolia within your Metamask wallet!** You don't want to deploy to a real network and spend real gas, and the Sepolia Optimistic Oracle address hardcoded into the contract will not work on other networks.
5. Under the "Contract" dropdown, select `OO_GettingStarted`.
6. Click "Deploy" and confirm the transaction with your wallet.
7. You should now see the `OO_GettingStarted` contract under "Deployed Contracts". Clicking the dropdown carrot will reveal buttons for each of the functions in the contract.
8. Click `requestData` to request the data specified in the contract's ancillary data, asking about the temperature in Manhattan on July 25th, 2022. Confirm the transaction in your wallet. This will submit a data request to the Optimistic Oracle.

What we've done in the above steps is: a) deploy a smart contract and b) submit a "price request" to the Optimistic oracle through the call to the Optimistic Oracle's `requestPrice` function.

### Proposing data

Now that we've asked the OO a question, it's time to propose a solution! The Optimistic Oracle works via an *interactive escalation game* wherein a) requesters ask questions b) proposers provide solutions and c) disputers monitor the proposals and dispute if they are invalid. If no dispute is done then the proposal is taken as valid. In this example, we'll be acting as the proposer who will be answering the question we asked in the previous section

1. Go to the Sepolia[ Optimistic Oracle UI](https://testnet.oracle.umaproject.org/) to see your request, which should be a `YES_OR_NO_QUERY` at the top of the index page. Click on the request to go to the details page.
2. Click "Connect Wallet" and make sure you are connected to the Sepolia testnet so that you can make a proposal. You should see all the information posed in the data request: the requester, identifier, timestamp, ancillary data and a link to the UMIP.
3. Submit your proposal (either `1` or `0`). The actual value you submit is not super important in this case since this is just for testing purposes, but the [correct response](https://www.wunderground.com/history/daily/us/ny/williston-park/KJFK/date/2022-7-25) is `0`. In doing this we are acting as the "proposer" providing a solution.

Once you've provided an answer to the question the proposal enters into a liveness period of 30 seconds. During this time it's up to Disputers to verify the correctness of what the proposer provided. Note that in a main net price request you'll normally set the liveness to much longer than 30 seconds (say two hours), the proposer will be bonded (if they propose wrong they lose money) and the proposer will be rewarded for providing a valid answer to the question.

### Settling the final answer

Finally, we can fetch the data proposed in the previous step from the smart contract. For this example, we assume that there the proposed data was correct (was not disputed). Head back to your Remix tab and:

1. After the transaction finalizes, wait 30 seconds (the challenge window) and then click `settleRequest` on the `OO_GettingStarted` contract on Remix. Confirm the transaction in your wallet. Because the proposal was not disputed within the challenge window, it can now be settled.
2. Click `getSettledPrice` to get the settled value. It should look like `int256: 0` under `getSettledPrice` if you proposed an answer of `0`. You can also see details in the console logs.

Congratulations! you've successfully integrated with the Optimistic Oracle, requested data, proposed data and fetched it within your smart contract! Note that the incentives in this toy example mean that there is no reason that anyone would provide the *correct* price (there was no reward for the proposers or disputes and there was no bond). A real world version of this should [leverage custom bonds and more realistic liveness parameters](/developers/setting-custom-bond-and-liveness-parameters).

### Next Steps

Hopefully you got a basic understanding of the OO request flow from this getting started guide. Check out some of the [example tutorials](/developers/optimistic-oracle/solidity-examples) where we walk through more details on the functions discussed in this guide.


# Deposit Box

Simple deposit box example that showcases the basic request propose lifecycle of the UMA Optimistic Oracle

#### UMA Contract Lifecycle

UMA's Optimistic Oracle allows contracts to quickly request and receive price information. A request is made when a contract submits the following parameters with a request to the Optimistic Oracle contract:

* **identifier:** price identifier being requested.
* **timestamp:** timestamp of the price being requested.
* **ancillaryData:** additional arguments passed with the price request.
* **currency:** ERC20 token used for payment of rewards and fees. Must be approved for use with the DVM.
* **reward:** reward offered to a successful proposer. Will be paid by the caller. Note: this can be 0.

Proposers respond to price requests by referencing off-chain price feeds to submit the price of an asset. In return for their work they will receive a pre-defined proposal reward set by the Requestor. To propose prices, the Proposer is required to stake a proposal bond. In the event that the price information they proposed is disputed and deemed incorrect, the Proposer will lose their bond.

Disputers can refute a price submitted by a Proposer within the proposal liveness period by referencing their own off-chain price feeds. The proposal liveness period is a pre-defined amount of time a proposal can be disputed before the Requestor receives the price of the asset.

If Disputers do not refute the price submitted by the Proposer within the proposal liveness period, the price is sent to the Requestor. If a proposal is disputed, the price will be submitted to UMA’s DVM and resolved after a 48-96 hour voting period.

#### Integrating with the Optimistic Oracle

We continue to use the [OptimisticDepositBox](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/OptimisticDepositBox.sol) contract as an example. The OptimisticDepositBox is a minimal financial contract that allows a user to deposit collateral into a contract and later withdraw their collateral corresponding to a desired USD amount. When the user wants to make a withdrawal, a price request is made to the Optimistic Oracle.

Let's first take a look at the OptimisticDepositBox constructor. Optimistic Oracle price requests require the use of a whitelisted identifier and collateral. The OptimisticDepositBox uses the protocol Finder to discover UMA protocol contracts and call the `isOnWhitelist` method on the `AddressWhitelist` and the `isIdentifierSupported` method on the `IdentifierWhitelist` contract to confirm both are whitelisted before deployment.

```solidity
constructor(
    address _collateralAddress,
    address _finderAddress,
    bytes32 _priceIdentifier,
    address _timerAddress
) nonReentrant() Testable(_timerAddress) {
    finder = FinderInterface(_finderAddress);
    require(_getIdentifierWhitelist().isIdentifierSupported(_priceIdentifier), "Unsupported price identifier");
    require(_getAddressWhitelist().isOnWhitelist(_collateralAddress), "Unsupported collateral type");
    collateralCurrency = IERC20(_collateralAddress);
    priceIdentifier = _priceIdentifier;
}

...

function _getIdentifierWhitelist() internal view returns (IdentifierWhitelistInterface) {
    return IdentifierWhitelistInterface(finder.getImplementationAddress(OracleInterfaces.IdentifierWhitelist));
}

function _getAddressWhitelist() internal view returns (AddressWhitelist) {
    return AddressWhitelist(finder.getImplementationAddress(OracleInterfaces.CollateralWhitelist));
}
```

Example arguments used to deploy the contract can be found and tested in the [OptimisticDepositBox fixture](https://github.com/UMAprotocol/dev-quickstart/blob/main/test/fixtures/OptimisticDepositBox.Fixture.ts). A whitelisted USDC contract is used for collateral, the Finder and Timer addresses use existing UMA ecosystem contracts that are deployed, and a price identifier that has been [whitelisted with the IdentifierWhitelist](https://github.com/UMAprotocol/dev-quickstart/blob/main/test/fixtures/UmaEcosystem.Fixture.ts#L29) contract.

```typescript
// Deploy the OptimisticDepositBox contract.
  const optimisticDepositBox = await (
    await getContractFactory("OptimisticDepositBox", deployer)
  ).deploy(usdc.address, parentFixture.finder.address, identifier, parentFixture.timer.address);
```

#### Requesting a Price

A price request is made when the `requestWithdrawal` [method](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/OptimisticDepositBox.sol#L162) is called. A depositor submits the `denominatedCollateralAmount` to withdrawal and a `requestPrice` call is constructed with the following arguments [shown above](#requesting-a-price) and in the [requestPrice interface](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/interfaces/OptimisticOracleV2Interface.sol#L111):

```solidity
function requestWithdrawal(uint256 denominatedCollateralAmount)
    public
    noPendingWithdrawal(msg.sender)
    nonReentrant
{
    OptimisticDepositBoxData storage depositBoxData = depositBoxes[msg.sender];
    require(denominatedCollateralAmount > 0, "Invalid collateral amount");

    // Update the position data for the user.
    depositBoxData.withdrawalRequestAmount = denominatedCollateralAmount;
    depositBoxData.withdrawalRequestTimestamp = getCurrentTime();

    emit RequestWithdrawal(msg.sender, denominatedCollateralAmount, depositBoxData.withdrawalRequestTimestamp);

    // A price request is sent for the current timestamp.
    _requestOraclePrice(depositBoxData.withdrawalRequestTimestamp);
}

.....

// Requests a price for `priceIdentifier` at `requestedTime` from the Optimistic Oracle.
function _requestOraclePrice(uint256 requestedTime) internal {
    OptimisticOracleInterface oracle = _getOptimisticOracle();
    // For other use cases, you may need ancillary data or a reward. Here, they are both zero.
    oracle.requestPrice(priceIdentifier, requestedTime, "", IERC20(collateralCurrency), 0);
}
```

After price request functionality has been implemented into your contracts, you can test your changes by creating a test in the developer quickstart repo similar to the OptimisticDepositBox test below:

```typescript
await expect(optimisticeDepositBox.connect(depositor).requestWithdrawal(amountToWithdraw))
  .to.emit(optimisticDepositBox, "RequestWithdrawal")
  .withArgs(depositor.address, amountToWithdraw, requestTimestamp);
```

#### Price Proposals

The code snippets above represent the core functionality for deploying a minimal contract and requesting a price. The next step in the contract lifecycle after a request has been made is a price can be proposed.

[Here](https://github.com/UMAprotocol/protocol/blob/b0558f37a5ce240a08d694ebd40f9ed327c897ed/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L304) is the `proposePriceFor` function from the Optimistic Oracle contract that is used to propose a price for requests. The price proposal will revert if the parameters do not match an existing price request. For reference, the [developer quickstart repo](https://github.com/UMAprotocol/dev-quickstart/blob/0c46e1fcdeb0a2543fa3ea0dbeebcfe2da072632/test/OptimisticDepositBox/OptimisticDepositBox.Proposal.ts#L26) demonstrates how to call the `proposePriceFor` method.

#### Liveness

An important aspect to consider when using the Optimistic Oracle is the liveness period which is the number of seconds a proposal must wait before a price can be resolved and contracts can be settled. A `defaultLiveness` value is set (currently 7,200) or developers can set a customLiveness value.

```typescript
request.expirationTime = getCurrentTime().add(
    request.customLiveness != 0 ? request.customLiveness : defaultLiveness
);
```

To set a `customLiveness` value with our OptimisticDepositBox example above, we could add line 5 to change the liveness period from 7,200 seconds to 3,600:

```solidity
function _requestOraclePrice(uint256 requestedTime) internal {
    OptimisticOracleInterface oracle = _getOptimisticOracle();
    // For other use cases, you may need ancillary data or a reward. Here, they are both zero.
    oracle.requestPrice(priceIdentifier, requestedTime, "", IERC20(collateralCurrency), 0);
    oracle.setCustomLiveness(priceIdentifier, requestedTime, "", 3600);
}
```

#### Proposal Bond

A proposal bond is a preloaded reward to incentivize price proposals. Similar to the liveness period, a custom bond amount can be set for a price request.

Two important aspects to remember when setting a proposal bond is:

* When making a price request, the caller must approve the contract to transfer the value of the proposer reward for the collateral being used.
* The value corresponds to the collateral decimal value. So setting the reward to 1 for USDC that uses 6 decimals would be 1000000 while an 18 decimal token would be 1.

The below shows an example of setting the proposer bond to 50 USDC (6 decimals):

```solidity
function _requestOraclePrice(uint256 requestedTime) internal {
    OptimisticOracleInterface oracle = _getOptimisticOracle();
    // For other use cases, you may need ancillary data. Here, the ancillary data is set to zero.
    oracle.requestPrice(priceIdentifier, requestedTime, "", IERC20(collateralCurrency), 0);
    oracle.setBond(priceIdentifier, requestedTime, "", 50000000);
}
```

#### Settlement

The OptimisticDepositBox contract uses the `executeWithdrawal` method to settle contracts. It first checks if a price has been resolved by calling the `hasPrice` method on the Optimistic Oracle which reverts if the request is not settled or settleable. If a price has been resolved, `settleAndGetPrice` is called.

```solidity
function _getOraclePrice(uint256 withdrawalRequestTimestamp) internal returns (uint256) {
    OptimisticOracleInterface oracle = _getOptimisticOracle();
    require(
        oracle.hasPrice(address(this), priceIdentifier, withdrawalRequestTimestamp, ""),
        "Unresolved oracle price"
    );
    int256 oraclePrice = oracle.settleAndGetPrice(priceIdentifier, withdrawalRequestTimestamp, "");

    // For simplicity we don't want to deal with negative prices.
    if (oraclePrice < 0) {
        oraclePrice = 0;
    }
    return uint256(oraclePrice);
}
```


# Event-Based Prediction Market

Building a prediction market that uses the UMA Optimistic Oracle for settlement and event identification

In this section, we'll talk about the [Event Based Prediction market contract](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/EventBasedPredictionMarket.sol), which you can find in the [developer's quick-start repo](https://github.com/UMAprotocol/dev-quickstart). This tutorial will show how event-based OO data requests can be used in a binary prediction market.

You will find out how this smart contract works and how to test it and deploy it. Refer to the [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) contract for additional information on the event-based price requests.

### The Event-Based Prediction Market

This smart contract lets you set up prediction markets that are based on an event-based price request.

For example, you could ask a question like, "Will BTC be over $1M when the first Moon hotel opens?"

*(Note: Although these are called "price" requests in the code, you can request any kind of data from the optimistic oracle. If it's helpful, you can mentally replace "price" with "data" when you see it in the code and documentation.)*

In addition, an event-based price request cannot expire early and automatically returns the requester's reward in the event of a dispute.\
\
By participating in the contract, users are issued long and short position tokens, which, when held, provide exposure to the prediction market.\
\
This contract's logic is a streamlined version of the [LongShortPair](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/financial-templates/long-short-pair/LongShortPair.sol) with an event-based pricing request.

### Development environment and tests

#### Clone the dev-quickstart repo

```
git clone git@github.com:UMAprotocol/dev-quickstart.git
cd dev-quickstart
```

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:

```
yarn
```

#### 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:

```
yarn hardhat compile
```

### Contract design

#### Contract creation and initialization

The contract is created by setting up the prediction market with a series of parameters. With the parameters, you can choose what kind of collateral you want to use with `_collateralToken`. With `_pairName`, we can choose a name for the pair of long and short tokens. `_customAncillaryData` is where we define the price request question. The addresses of the `_finder` and `_timerAddress` set the rest of the contracts addresses we interact with. For reference, [here](https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks) is the full list of UMA contract deployments.

```solidity
    constructor(
        string memory _pairName,
        ExpandedERC20 _collateralToken,
        bytes memory _customAncillaryData,
        FinderInterface _finder,
        address _timerAddress
    ) ...
```

Once the contract has been deployed, the owner can call `initializeMarket()` after approving the `proposerReward` amount to be paid to the wallet that resolves the price request. To keep things simple, `proposerReward` is set to `10e18.`

Also observe how `priceIdentifier` is set to `"YES_OR_NO_QUERY"`.\
\
This function sets up the prediction market by getting the proposer reward and calling `_requestOraclePrice`. This last function starts the price request in [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) and sets up a number of options that are explained below.

```solidity
function _requestOraclePrice() internal {
    OptimisticOracleV2Interface optimisticOracle = getOptimisticOracle();

    collateralToken.safeApprove(address(optimisticOracle), proposerReward);

    optimisticOracle.requestPrice(
        priceIdentifier,
        requestTimestamp,
        customAncillaryData,
        collateralToken,
        proposerReward
    );

    // Set the Optimistic oracle liveness for the price request.
    optimisticOracle.setCustomLiveness(
        priceIdentifier,
        requestTimestamp,
        customAncillaryData,
        optimisticOracleLivenessTime
    );

    // Set the Optimistic oracle proposer bond for the price request.
    optimisticOracle.setBond(priceIdentifier, requestTimestamp, customAncillaryData, optimisticOracleProposerBond);

    // Make the request an event-based request.
    optimisticOracle.setEventBased(priceIdentifier, requestTimestamp, customAncillaryData);

    // Enable the priceDisputed and priceSettled callback
    optimisticOracle.setCallbacks(priceIdentifier, requestTimestamp, customAncillaryData, false, true, true);

    priceRequested = true;
}
```

`_requestOraclePrice` is in charge of initializing the price request in the [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) by performing the following actions:

1. Create the price request with the above-mentioned parameters.
2. Define the custom liveness period that a proposed oracle response must sit through before being accepted as truth.
3. Specify the bond that the proposer and disputer must post in order to resolve a request.
4. Set the type of request to Event Based.
5. Turn on the `priceSettled` and `priceDisputed` callbacks so that our contract can use OO callbacks to respond to these kinds of events

#### Long and Short tokens creation and redemption

Any user can now call the `create` function with the `tokensToCreate` parameter to mint the same number of short and long tokens. Having both tokens in the same proportion means being in a neutral position, as is the case when calling `create`.

\
Holding only long tokens (by transferring short tokens to another wallet or adding liquidity to an AMM pair), gives exposure to the long position and vice versa.

```solidity
    function create(uint256 tokensToCreate) public requestInitialized {
        collateralToken.safeTransferFrom(msg.sender, address(this), tokensToCreate);

        require(longToken.mint(msg.sender, tokensToCreate));
        require(shortToken.mint(msg.sender, tokensToCreate));

        emit TokensCreated(msg.sender, tokensToCreate, tokensToCreate);
    }
```

At any time a token holder with both tokens in the same proportion can exchange them for collateral with \`redeem\`.

```solidity
    function redeem(uint256 tokensToRedeem) public {
        require(longToken.burnFrom(msg.sender, tokensToRedeem));
        require(shortToken.burnFrom(msg.sender, tokensToRedeem));

        collateralToken.safeTransfer(msg.sender, tokensToRedeem);

        emit TokensRedeemed(msg.sender, tokensToRedeem, tokensToRedeem);
    }
```

Any long-short token holder can settle tokens for collateral with `settle` if the oracle has processed the price request.

\
The returned collateral amount is a function of `longTokensToRedeem`, `shortTokensToRedeem`, and `settlementPrice`.

```solidity
    function settle(uint256 longTokensToRedeem, uint256 shortTokensToRedeem)
        public
        returns (uint256 collateralReturned)
    {
        require(receivedSettlementPrice, "price not yet resolved");

        require(longToken.burnFrom(msg.sender, longTokensToRedeem));
        require(shortToken.burnFrom(msg.sender, shortTokensToRedeem));

        // settlementPrice is a number between 0 and 1e18. 0 means all collateral goes to short tokens and 1e18 means
        // all collateral goes to the long token. Total collateral returned is the sum of payouts.
        uint256 longCollateralRedeemed = (longTokensToRedeem * settlementPrice) / (1e18);
        uint256 shortCollateralRedeemed = (shortTokensToRedeem * (1e18 - settlementPrice)) / (1e18);

        collateralReturned = longCollateralRedeemed + shortCollateralRedeemed;
        collateralToken.safeTransfer(msg.sender, collateralReturned);

        emit PositionSettled(msg.sender, collateralReturned, longTokensToRedeem, shortTokensToRedeem);
    }
```

#### Price request lifecycle callbacks: priceSettled and priceDisputed

When the price request we set up above is settled in the [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol), the `priceSettled` function of this contract is invoked.

This function calculates and stores settlementPrice as `0`, `0.5`, or `1`. This number is used in the `settle` function to calculate the collateral to pay in exchange for long tokens and short tokens.

```solidity
function priceSettled(
    bytes32 identifier,
    uint256 timestamp,
    bytes memory ancillaryData,
    int256 price
) external {
    OptimisticOracleV2Interface optimisticOracle = getOptimisticOracle();
    require(msg.sender == address(optimisticOracle), "not authorized");

    require(identifier == priceIdentifier, "same identifier");
    require(keccak256(ancillaryData) == keccak256(customAncillaryData), "same ancillary data");

    // We only want to process the price if it is for the current price request.
    if (timestamp != requestTimestamp) return;

    // Calculate the value of settlementPrice using either 0, 0.5e18, or 1e18 as the expiryPrice.
    if (price >= 1e18) {
        settlementPrice = 1e18;
    } else if (price == 5e17) {
        settlementPrice = 5e17;
    } else {
        settlementPrice = 0;
    }

    receivedSettlementPrice = true;
}
```

In the same way, this contract's `priceDisputed` function is called when a price request is disputed. This function re-starts the same price request with the bond amount that was given back to the requester, which in our case is the `EventBasedPredictionMarket`.

```solidity
function priceDisputed(
    bytes32 identifier,
    uint256 timestamp,
    bytes memory ancillaryData,
    uint256 refund
) external {
    OptimisticOracleV2Interface optimisticOracle = getOptimisticOracle();
    require(msg.sender == address(optimisticOracle), "not authorized");

    requestTimestamp = getCurrentTime();
    require(timestamp <= requestTimestamp, "different timestamps");
    require(identifier == priceIdentifier, "same identifier");
    require(keccak256(ancillaryData) == keccak256(customAncillaryData), "same ancillary data");
    require(refund == proposerReward, "same proposerReward amount");

    _requestOraclePrice();
}
```

#### Tests

To execute the EventBasedPredictionMarket tests, run:

```
yarn test test/EventBasedPredictionMarket/*
```

#### Deployment

Before deploying the contract check/edit the default arguments defined in [the deployment script](https://github.com/UMAprotocol/dev-quickstart/blob/main/deploy/002_deploy_event_based_prediction_market.ts).

To deploy `EventBasedPredictionMarket` in Görli network, run:

```bash
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat deploy --network goerli --tags EventBasedPredictionMarket
```

Optionally, you can verify the deployed contract on Etherscan:

```bash
ETHERSCAN_API_KEY=YOUR_API_KEY yarn hardhat etherscan-verify --network goerli --license AGPL-3.0 --force-license --solc-input
```


# Insurance Claim Arbitration

Using the Optimistic Oracle to allow for verification of insurance claims

This section covers the [insurance claims arbitration contract](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/InsuranceArbitrator.sol), which is available in the [developer's quick-start repo](https://github.com/UMAprotocol/dev-quickstart). This tutorial shows an example of how insurance claims can be resolved and settled through the [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) contract.

You will find out how to test and deploy this smart contract and how it integrates with the Optimistic Oracle.

### Insurance Arbitrator Contract

This smart contract allows insurers to issue insurance policies by depositing the insured amount, designating the insured beneficiary, and describing the insured event.

Anyone can submit a claim that the insured event has occurred at any time. Insurance Arbitrators resolve the claim through the Optimistic Oracle by passing a question with a description of the insured event in ancillary data using the `YES_OR_NO_QUERY` price identifier specified in [UMIP-107](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md).

If the claim is confirmed and settled through the Optimistic Oracle, this contract automatically pays out insurance coverage to the beneficiary. If the claim is rejected, the policy continues to be active and ready for subsequent claim attempts.

### 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:

```bash
git clone git@github.com:UMAprotocol/dev-quickstart.git
cd dev-quickstart
yarn
```

#### 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:

```bash
yarn hardhat compile
```

### Contract implementation

The contract discussed in this tutorial can be found at `dev-quickstart/contracts/InsuranceArbitrator.sol` ([here](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/InsuranceArbitrator.sol)) within the repo.

#### Contract creation and initialization

`_finder` parameter in the constructor points the Insurance Arbitrator to the Finder contract that stores the addresses of the rest of the UMA contracts. The Finder address can be fetched from the relevant [networks](https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks) file, if you are on a live network, or you can provide your own `Finder` instance if deploying UMA [protocol](https://github.com/UMAprotocol/protocol) in your own sandboxed testing environment.

`_currency` parameter in the constructor identifies the token used for settlement of insurance claims, as well as the bond currency for proposals and disputes. This token should be approved as whitelisted UMA collateral. Please check [Approved Collateral Types](/resources/approved-collateral-types) for production networks or call `getWhitelist()` on the [Address Whitelist](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/common/implementation/AddressWhitelist.sol) contract for any of the test networks.

Alternatively, you can approve a new token address with `addToWhitelist` method in the Address Whitelist contract if working in a sandboxed UMA environment.

`_timer` is used only when running unit tests locally to simulate the advancement of time. For all the public networks (including testnets) the zero address should be used.

```solidity
    constructor(
        FinderInterface _finder,
        address _currency,
        address _timer
    ) Testable(_timerAddress) {
        finder =_finderAddress;
        currency = IERC20(_currency);
        oo = OptimisticOracleV2Interface(finder.getImplementationAddress(OracleInterfaces.OptimisticOracleV2));
    }
```

As part of initialization, the `oo` variable is set to the address of the `OptimisticOracleV2` implementation as discovered through `getImplementationAddress` method in the [Finder](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/implementation/Finder.sol) contract.

#### Issuing insurance

`issueInsurance` method allows any insurer to deposit `insuredAmount` of `currency` tokens by designating an insurance beneficiary (`insuredAddress`) and defining the insured event (`insuredEvent`). Before calling this method, the insurer should have approved this contract to spend the required amount of `currency` tokens.

```solidity
    function issueInsurance(
        string calldata insuredEvent,
        address insuredAddress,
        uint256 insuredAmount
    ) external returns (bytes32 policyId) ...
```

Internally, the issued policy is stored in the `insurancePolicies` mapping using the calculated `policyId` key that is generated by hashing the current block number with the provided insurance parameters in the internal `_getPolicyId` function.

After pulling `insuredAmount` from the caller in the `issueInsurance` method, the contract emits a `PolicyIssued` event including the `policyId` parameter that should be used when claiming insurance.

#### Submitting insurance claim

Anyone can submit an insurance claim on the issued policy by calling the `submitClaim` method with the relevant `policyId` parameter. This method will initiate both a data request and proposal with the Optimistic Oracle. A proposal bond is required, hence the caller should have approved this contract to spend the required amount of `currency` tokens for the proposal bond.

```solidity
    function submitClaim(bytes32 policyId) ...
```

After checking that the `policyId` represents a valid unclaimed insurance policy, the contract gets the current `timestamp` and composes `ancillaryData` that will be required for making requests and proposals to the Optimistic Oracle:

```solidity
        uint256 timestamp = getCurrentTime(); // note that `getCurrentTime` is exported from testable to enable easy time manipulation.
        bytes memory ancillaryData = abi.encodePacked(ancillaryDataHead, claimedPolicy.insuredEvent, ancillaryDataTail);
        bytes32 claimId = _getClaimId(timestamp, ancillaryData);
        insuranceClaims[claimId] = policyId;
```

The resulting `timestamp` and `ancillaryData` parameters are hashed in the internal `_getClaimId` method that is used as a key when storing the linked `policyId` in the `insuranceClaims` mapping. This information will be required when receiving a callback from the Optimistic Oracle.

The concatenated `ancillaryData` will have a valid question as specified in [UMIP-107](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md) for `YES_OR_NO_QUERY` price identifier.

An Optimistic Oracle data request is initiated without providing any proposer reward since the proposal will be done within the `submitClaim` method:

```solidity
        oo.requestPrice(priceIdentifier, timestamp, ancillaryData, currency, 0);
```

Before the proposal is made, the Optimistic Oracle allows the requesting contract (this Insurance Arbitrator) to set additional parameters like bonding, liveness, and callback settings. This requires passing the same `priceIdentifier`, `timestamp`, and `ancillaryData` parameters to identify the request.

Total bond to be pulled from the claim initiator consists of the Optimistic Oracle proposer bond and final fee for the relevant `currency` token. This contract sets the proposer bond as a fixed percentage (constant `oracleBondPercentage`) from `insuredAmount`. When calling the `setBond` method, the Optimistic Oracle calculates and returns the total bond that would be pulled when making the proposal:

```solidity
        uint256 proposerBond = (claimedPolicy.insuredAmount * oracleBondPercentage) / 1e18;
        uint256 totalBond = oo.setBond(priceIdentifier, timestamp, ancillaryData, proposerBond);
```

Optimistic Oracle liveness is set by calling the `setCustomLiveness` method. This contract uses 24 hours so that verifiers have sufficient time to check the claim, but one can adjust the `optimisticOracleLivenessTime` constant for testing. (You probably don't want to wait a full day to resolve your test requests!)

```solidity
        oo.setCustomLiveness(priceIdentifier, timestamp, ancillaryData, optimisticOracleLivenessTime);
```

In contrast to earlier versions, the Optimistic Oracle V2 by default does not use callbacks and requesting contracts have to explicitly subscribe to them if intending to perform any logic when a data request has changed state. Here, in calling `setCallbacks`, this contract only subscribes to a callback for settlement, as implemented in the `priceSettled` method. (Note: Subscribing to any other callbacks that are not implemented in the requesting contract would make data requests unresolvable.)

```solidity
        oo.setCallbacks(priceIdentifier, timestamp, ancillaryData, false, false, true);
```

After `totalBond` amount of `currency` token is pulled from the claim initiator and approved to be taken by Optimistic Oracle, this contract proposes `1e18` representing an answer of `YES` to the raised question. Requesting and proposing affirmative answers atomically allows us to reduce the number of steps taken by end users and it is most likely expected that the insured beneficiary would be initiating the claim.

```solidity
        oo.proposePriceFor(msg.sender, address(this), priceIdentifier, timestamp, ancillaryData, int256(1e18));
```

#### Disputing insurance claim

For the sake of simplicity this contract does not implement a dispute method, but the disputer can dispute the submitted claim directly through Optimistic Oracle before the liveness passes by calling its `disputePrice` method:

```solidity
    function disputePrice(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) ...
```

The disputer should pass the address of this Insurance Arbitrator contract as `requester` and all the other parameters from the original request when the claim was initiated as emitted by the Optimistic Oracle in its `RequestPrice` event.

If the claim is disputed, the request is escalated to the UMA DVM and it can be settled only after UMA voters have resolved it. To learn more about the DVM, see the docs section on the DVM: [how does UMA's DVM work](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism).

#### Settling insurance claim

Similar to disputes, claim settlement should be initiated through the Optimistic Oracle contract by calling its `settle` method with the same parameters:

```solidity
    function settle(
        address requester,
        bytes32 identifier,
        uint256 timestamp,
        bytes memory ancillaryData
    ) ...
```

In case the liveness has expired or a dispute has been resolved by the UMA DVM, this call would initiate a `priceSettled` callback in the Insurance Arbitrator contract:

```solidity
    function priceSettled(
        bytes32, // identifier passed by Optimistic Oracle, but not used here as it is always the same.
        uint256 timestamp,
        bytes memory ancillaryData,
        int256 price
    ) ...
```

Based on the received callback parameters, this contract can identify the relevant `claimId` that is used to get the stored insurance policy:

```solidity
        bytes32 claimId = _getClaimId(timestamp, ancillaryData);
```

Importantly, all callbacks should be restricted to accept calls only from the Optimistic Oracle to avoid someone spoofing a resolved answer:

```solidity
        require(address(oo) == msg.sender, "Unauthorized callback");
```

Depending on the resolved answer, this contract would either pay out the insured beneficiary and delete the insurance (in case of `1e18` representing the answer `YES`, the insurance claim was valid) or reject the payout and re-open the policy for any subsequent claims:

```solidity
        // Deletes insurance policy and transfers claim amount if the claim was confirmed.
        if (price == 1e18) {
            delete insurancePolicies[policyId];
            currency.safeTransfer(claimedPolicy.insuredAddress, claimedPolicy.insuredAmount);

            emit ClaimAccepted(claimId, policyId);
            // Otherwise just reset the flag so that repeated claims can be made.
        } else {
            insurancePolicies[policyId].claimInitiated = false;

            emit ClaimRejected(claimId, policyId);
        }
```

### Tests and deployment

All the unit tests covering the functionality described above are available [here](https://github.com/UMAprotocol/dev-quickstart/tree/main/test/InsuranceArbitrator). To execute all of them, run:

```bash
yarn test test/InsuranceArbitrator/*
```

Before deploying the contract check the comments on available environment variables in [the deployment script](https://github.com/UMAprotocol/dev-quickstart/blob/main/deploy/003_deploy_insurance_arbitrator.ts).

In the case of the Görli testnet, the defaults would use the Finder instance that references the [Mock Oracle](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/oracle/test/MockOracleAncillary.sol) 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 Insurance Arbitrator contract on Görli, run:

```bash
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat deploy --network goerli --tags InsuranceArbitrator
```

Optionally you can verify the deployed contract on Etherscan:

```bash
ETHERSCAN_API_KEY=YOUR_API_KEY yarn hardhat etherscan-verify --network goerli --license AGPL-3.0 --force-license --solc-input
```

### 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:

```bash
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat console --network goerli
```

#### Initial setup

From the Hardhat console, start by adding the required `getAbi` dependency for interacting with UMA contracts and use the first two accounts as insurer and insured beneficiary:

```javascript
const { getAbi } = require("@uma/contracts-node");
const [insurer, insured] = await ethers.getSigners();
```

Grab the deployed Insurance Arbitrator contract:

```javascript
const insuranceArbitratorDeployment = await deployments.get("InsuranceArbitrator");
const insuranceArbitrator = new ethers.Contract(
	insuranceArbitratorDeployment.address,
	insuranceArbitratorDeployment.abi,
	ethers.provider
);
```

#### Issue insurance

Assuming `TestnetERC20` was used as `currency` when deploying, mint the required insurance amount (e.g. 10,000 TEST tokens) and approve the Insurance Arbitrator to pull them:

```javascript
const insuredAmount = ethers.utils.parseEther("10000");
const currency = new ethers.Contract(await insuranceArbitrator.currency(), getAbi("TestnetERC20"), ethers.provider);
await (await currency.connect(insurer).allocateTo(insurer.address, insuredAmount)).wait();
await (await currency.connect(insurer).approve(insuranceArbitrator.address, insuredAmount)).wait();
```

Issue the insurance policy and grab the resulting `policyId` from the emitted `PolicyIssued` event:

```javascript
const issueReceipt = await (await insuranceArbitrator.connect(insurer).issueInsurance(
	"Bad things have happened",
	insured.address,
	insuredAmount
)).wait();
const policyId = (await insuranceArbitrator.queryFilter(
	"PolicyIssued",
	issueReceipt.blockNumber,
	issueReceipt.blockNumber
))[0].args.policyId;
```

#### Submit insurance claim

First calculate the expected proposer bond:

```javascript
const proposerBond = insuredAmount.mul(await insuranceArbitrator.oracleBondPercentage()).div(ethers.utils.parseEther("1"));
```

Fetch the expected final fee from the `Store` contract (which is discovered through the `Finder`):

```javascript
const finder = new ethers.Contract(await insuranceArbitrator.finder(), getAbi("Finder"), ethers.provider);
const store = new ethers.Contract(await finder.getImplementationAddress(
	ethers.utils.formatBytes32String("Store")),
	getAbi("Store"),
	ethers.provider);
const finalFee = (await store.computeFinalFee(currency.address)).rawValue;
```

Calculate the expected total bond and provide funding/approval for the insured claimant:

```javascript
const totalBond = proposerBond.add(finalFee);
await (await currency.connect(insured).allocateTo(insured.address, totalBond)).wait();
await (await currency.connect(insured).approve(insuranceArbitrator.address, totalBond)).wait();
```

Now initiate the insurance claim and grab request details from the `RequestPrice` event emitted by the Optimistic Oracle:

```javascript
const oo = new ethers.Contract(await insuranceArbitrator.oo(), getAbi("OptimisticOracleV2"), ethers.provider);
const claimReceipt = await (await insuranceArbitrator.connect(insured).submitClaim(policyId)).wait();
const request = (await oo.queryFilter("RequestPrice", claimReceipt.blockNumber, claimReceipt.blockNumber))[0].args;
```

#### Dispute insurance claim

Before liveness passes, the insurer can dispute the claim through the Optimistic Oracle. First, they must fund and approve with the same bonding amount:

```javascript
await (await currency.connect(insurer).allocateTo(insurer.address, totalBond)).wait();
await (await currency.connect(insurer).approve(oo.address, totalBond)).wait();
```

If you are on a testnet like Göerli, in order to simulate UMA voting on a testnet, you can use the [Mock Oracle](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/test/MockOracleAncillary.sol):

```javascript
const mockOracle = new ethers.Contract(await finder.getImplementationAddress(
	ethers.utils.formatBytes32String("Oracle")),
	getAbi("MockOracleAncillary"),
	ethers.provider);
```

Now initiate the dispute and grab the vote request details from the `PriceRequestAdded` event emitted by the Mock Oracle:

```javascript
const disputeReceipt = await (await oo.connect(insurer).disputePrice(
	request.requester,
	request.identifier,
	request.timestamp,
	request.ancillaryData
)).wait();
const voteRequest = (await mockOracle.queryFilter(
	"PriceRequestAdded",
	disputeReceipt.blockNumber,
	disputeReceipt.blockNumber
))[0].args;
```

#### Settle insurance claim

Before settling the claim, we can take a look at the vote request as seen by UMA voters:

```javascript
console.log("identifier:", ethers.utils.parseBytes32String(voteRequest.identifier));
console.log("time:", Number(voteRequest.time));
console.log("ancillaryData:", ethers.utils.toUtf8String(voteRequest.ancillaryData));
```

The `ancillaryData` should start with `q:"Had the following insured event occurred as of request timestamp: Bad things have happened?"`. It is then followed by the `ooRequester` key with our Insurance Arbitrator address in its value.

In order to simulate `YES` as the resolved answer we would pass `1e18` as the `price` parameter in the Mock Oracle `pushPrice` method:

```javascript
await (await mockOracle.connect(insured).pushPrice(
	voteRequest.identifier,
	voteRequest.time,
	voteRequest.ancillaryData,
	ethers.utils.parseEther("1")
)).wait();
```

Now we can settle the request through the Optimistic Oracle and observe the emitted `ClaimAccepted` from our Insurance Arbitrator contract:

```javascript
const settleReceipt = await (await oo.connect(insured).settle(
	request.requester,
	request.identifier,
	request.timestamp,
	request.ancillaryData
)).wait();
const claimSettlementEvent = (await insuranceArbitrator.queryFilter(
	"ClaimAccepted",
	settleReceipt.blockNumber,
	settleReceipt.blockNumber
))[0];
console.log(claimSettlementEvent);
```

The above settlement transaction should also transfer `insuredAmount` tokens to the insured beneficiary as well as return the proposer bond to the claim initiator.

Alternatively, if `0` value was resolved, the settlement transaction should emit the `ClaimRejected` event without paying out the `insuredAmount` and returning the bond to the disputer, along with half of the proposer's bond.


# 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](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) to [UMA's Data Verification Mechanism](/protocol-overview/how-does-umas-oracle-work#umas-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](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) 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 `int256`.                                                                                                                               | Customized data structures: the data requested from the oracle can be of any type (`uint256` in this IOO implementation).                                              |
| 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 `msg.sender`, requiring the requester and proposer to be same. |
| Disputes are resolved in the [DVM](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism) through a vote of UMA token holders.                                  | Disputes are resolved in the [DVM](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism) 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 chose `uint256`, 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](https://github.com/UMAprotocol/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:

```bash
git clone git@github.com:UMAprotocol/dev-quickstart.git
cd dev-quickstart
yarn
```

#### 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:

```bash
yarn hardhat compile
```

### Contract Implementation

The contract discussed in this tutorial can be found at `dev-quickstart/contracts/InternalOptimisticOracle.sol` ([here](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/InternalOptimisticOracle.sol)) 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.

```solidity
constructor(
    address _finderAddress,
    address _currency,
    address _timerAddress
) Testable(_timerAddress) {
    finder = FinderInterface(_finderAddress);
    currency = IERC20(_currency);
    oo = OptimisticOracleV2Interface(finder.getImplementationAddress(OracleInterfaces.OptimisticOracleV2));
}
```

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](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/implementation/Finder.sol) 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](/resources/approved-price-identifiers) 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](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md).\
\
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 the `currency` defined in the constructor to pay to the proposer on settlement
* `bond` the bond in the `currency` defined in the constructor that we want to require on top of the [`finalFee`](/resources/approved-collateral-types) (\~1500 USD worth of the currency). The [`finalFee`](/resources/approved-collateral-types) can be viewed as the minimum bond required by the system to process a dispute, and the bond is any additional amount we to require on top.
* `liveness` 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`.

```solidity
function requestPrice(
    uint256 timestamp,
    bytes memory ancillaryData,
    uint256 reward,
    uint256 bond,
    uint64 liveness
) public {
    bytes32 requestId = _getId(msg.sender, timestamp, ancillaryData);
    require(address(requests[requestId].currency) == address(0), "Request already initialized");
    require(_getIdentifierWhitelist().isIdentifierSupported(priceIdentifier), "Unsupported identifier");
    require(_getCollateralWhitelist().isOnWhitelist(address(currency)), "Unsupported currency");
    require(timestamp <= getCurrentTime(), "Timestamp in future");

    requests[requestId] = Request({
        proposer: address(0),
        disputer: address(0),
        currency: currency,
        settled: false,
        proposedPrice: 0,
        reward: reward,
        finalFee: _getFinalFee(),
        bond: bond,
        liveness: liveness,
        expirationTime: 0
    });

    if (reward > 0) currency.safeTransferFrom(msg.sender, address(this), reward);
}
```

#### 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.

```solidity
function proposePrice(
    uint256 timestamp,
    bytes memory ancillaryData,
    uint256 proposedPrice
) public {
    Request storage request = requests[_getId(msg.sender, timestamp, ancillaryData)];
    require(address(request.currency) != address(0), "Price not requested");
    require(request.proposer == address(0), "Price already proposed");

    request.proposer = msg.sender;
    request.proposedPrice = proposedPrice;
    request.expirationTime = uint64(getCurrentTime()) + request.liveness;

    request.currency.safeTransferFrom(msg.sender, address(this), request.bond + request.finalFee);
}
```

#### 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.

```solidity
function disputePrice(uint256 timestamp, bytes memory ancillaryData) public {
    bytes32 requestId = _getId(msg.sender, timestamp, ancillaryData);
    Request storage request = requests[requestId];
    require(request.proposer != address(0), "No proposed price to dispute");
    require(request.disputer == address(0), "Proposal already disputed");
    require(uint64(getCurrentTime()) < request.expirationTime, "Proposal past liveness");

    request.disputer = msg.sender;

    // If the final fee gets updated between the time the request is made and the time the dispute is made, the
    // disputer will have to pay the final fee increase x2. This is a very rare edge case that we are willing to accept.
    uint256 finalFeeRise = _getFinalFeeRise(request);
    uint256 initialBalance = request.currency.balanceOf(address(this));

    request.currency.safeTransferFrom(
        msg.sender,
        address(this),
        request.bond + request.finalFee + 2 * finalFeeRise
    );

    request.currency.approve(address(oo), 2 * (request.bond + request.finalFee + finalFeeRise) + request.reward);

    bytes memory disputeAncillaryData = _getDisputeAncillaryData(requestId);
    oo.requestPrice(priceIdentifier, timestamp, disputeAncillaryData, request.currency, request.reward);
    oo.setBond(priceIdentifier, timestamp, disputeAncillaryData, request.bond);
    oo.proposePriceFor(request.proposer, address(this), priceIdentifier, timestamp, disputeAncillaryData, 1e18);
    oo.disputePriceFor(msg.sender, address(this), priceIdentifier, timestamp, disputeAncillaryData);

    // If the final fee has decreased, refund the excess to the disputer & proposer.
    uint256 balanceToRefund = request.currency.balanceOf(address(this)) > initialBalance
        ? request.currency.balanceOf(address(this)) - initialBalance
        : 0;
    if (balanceToRefund > 0) request.currency.safeTransfer(msg.sender, balanceToRefund);
}
```

### Tests and deployment

All the unit tests covering the functionality described above are available [here](https://github.com/UMAprotocol/dev-quickstart/tree/main/test/OptimisticArbitrator). To execute all of them, run:

```bash
yarn test test/InternalOptimisticOracle/*
```

Before deploying the contract check the comments on available environment variables in [the deployment script](https://github.com/UMAprotocol/dev-quickstart/tree/main/deploy).

In the case of the Görli testnet, the defaults would use the Finder instance that references the [Mock Oracle](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/oracle/test/MockOracleAncillary.sol) 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:

```shell
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat deploy --network goerli --tags InternalOptimisticOracle
```

Optionally, you can verify the deployed contract on Etherscan:

```bash
ETHERSCAN_API_KEY=YOUR_API_KEY yarn hardhat etherscan-verify --network goerli --license AGPL-3.0 --force-license --solc-input
```

### 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:

```
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat console --network goerli
```

**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.

```javascript
const { getAbi, getAddress } = require("@uma/contracts-node");
const { ethers } = require("hardhat");
const signer = (await ethers.getSigners())[0];

const iooDeployment = await deployments.get("InternalOptimisticOracle");
const ioo = new ethers.Contract(
  iooDeployment.address,
  iooDeployment.abi,
  ethers.provider
);
const finder = new hre.ethers.Contract(
  "0xDC6b80D38004F495861E081e249213836a2F3217", // Finder address used in the deployment
  getAbi("Finder"),
  ethers.provider
);
const store = new hre.ethers.Contract(
  await finder.getImplementationAddress(
    ethers.utils.formatBytes32String("Store")
  ),
  getAbi("Store"),
  ethers.provider
);
const currency = new hre.ethers.Contract(
  await ioo.currency(),
  getAbi("TestnetERC20"),
  ethers.provider
);
const mockOracle = new hre.ethers.Contract(
  await finder.getImplementationAddress(
    ethers.utils.formatBytes32String("Oracle")
  ),
  getAbi("MockOracleAncillary"),
  ethers.provider
);
```

**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.

```javascript
const reward = ethers.utils.parseUnits("100", 18);

await (await currency.connect(signer).allocateTo(signer.address, reward)).wait();
await (await currency.connect(signer).approve(ioo.address, reward)).wait();
```

Request the price:

```javascript
let requestTimestamp = await(
  await ethers.provider.getBlock("latest")
).timestamp;

const bond = ethers.utils.parseUnits("500", 18);

const liveness = 10; // 10 seconds for testing purposes

const ancillaryData = ethers.utils.toUtf8Bytes(
  `q: "What uint256 are we looking for?"`
);

await(
  await ioo.connect(signer).requestPrice(
    requestTimestamp,
    ancillaryData,
    reward,
    bond,
    liveness
  )
).wait();
```

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:

```javascript
const finalFee = await store.computeFinalFee(currency.address);
const totalAmount = bond.add(finalFee.rawValue);

await (await currency.connect(signer).allocateTo(signer.address, totalAmount)).wait();
await (await currency.connect(signer).approve(ioo.address, totalAmount)).wait();
```

And propose the price:

```javascript
const correctAnswer = ethers.utils.parseEther("1");
await(
  await ioo
    .connect(signer)
    .proposePrice(requestTimestamp, ancillaryData, correctAnswer)
).wait();
```

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:

```javascript
await(
  await ioo.connect(signer).settleAndGetPrice(requestTimestamp, ancillaryData)
).wait();

console.log(
  "Price is the correct answer: ",
  (await ioo.getPrice(requestTimestamp, ancillaryData)).eq(correctAnswer)
);
```

**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:

```javascript
// get a new request timestamp
requestTimestamp = await(await ethers.provider.getBlock("latest")).timestamp;

// mint and approve the reward
await(await currency.connect(signer).allocateTo(signer.address, reward)).wait();
await(await currency.connect(signer).approve(ioo.address, reward)).wait();

// request a new price
const longerLiveness = 3600;
await(
  await ioo
    .connect(signer)
    .requestPrice(requestTimestamp, ancillaryData, reward, bond, longerLiveness)
).wait();

// mint and approve the bond
await(
  await currency.connect(signer).allocateTo(signer.address, totalAmount)
).wait();
await(await currency.connect(signer).approve(ioo.address, totalAmount)).wait();

// propose a price
await(
  await ioo
    .connect(signer)
    .proposePrice(requestTimestamp, ancillaryData, correctAnswer)
).wait();
```

Then we can dispute the price:

```javascript
// mint and approve the bond for the disputer
await(
  await currency.connect(signer).allocateTo(signer.address, totalAmount)
).wait();
await(await currency.connect(signer).approve(ioo.address, totalAmount)).wait();

// dispute the price
const receipt = await(
  await ioo.connect(signer).disputePrice(requestTimestamp, ancillaryData)
).wait();
```

As we are in a test environment, we can simulate the vote that would normally occur by pushing a price to the mockOracle:

```javascript
const disputedPriceRequest = (
  await mockOracle.queryFilter(mockOracle.filters.PriceRequestAdded(), receipt.blockNumber, receipt.blockNumber)
)[0];

await mockOracle.connect(signer).pushPrice(
    disputedPriceRequest.args.identifier,
    disputedPriceRequest.args.time,
    disputedPriceRequest.args.ancillaryData,
    correctAnswer // The original price is accepted
  );
```

At this point, we can call `settleAndGetPrice` to settle the accepted price request and then confirm that the result matches the proposed price:

```javascript
await(
  await ioo.connect(signer).settleAndGetPrice(requestTimestamp, ancillaryData)
).wait();

console.log(
  "Price is the correct answer: ",
  (await ioo.getPrice(requestTimestamp, ancillaryData)).eq(correctAnswer)
);
```


# Optimistic Arbitrator

Wrapping Optimistic Oracle functionality into individual function calls

### The Optimistic Arbitrator

The Optimistic Arbitrator (OA) is a contract built on top of the [Optimistic Oracle V2](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol) (OO) in order to abstract some mechanics and make them easier to use and implement with an "assert and ratify" pattern, as opposed to the Optimistic Oracle's typical "request and propose" pattern.

It should be noted that the uses permitted by the OA were always permitted by the OO; the OA merely formalizes these specific modes of use in a more developer-friendly implementation for this particular purpose. This pattern can also be used as inspiration in your projects when you request and propose (or request, propose and dispute) requests with the OO in one function call.

In short, the OA allows a user to make an assertion, i.e., propose an answer to a question directly. They can optionally ratify the assertion by escalating the question immediately to [UMA's Data Verification Mechanism](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism) (DVM) where all UMA token holders vote on it.

An assertion consists of using the `requestPrice` and `proposePrice` methods of the OptimisticOracle to ask and answer a question simultaneously. It has the advantage, compared to a traditional `priceRequest`, of receiving a response immediately and incurring no final cost if the assertion is truthful and undisputed.

To ratify an assertion, an initial assertion must be made and disputed, such that the question is escalated to the [DVM](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism), where UMA token holders vote on it. Ratifications can be useful when the user prefers to have their assertion voted on, at a higher cost, rather than optimistically accepted.

The following table shows how the Optimistic Arbitrator differs from the Optimistic Oracle and why using the Optimistic Arbitrator could be interesting:

| Optimistic Oracle (OO)                                                                                                                                                                                                                                                                                                   | Optimistic Arbitrator (OA)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p>In the basic scenario, the user will first pose a question (<code>priceRequest</code>), wait for someone to propose (<code>proposePrice</code>) an answer and potentially dispute (<code>disputePrice</code>) a wrong answer.<br></p>                                                                                 | The user makes an assertion by submitting a question (`priceRequest`) to the Optimistic Oracle and simultaneously proposing an answer (`proposePrice`). Optionally, they can ratify the assertion and present it to the DVM for a vote (`disputePrice`).                                                                                                                                                                                                                                                                                                                                                                                            |
| The requester, proposer and disputer are generally different people.                                                                                                                                                                                                                                                     | The requester and and proposer are always the same person. The disputer may be another person or the same one.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| The user waits for someone to respond to their question.                                                                                                                                                                                                                                                                 | The user does not wait, answering their own question immediately.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| The user pays only the reward for the question, which is optional and of their choosing (although, if it is 0, proposers would not be interested in answering).                                                                                                                                                          | The user does not have to pay a reward since they are the proposer, but they must lock the bond amount to propose, which they will receive back if the proposal is not disputed (or determined to be correct after a dispute).                                                                                                                                                                                                                                                                                                                                                                                                                      |
| In the simplest scenario, in which a user merely requests a price (i.e., asks a question), proposals and disputes are made by other users, so the requester is not charged or required to pay any fees beyond the reward. However if the reward is $0, most likely, they will never receive an answer to their question. | <p>When making only an assertion, the user must lock a bond at least equal to the <a href="/pages/K6WnNcMvctgDpRgnaUpx"><code>finalFee</code></a>; this bond is returned if the assertion was correct (undisputed). Therefore, the final cost of asserting truthfully is $0.</p><p>When asserting and ratifying, the function has a fixed final cost for the user equal to the <a href="/pages/K6WnNcMvctgDpRgnaUpx"><code>finalFee</code></a> of the <code>currency</code> used (\~1,500 USD). This is the cost of taking a dispute to the <a href="/pages/bzpPheJPDQo3ds4dmiLM#umas-data-verification-mechanism">DVM</a> for vote resolution.</p> |
| Disputes are resolved in the [DVM](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism) where they are voted on by UMA token holders.                                                                                                                                                          | Disputes are resolved in the [DVM](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism) where they are voted on by UMA token holders.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

### Development environment and tests

#### Clone repository and Install dependencies

Clone the [UMA dev-quickstart](https://github.com/UMAprotocol/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:

```bash
git clone git@github.com:UMAprotocol/dev-quickstart.git
cd dev-quickstart
yarn
```

#### 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:

```bash
yarn hardhat compile
```

### Contract Implementation

The contract discussed in this tutorial can be found at `dev-quickstart/contracts/OptimisticArbitrator.sol`([here](https://github.com/UMAprotocol/dev-quickstart/blob/main/contracts/OptimisticArbitrator.sol)) within the repo.

#### Contract creation and initialisation

The constructor of the Optimistic Arbitrator contract takes two parameters:

`_finderAddress` the Finder contract stores the addresses of all of the other relevant UMA contracts, which vary from chain to chain.

`_currency` the collateral token used to pay fees.

```solidity
constructor(address _finderAddress, address _currency) {
    finder = FinderInterface(_finderAddress);
    currency = IERC20(_currency);
    oo = OptimisticOracleV2Interface(finder.getImplementationAddress(OracleInterfaces.OptimisticOracleV2));
}
```

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](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/implementation/Finder.sol) contract.

#### Making an assertion

The following function allows a user to make an assertion. Behind the scenes it uses an `OO.priceRequest` with a `YES_OR_NO_QUERY` [price identifier](/resources/approved-price-identifiers). This imposes a set of rules on how the assertion is formulated and formatted, as specified in [UMIP-107](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md).\
\
The `makeAssertion` function takes the following arguments:

* `timestamp` timestamp used to identify the assertion, usually the current timestamp.
* `ancillaryData` the byte-converted question for which we want to assert (e.g. `'q: Was the price of BTC above 18000 USD for the duration of October 10 2022 UTC time considering top 5 volume weighted markets'`)
* `assertedValue` our response to the question. (e.g. `1e18` for yes `0` for no)
* `bond` the bond in the `currency` defined in the constructor that we want to require on top of the [`finalFee`](/resources/approved-collateral-types) (\~1,500 USD worth of the currency). The [`finalFee`](/resources/approved-collateral-types) can be viewed as the minimum bond required by the system, and the bond is any additional amount we require on top of that.
* `liveness` time period during which the proposal can be disputed

Note: The `makeAssertion` function requires the caller to approve the OA to spend the `bond+finalFee` amount of the `currency`.

```solidity
function makeAssertion(
    uint256 timestamp,
    bytes memory ancillaryData,
    int256 assertedValue,
    uint256 bond,
    uint64 liveness
) public {
    _pullAndApprove(bond + _getStore().computeFinalFee(address(currency)).rawValue);
    _makeAssertion(timestamp, ancillaryData, assertedValue, bond, liveness);
}
```

#### Ratifying an assertion

Once an assertion has been made, it can be ratified. This function will, behind the scenes, dispute an existing assertion in the OA, i.e., an OO dispute will be made against the OO proposal made in `makeAssertion`. The method accepts only the two arguments that we utilized in `makeAssertion`:

* `timestamp` timestamp used to identify the assertion, usually the current timestamp.
* `ancillaryData` the byte-converted question which we want to assert.

Note: The `ratifyAssertion` function requires the caller to again approve the OA to spend the `bond+finalFee` amount of the `currency`.

```solidity
function ratifyAssertion(uint256 timestamp, bytes memory ancillaryData) public {
    OptimisticOracleV2Interface.Request memory request = oo.getRequest(
        address(this),
        priceIdentifier,
        timestamp,
        ancillaryData
    );
    _pullAndApprove(request.requestSettings.bond + _getStore().computeFinalFee(address(currency)).rawValue);
    oo.disputePriceFor(msg.sender, address(this), priceIdentifier, timestamp, ancillaryData);
}
```

#### Ratifying and asserting

This function allows the user to both assert and ratify, in a single transaction, hence simplifying the arguments. This is the function to utilize if we wish to initiate ratification immediately. This kind of pattern might be useful if you want to bypass the OO and directly use the UMA DVM when asking/asserting questions. We've seen this as a useful integration path in a number of projects that maintain their own internal escalation games within their contracts and just want to use the UMA DVM for arbitration.

`assertAndRatify` takes three arguments:

* `timestamp` timestamp used to identify the assertion, usually the current timestamp.
* `ancillaryData` the byte-converted question for which we want to assert (e.g. `'q: "Was the price of BTC above 18000 USD for the duration of October 10 2022 UTC time considering top 5 volume weighted markets"'`)
* `assertedValue` our response to the question. (e.g. `1e18` for yes `0` for no)

Note 1: Here, we do not set a `bond` on top of the `finalFee` because it is set to zero by default due to the fact that the `proposer` and `disputer` are the same wallet. Adding a bond would increase the final cost of ratification for users.

Note 2: The `assertAndRatify` function requires the caller to approve the OA to spend the `2*bond+finalFee` amount of the `currency`. In this function we are acting as the Optimistic Oracle requestor, proposer and disputer all in one transaction. Proposing and disputing a price both require the posting of the final fee and so to do these actions in one go we are required to pull 2x the final fee as payment.

```solidity
function assertAndRatify(
    uint256 timestamp,
    bytes memory ancillaryData,
    int256 assertedValue
) public {
    _pullAndApprove(2 * _getStore().computeFinalFee(address(currency)).rawValue);
    _makeAssertion(timestamp, ancillaryData, assertedValue, 0, 0);
    oo.disputePriceFor(msg.sender, address(this), priceIdentifier, timestamp, ancillaryData);
}
```

### Tests and deployment

All the unit tests covering the functionality described above are available [here](https://github.com/UMAprotocol/dev-quickstart/tree/main/test/OptimisticArbitrator). To execute all of them, run:

```bash
yarn test test/OptimisticArbitrator/*
```

Before deploying the contract check the comments on available environment variables in [the deployment script](https://github.com/UMAprotocol/dev-quickstart/blob/main/deploy/004_deploy_optimistic_arbitrator.ts).

In the case of the Görli testnet, the defaults would use the Finder instance that references the [Mock Oracle](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/test/MockOracleAncillary.sol) 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 Optimistic Arbitrator contract on Görli, run:

```shell
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat deploy --network goerli --tags OptimisticArbitrator
```

Optionally you can verify the deployed contract on Etherscan:

```bash
ETHERSCAN_API_KEY=YOUR_API_KEY yarn hardhat etherscan-verify --network goerli --license AGPL-3.0 --force-license --solc-input
```

### 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:

```
NODE_URL_5=YOUR_GOERLI_NODE MNEMONIC=YOUR_MNEMONIC yarn hardhat console --network goerli
```

**Initial setup**

You will need to get the libraries and connect to the necessary contracts that we are going to use. In this tutorial we are using the `TestnetERC20` token as the `currency` as described in the deployment script.

```javascript
const { getAbi, getAddress } = require("@uma/contracts-node");
const { ethers } = require("hardhat");
const signer = (await ethers.getSigners())[0];

const optimisticArbitratorDeployment = await deployments.get("OptimisticArbitrator");
const optimisticArbitrator = new ethers.Contract(
  optimisticArbitratorDeployment.address,
  optimisticArbitratorDeployment.abi,
  ethers.provider
);
const finder = new hre.ethers.Contract(
  "0xDC6b80D38004F495861E081e249213836a2F3217", // Finder address used in the deployment
  getAbi("Finder"),
  ethers.provider
);
const store = new hre.ethers.Contract(
  await finder.getImplementationAddress(ethers.utils.formatBytes32String("Store")),
  getAbi("Store"),
  ethers.provider
);
const currency = new hre.ethers.Contract(
  await optimisticArbitrator.currency(),
  getAbi("TestnetERC20"),
  ethers.provider
);
const mockOracle = new hre.ethers.Contract(
  await finder.getImplementationAddress(ethers.utils.formatBytes32String("Oracle")),
  getAbi("MockOracleAncillary"),
  ethers.provider
);
```

**Make an assertion**

First we need to mint the `bond + finalFee` amount and approve the Optimistic Arbitrator to pull the tokens. This amount of `currency` tokens will be used to propose the assertion to the Optimistic Oracle behind the scenes:

```javascript
const bond = ethers.utils.parseUnits("500", 18);
const finalFee = await store.computeFinalFee(currency.address);
const totalAmount = bond.add(finalFee.rawValue);

await (await currency.connect(signer).allocateTo(signer.address, totalAmount)).wait();
await (await currency.connect(signer).approve(optimisticArbitrator.address, totalAmount)).wait();
```

Then we can proceed to make the assertion in the Optimistic Arbitrator:

```javascript
const YES_ANSWER = ethers.utils.parseUnits("1", 18);

let requestTimestamp = await (await ethers.provider.getBlock("latest")).timestamp;

const ancillaryData = ethers.utils.toUtf8Bytes(
  `q: "Was the price of BTC was above 18000 USD for the duration of October 10 2022 UTC time considering top 5 volume weighted markets"`
);

await (
  await optimisticArbitrator.connect(signer).makeAssertion(
    requestTimestamp,
    ancillaryData,
    YES_ANSWER, // We are asserting affirmatively the ancilliaryData yes or no question
    bond,
    60 // 1 minute for test purposes
  )
).wait();
```

Then, wait one minute for the proposal's liveness time to expire. Then, we may settle the assertion to receive back the `bond + finalFee` as we haven't been disputed. We also can verify the assertion's result that we proposed, `YES_ANSWER`, meaning that the assertion has been accepted.

```javascript
await (await optimisticArbitrator.connect(signer).settleAndGetResult(requestTimestamp, ancillaryData)).wait();
const result = await optimisticArbitrator.connect(signer).getResult(requestTimestamp, ancillaryData); // YES_ANSWER
console.log("The assertion has been accepted :", result.eq(YES_ANSWER));
```

**Make assertion and ratify**

To ratify an assertion and escalate its resolution to the DVM, we will utilize the `assertAndRatify` method, which combines assertion and ratification into a single transaction.

The Optimistic Arbitrator must be approved once more, but this time the fee is double the `finalFee`, as both proposing and disputing in the Optimistic Oracle require placing a bond.

At the end of the procedure, we will receive half of the bond, or `1 x finalFee`. The remainder (`1 x finalFee`) will be consumed; asserting and ratifying will cost the caller `1 x finalFee`:

```javascript
const totalAmountAssertAndRatify = ethers.BigNumber.from(2).mul(bond.add(finalFee.rawValue));
requestTimestamp = await (await ethers.provider.getBlock("latest")).timestamp;

await (await currency.connect(signer).allocateTo(signer.address, totalAmount)).wait();
await (await currency.connect(signer).approve(optimisticArbitrator.address, totalAmount)).wait();
```

We can then proceed to assert and verify:

```javascript
let receipt = await (await optimisticArbitrator.connect(signer).assertAndRatify(requestTimestamp, ancillaryData, YES_ANSWER)).wait();
```

As we are in a test environment, we can simulate the vote that will occur after we assert and ratify by pushing a price to the `mockOracle`:

```javascript
const disputedPriceRequest = (
  await mockOracle.queryFilter(mockOracle.filters.PriceRequestAdded(), receipt.blockNumber, receipt.blockNumber)
)[0];

await mockOracle.connect(signer).pushPrice(
    disputedPriceRequest.args.identifier,
    disputedPriceRequest.args.time,
    disputedPriceRequest.args.ancillaryData,
    YES_ANSWER // The original assertion is accepted
  );
```

At this point, we can call `settleAndGetResult` to settle the accepted price request and then confirm that the result validates the assertion:

```javascript
await (await optimisticArbitrator.connect(signer).settleAndGetResult(requestTimestamp, ancillaryData)).wait();
const resultRatify = await optimisticArbitrator.connect(signer).getResult(requestTimestamp, ancillaryData); // YES_ANSWER
console.log("Assert and ratify result is a yes answer :", resultRatify.eq(YES_ANSWER));
```

<br>


# Optimistic Oracle v3

This section showcases different design patterns for building contracts that integrate with the UMA Optimistic Oracle v3. These include:

* A [quick start tutorial](/developers/optimistic-oracle-v3/quick-start) showcasing the simplest possible OOv3 integration contract.
* Building example contracts:
  * [Prediction market](/developers/optimistic-oracle-v3/prediction-market)
  * [Insurance contract](/developers/optimistic-oracle-v3/in-depth-tutorial-insurance)
  * [A generic data assertion framework](/developers/optimistic-oracle-v3/data-asserter)
* A description of [Escalation Managers](/developers/optimistic-oracle-v3/escalation-managers) and how they can be used.
* Deploying [sandboxed oracle environment](/developers/optimistic-oracle-v3/sandboxed-oracle-environment) for testing the dispute flow.


# ASSERT\_TRUTH Deprecation

As of December 8th, 2025. [ASSERT\_TRUTH2](https://github.com/UMAprotocol/UMIPs/pull/629/files#diff-f961c56cb0d079742c67cdc3d0f1a36119daf1f46416a59c1f3ac68be7bade0c) is the intended identifier to be used with OOV3. The original [ASSERT\_TRUTH](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-170.md) identifier will be deprecated as of December 15, 2025. These identifiers have the same specifications and the change is only to break support for deprecated projects.

**Please note:** after these changes do not use the following OOV3 functions:

* the `assertTruthWithDefaults` function will always revert as it hardcodes in the ASSERT\_TRUTH identifier
* the `defaultIdentifier` public constant variable will return the deprecated ASSERT\_TRUTH identifier


# Using Blacklisting Tokens as Currency

Using a token that allows backlisting (e.g. USDC) as your [assertTruth](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol#L138) `currency` opens up a griefing vector that integrators should be aware of.

The [assertTruth](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol#L138) and [disputeAssertion](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol#L220) functions allow the caller to set any address as the `asserter` and `disputer` that will receive payouts if their assertion/dispute is correct upon settlement. If a bad actor calls these functions and specifies a blacklisted address for repayment, the [settle](https://github.com/UMAprotocol/protocol/blob/dd9d1fa988d8520ad36db145db13591e9a104fa9/packages/core/contracts/optimistic-oracle-v2/implementation/OptimisticOracleV2.sol#L498) function will revert and cause the request to be frozen unless the address is unblacklisted. This costs malicious users their bonds and does not result in any gain, but freezing the assertion could cause issues for the integrator.

Integrations that use tokens with blacklisting functionality should ensure that they have a way to manage frozen assertions (e.g. an admin function that can ignore a frozen assertion) without negatively affecting their integration.


# Quick start

0 to 1 integration with the Optimistic Oracle V3.

The primary integration point into the UMA ecosystem is the Optimistic Oracle V3 (OOV3). The OOV3 is an ***oracle for arbitrary off-chain data*** which leverages an interactive escalation game between *asserters* and *disputers* and is secured by *economic incentives*. It differs from the Optimistic Oracle V2 (getting started can be found [here](/developers/optimistic-oracle/getting-started)) by being easier to reason about and simpler in integration points.

This getting started tutorial will show you how to go from 0 to 1 with the OOV3 by executing the simplest possible assertion flow. Later in the docs you can find more information on how the OOV3 works and dig deeper into its mechanism and more sophisticated code examples.

### **A minimum viable OOV3 integration**

The OOV3 works by making a truth claim about the world, stating that something has happened or is true. Once asserted, the assertion enters a challenge period wherein someone can disagree with the assertion, by disputing it. If no one disputes it during the challenge window the statement is taken as true. If disputed, the outcome is arbitrated using the UMA DVM (more info on how this works here).

In this tutorial you will be working through a simple smart contract that asserts the following truth: `Statement: Argentina won the 2022 Fifa world cup in Qatar.`

Once through the challenge window, you will use the assertion in your resolution contract. This shows the full non-dispute lifecycle of an OOV3 data assertion. It will give you the basic intuition as to how the Optimistic Asserter works without much overhead and is a great starting point before digging deeper. Let's get started!

### **Prerequisites**

To complete this tutorial you will nee&#x64;**:**

1. Metamask installed in a Chromium based browser (such as [Google Chrome](https://www.google.com/chrome/)) If you don't have it already, you can get Metamask [here](https://metamask.io/).
2. A wallet set up in Metamask.
3. [Görli](https://goerli.net/) test ETH to send test transactions. You can get GETH from [Alchemy's faucet](https://goerlifaucet.com/).

### Asserting a truth

First, we will work through the basic flow for *asserting a truth to the oracle*. In this example, we are making a statement about the outcome of a sports event, but the statement could be much more complex; it could power any kind of smart contract system requiring data.

The contract used in this tutorial is meant to be a simple data assertion flow. The contract exposes a simple `assertTruth` function which asserts the truth to the OA about the outcome of the world cup.

1. [Go to this example contract on Remix](https://remix.ethereum.org/#activate=solidity,fileManager\&version=soljson-v0.8.16+commit.07a7930e.js\&optimize=false\&runs=200\&gist=17a8a29b2f8ae432e8bac0b88cff8bb1\&call=fileManager//open//gist-17a8a29b2f8ae432e8bac0b88cff8bb1/OOV3_GettingStarted.sol). This gives you the minimum assertion flow. Read the contract and the comments listed within.
2. In the far left hand menu, click the link to deploy and run transactions (which looks like the Ethereum logo and a right arrow).
3. In the "Environment" dropdown, choose "Injected Provider," and connect to your wallet. **Make sure you are connected to Görli within your metamask wallet, that has Görli test ETH!** You don't want to deploy to a real network and spend real gas, and the Görli Optimistic Oracle address hardcoded into the contract will not work on other networks.
4. Under the "Contract" dropdown, select `OOV3_GettingStarted`.
5. Click "Deploy" and confirm the transaction with your wallet.
6. You should now see the `OOV3_GettingStarted` contract under "Deployed Contracts". Clicking the dropdown carrot will reveal buttons for each of the functions in the contract.
7. Click `assertTruth` to request the data specified in the contract's ancillary data, asserting the truth about the outcome of the Fifa world cup. This will submit a data assertion to the Optimistic Asserter and the assertion will enter the challenge period.

What we've done in the above steps is: a) deploy a smart contract and b) submit a "data assertion" to the Optimistic Oracle V3 through the call to the Optimistic Oracle V3's `assertTruthWithDefaults` function. Now, the assertion has entered the challenge window and can be disputed by someone who disagrees with the assertion. If you click the `getAssertion` you can see some of the fields that are associated with the assertion. For more details on what these fields mean you can look at the OOV3 interface [here](https://github.com/UMAprotocol/protocol/blob/7a93650a7494eaee83756382a18ecf11314499cf/packages/core/contracts/optimistic-oracle-v3/interfaces/OptimisticOracleV3Interface.sol).

The default liveness for an assertion is 120 seconds (2 minutes). Wait this time before going to the next step.

### Settling the assertion

Once the assertion has settled, and assuming no one has disputed it, we can settle it! Do the following:

1. Click `settleAndGetAssertionResult` in remix. This will send a transaction to settle the assertion and return the settlement value.
2. Wait for the transaction to mine.
3. Once mined you can expand the transaction output block inline with the green tick arrow. If you scroll down here you can find some information about the assertion, such as the settlement value (which should be `true` as it was not disputed).

<figure><img src="/files/SLsTTPyteeZxw1Rr8TRs" alt=""><figcaption></figcaption></figure>

4. Now, you can call `getAssertion` and `getAssertionResult` from remix and see the outputs. `getAssertion` has changed such the `settled` value is `true` and `getAssertionResult` now returns `true` as the assertion was deemed valid as it was not disputed.

### Next Steps

Hopefully you got a basic understanding of the Optimistic Oracle V3 request flow from this getting started guide. Note that this kind of example would not really work on mainnet as: a) there were no bonds for the asserter (and therefore no rewards for disputers) b) the challenge window was too short. The Optimistic Oracle V3 works due to economic incentives between the asserter and disputers, which was absent in this example to keep things simple. For some incentive compatible examples check out some of the example tutorials where we walk through more details on the functions discussed in this guide. Those can be found [here](/developers/optimistic-oracle-v3).


# Prediction Market

Building a prediction market that uses the UMA Optimistic Oracle V3 for settlement and event identification

This section covers the [Prediction Market contract](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/PredictionMarket.sol), which is available in the Optimistic Oracle V3 [quick-start repo](https://github.com/UMAprotocol/dev-quickstart-oov3) and enables the creation of a binary prediction market using [Optimistic Oracle V3 (OOV3)](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol) assertions.

### Prediction Market

A prediction market smart contract allows individuals to make predictions and place bets on the likelihood of different outcomes for a future event. These types of contracts can be used for any kind of events such as:

1. Sports games
2. Cryptocurrency price predictions
3. Product launches
4. Political policy decisions

This smart contract enables the creation of prediction markets based on any off-chain event with three possible outcomes, two of which are chosen by the market creator and the third reflecting the remaining outcomes (i.e. tie or a draw in a sporting event).

By participating in the prediction market, individuals can create outcome tokens (shares) and buy and sell them of the different outcomes based on their assessment of the likelihood of each outcome occurring. The market determines the price of each share based on supply and demand, and individuals can profit by purchasing shares whose value grows as the likelihood of a certain event increases. Nevertheless, this contract does not cover the selling of outcome tokens; only their creation is covered.

### Development environment

This project uses [forge](https://github.com/foundry-rs/foundry/tree/master/forge) as the Ethereum testing framework. You will also need to install Foundry, refer to [Foundry installation documentation](https://book.getfoundry.sh/getting-started/installation) if you don’t have it already.

You will also need `git` for cloning the repository, as well as `bash` shell and `jq` tool in order to parse transaction outputs when interacting with deployed contracts.

Clone the UMA [Optimistic Oracle V3 quick-start repository](https://github.com/UMAprotocol/dev-quickstart-oov3) and install the dependencies:

```bash
git clone https://github.com/UMAprotocol/dev-quickstart-oov3.git
cd dev-quickstart-oov3
forge install
```

### Contract implementation

The contract discussed in this tutorial can be found at `dev-quickstart-oov3/src/PredictionMarket.sol` ([here](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/PredictionMarket.sol)) within the repo.

#### Contract creation and initialization

To initialize the state variables of the contract, the constructor takes three parameters:

1. `_finder`: address that represents the Finder contract, which is used to locate and retrieve other contracts within the system.
2. `_currency`: address that represents the currency that will be used for trading in the markets created by this contract.
3. `_optimisticOracleV3`: address that represents the OptimisticOracleV3, contract to assert truths about the world which are verified using an optimistic escalation game.

```solidity
    constructor(
        address _finder,
        address _currency,
        address _optimisticOracleV3
    ) {
        finder = FinderInterface(_finder);
        require(_getCollateralWhitelist().isOnWhitelist(_currency), "Unsupported currency");
        currency = IERC20(_currency);
        oo = OptimisticOracleV3Interface(_optimisticOracleV3);
        defaultIdentifier = oo.defaultIdentifier();
    }
```

#### Market creation

The `initializeMarket()` function is used to create a new market in the Prediction Market contract.

Once the contract has been deployed, anyone can call `initializeMarket()` after approving the optional `reward` amount to be paid to the wallet that runs the assertion.

```solidity
    function initializeMarket(
        string memory outcome1, // Short name of the first outcome.
        string memory outcome2, // Short name of the second outcome.
        string memory description, // Description of the market.
        uint256 reward, // Reward available for asserting true market outcome.
        uint256 requiredBond // Expected bond to assert market outcome (OOv3 can require higher bond).
    ) public returns (bytes32 marketId) { ... }
```

To create a new market, the `initializeMarket()` function is called with the following parameters:

* `outcome1`: A short name for the first outcome (i.e., "yes").
* `outcome2`: A short name for the second outcome (i.e., "no").
* `description`: A description of the market and the event being predicted.
* `reward`: The amount of currency (in Wei) that will be available as a reward for the user that runs that creates the assertion in the OOV3, necessary to settle the market once it's ready.
* `requiredBond`: The amount of currency (in Wei) that users must bond to assert the outcome of the event.

Once the input checks are passed, the function creates two new ERC20 tokens (one for each outcome) using the `ExpandedERC20` contract, which extends the standard ERC20 contract by adding the ability to mint and burn tokens. The Prediction Market is assigned the minter and burner roles to simplify how the outcome tokens handled when creating, redeeming or settling the tokens.

The `Market` is then created and stored internally associated to a `marketId` returned by the `initializeMarket()` and emitted in the `MarketInitialized` event at the end of the process together with the parameters defining the market.

#### Create outcome tokens

The `createOutcomeTokens` function mints a pair of tokens representing the value of outcome1 and outcome2 for a given market identified by `marketId`. This allows participants to trade on the outcome of the market by exchanging these tokens with each other outside of the scope of the Prediction Market contract.

The `createOutcomeTokens` function takes two parameters:

* `tokensToCreate`: an unsigned integer (`uint256`) value representing the number of tokens to be created for each outcome. The total number of tokens created will be twice this value, as two outcome tokens are created for each market.
* `marketId`: a `bytes32` value representing the unique identifier of the market for which the outcome tokens are being created.

The function first checks if the market exists. If the market exists, it transfers the specified amount of currency tokens from the caller to the contract using `safeTransferFrom` function from `currency` contract instance.

Then, the function mints `tokensToCreate` amount of both outcome1 and outcome2 tokens to the caller using the `mint` function of the respective `ExpandedERC20` token instances. Finally, it emits an event to notify that tokens have been created.

It is important to note that before calling this function, the caller must approve the contract to spend the required amount of currency tokens by calling the `approve` function on the `currency` contract instance.

#### Redeem outcome tokens

The `redeemOutcomeTokens` function burns an equal amount of `outcome1Token` and `outcome2Token` tokens for a given market and transfers the corresponding settlement currency tokens to the caller's account.

The function takes two parameters:

* `marketId`: a `bytes32` value representing the unique identifier of the market for which the tokens are being redeemed.
* `tokensToRedeem`: an unsigned integer (`uint256`) value representing the number of tokens of each outcome to be redeemed. The total number of tokens redeemed will be twice this value, as two outcome tokens are burned for each market.

Both parameters are passed to the function as arguments when it is called.

The function first retrieves the Market data from the storage using the provided marketId. Then, it checks if the outcome1Token is not equal to the zero address, which indicates the market exists.

Next, it burns the specified number of tokens for both outcome1Token and outcome2Token tokens using the `burnFrom()` function of their respective contracts, which decreases the balance of the caller's account.

Finally, it transfers the specified number of settlement currency tokens from the contract's account to the caller's account using the `safeTransfer()` function. The function emits a `TokensRedeemed` event, which includes the marketId, the caller's address, and the number of tokens redeemed.

#### Settle outcome tokens

The `settleOutcomeTokens` function allows a user to settle their outcome tokens for a given market and receive a payout in the settlement currency. The payout depends on the resolved market outcome and the number of tokens burned for each outcome.

The function takes one parameter:

* `marketId`: a `bytes32` value representing the unique identifier of the market for which the outcome tokens are being settled.

The `marketId` parameter is passed to the function as an argument when it is called.

The function first retrieves the Market data from the storage using the provided `marketId`. It then checks if the market has been resolved by verifying that the `resolved` flag in the market struct is set to `true`. If the market has not been resolved, the function throws an error.

Next, the function determines how many `outcome1Token` and `outcome2Token` tokens the caller has by calling the `balanceOf()` function of their respective contracts. The balances are stored in `outcome1Balance` and `outcome2Balance` variables.

After that, the function calculates the payout based on the resolved market outcome and the number of tokens burned for each outcome. If the market was resolved to the first outcome, then the payout equals the balance of `outcome1Token` while `outcome2Token` provides nothing. If the market was resolved to the second outcome, then the payout equals the balance of `outcome2Token` while `outcome1Token` provides nothing. If the market was resolved to the split outcome, then both outcome tokens provide half of their balance as currency payout.

Finally, the function burns the caller's outcome tokens using the `burnFrom()` function of their respective contracts, transfers the calculated payout from the contract's account to the caller's account using the `safeTransfer()` function, and emits a `TokensSettled` event, which includes the marketId, the caller's address, the payout amount, and the number of `outcome1Token` and `outcome2Token` tokens burned. The calculated payout is returned by the function.

#### Assert market

The function `assertMarket` is used to assert a market with any of the three possible outcomes: outcome1, outcome2, or unresolvable. The function takes two arguments:

* `marketId`, which is a unique identifier for the market
* `assertedOutcome`, which is a string representing the asserted outcome.

The function first checks that the market exists by verifying that the outcome1 token is not a null address. It then computes the hash of the asserted outcome using the `keccak256` function and checks that the market does not already have an active or resolved assertion. It also checks that the asserted outcome is one of the three allowed outcomes.

If all checks pass, the function sets the `assertedOutcomeId` of the market to the hash of the asserted outcome and computes the bond required to make the assertion. If the bond required by the market is higher than the minimum bond required by the oracle, the market bond is used instead. The function then composes a claim with the asserted outcome and the market description, pulls the bond from the caller's account, approves it for the oracle, and makes the assertion using the `_assertTruthWithDefaults` function.

Finally, the function stores information about the asserted market and emits a `MarketAsserted` event with the market ID, the asserted outcome, and the assertion ID.

### Tests and deployment

To run the contracts tests written in solidity with forge run the following command:

```bash
forge test --match-path *PredictionMarket*
```

#### Deployment

Run a local node with anvil (included in foundry toolkit) in a console:

```bash
anvil
```

In a second console continue running the rest of commands. Let's first export the environment variables for the wallets to use:

```bash
export MNEMONIC="test test test test test test test test test test test junk"
export USER_ID=1
export ASSERTER_ID=2
export DEPLOYER_WALLET=$(cast wallet address --mnemonic "$MNEMONIC")
export USER_WALLET=$(cast wallet address --mnemonic "$MNEMONIC" --mnemonic-index $USER_ID)
export ASSERTER_WALLET=$(cast wallet address --mnemonic "$MNEMONIC" --mnemonic-index $ASSERTER_ID)
export ETH_RPC_URL="http://127.0.0.1:8545"
```

Then Deploy the UMA Oracle Sandbox contracts to be used by running:

```bash
forge script script/OracleSandbox.s.sol \
	--fork-url $ETH_RPC_URL \
	--mnemonics "$MNEMONIC" \
	--sender $DEPLOYER_WALLET \
	--broadcast
```

Find in the logs the `FINDER_ADDRESS` and export it with:

```bash
export FINDER_ADDRESS=<FINDER_ADDRESS>
```

Then use the Finder to export rest of addresses by running the following commands:

```bash
export OOV3_ADDRESS=$(cast call $FINDER_ADDRESS "getImplementationAddress(bytes32)(address)" \
	$(cast --format-bytes32-string "OptimisticOracleV3"))
export DEFAULT_CURRENCY_ADDRESS=$(cast call $OOV3_ADDRESS "defaultCurrency()(address)")
```

We are ready to deploy the Prediction Market contract with the following command:

```bash
export PREDICTION_MARKET_ADDRESS=$(forge create src/PredictionMarket.sol:PredictionMarket \
	--json \
	--mnemonic "$MNEMONIC" \
	--constructor-args $FINDER_ADDRESS $DEFAULT_CURRENCY_ADDRESS $OOV3_ADDRESS \
	| jq -r .deployedTo)
echo "PREDICTION MARKET DEPLOYED TO" $PREDICTION_MARKET_ADDRESS
```

### Interacting with deployed contract

It's time to initialise a market. We can first export the market parameters to use.

Here the description shows that it's a market based on the outcome of a match between two teams. The two possible outcomes are `yes` or `no.` We offer 100 units of `DEFAULT_CURRENCY` to the asserter of the claim and require a bond of 5,000 units of `DEFAULT_CURRENCY` to assert or dispute the assertion.

```bash
export DESCRIPTION="The Glacial Storms beat the Electric Titans on March 8, 2023 at 3:00 PM UTC, \
	which is equivalent to the Unix timestamp 1686258000 seconds."
export OUTCOME_ONE="yes"
export OUTCOME_TWO="no"
export REWARD=$(cast --to-wei 100)
export REQUIRED_BOND=$(cast --to-wei 5000)
```

We need to mint the amount of asserter rewards and approve them before creating the market:

```bash
cast send --mnemonic "$MNEMONIC" \
	$DEFAULT_CURRENCY_ADDRESS "allocateTo(address,uint256)" $DEPLOYER_WALLET $REWARD
cast send --mnemonic "$MNEMONIC" \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" $PREDICTION_MARKET_ADDRESS $REWARD
```

Then we are ready to initialise the market with the `DEPLOYER_WALLET`

```bash
export MARKET_ID_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" \
	$PREDICTION_MARKET_ADDRESS \
	"initializeMarket(string,string,string,uint256,uint256)(bytes32)" \
	$OUTCOME_ONE $OUTCOME_TWO "$DESCRIPTION" $REWARD $REQUIRED_BOND \
	| jq -r .transactionHash)
export MARKET_ID=$(cast receipt --json $MARKET_ID_TX | jq -r .logs[-1].topics[1])
export OUTCOME_TOKEN_ONE_ADDRESS=$(cast --abi-decode \
	"MarketInitializedNonIndexed()(string,string,string,address,address,uint256,uint256)" \
	$(cast receipt --json $MARKET_ID_TX | jq -r .logs[-1].data) | awk 'NR==4 {print $1}')
export OUTCOME_TOKEN_TWO_ADDRESS=$(cast --abi-decode \
	"MarketInitializedNonIndexed()(string,string,string,address,address,uint256,uint256)" \
	$(cast receipt --json $MARKET_ID_TX | jq -r .logs[-1].data) | awk 'NR==5 {print $1}')
```

Then we can mint the necessary tokens to then create the outcome tokens:

```bash
export AMOUNT=$(cast --to-wei 10000)
cast send --mnemonic "$MNEMONIC" \
	$DEFAULT_CURRENCY_ADDRESS "allocateTo(address,uint256)" $DEPLOYER_WALLET $AMOUNT
cast send --mnemonic "$MNEMONIC" \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" $PREDICTION_MARKET_ADDRESS $AMOUNT
```

We can now create the outcome tokens. With an amount 10,000 units of `DEFAULT_CURRENCY` we get 10,000 `OUTCOME_TOKEN_ONE` and 10,000 `OUTCOME_TOKEN_TWO` tokens:

```bash
cast send --mnemonic "$MNEMONIC" \
	$PREDICTION_MARKET_ADDRESS "createOutcomeTokens(bytes32,uint256)" $MARKET_ID $AMOUNT
```

```bash
echo "BALANCE OUTCOME TOKEN ONE" $(cast call $OUTCOME_TOKEN_ONE_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "BALANCE OUTCOME TOKEN TWO" $(cast call $OUTCOME_TOKEN_TWO_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "BALANCE DEFAULT_CURRENCY" $(cast call $DEFAULT_CURRENCY_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
```

At any point before the market is settled we can redeem outcome tokens. By redeeming an amount we are burning the same amount of `OUTCOME_TOKEN_ONE` and `OUTCOME_TOKEN_TWO` to receive that amount of `DEFAULT_CURRENCY`:

```bash
cast send --mnemonic "$MNEMONIC" \
	$PREDICTION_MARKET_ADDRESS "redeemOutcomeTokens(bytes32,uint256)" \
	$MARKET_ID $(cast --to-wei 5000)
```

After redeeming 5,000 tokens we can see how both balances of `OUTCOME_TOKEN_ONE` and `OUTCOME_TOKEN_TWO` have decreased by 5,000 and `DEFAULT_CURRENCY` has increased that same amount.

```bash
echo "BALANCE OUTCOME TOKEN ONE" $(cast call $OUTCOME_TOKEN_ONE_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "BALANCE OUTCOME TOKEN TWO" $(cast call $OUTCOME_TOKEN_TWO_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "BALANCE DEFAULT_CURRENCY" $(cast call $DEFAULT_CURRENCY_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
```

Now, let's simulate how the `DEPLOYER_WALLET` would trade one position of the market by transferring the remaining 5,000 `OUTCOME_TOKEN_ONE` to another user. By doing this, `DEPLOYER_WALLET` is now only exposed to the outcome two ("no") because he only holds `OUTCOME_TOKEN_TWO`. On the other side, `USER_WALLET` is exposed to the outcome one ("yes") as he has traded some other currency against `OUTCOME_TOKEN_ONE`. This trade is out of the scope of this example, thats why we simulate it by running the following transfer:

```bash
cast send --mnemonic "$MNEMONIC" \
	$OUTCOME_TOKEN_ONE_ADDRESS "transfer(address,uint256)" $USER_WALLET $(cast --to-wei 5000)
```

At this point, let's imagine that the match between The Glacial Storms and the Electric Titans has taken place and that The Glacial Storms won. Then anyone can now `assert` that this has occurred by calling `assertMarket` with outcome `"yes"` as the claim defined in `DESCRIPTION` is true. We can do it, from the `ASSERTER_WALLET`, by running the following command:

```bash
cast send --mnemonic "$MNEMONIC" \
	$DEFAULT_CURRENCY_ADDRESS "allocateTo(address,uint256)" \
	$ASSERTER_WALLET $REQUIRED_BOND
cast send --mnemonic "$MNEMONIC" --mnemonic-index $ASSERTER_ID \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" \
	$PREDICTION_MARKET_ADDRESS $REQUIRED_BOND
export ASSERTION_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $ASSERTER_ID \
	$PREDICTION_MARKET_ADDRESS "assertMarket(bytes32,string)" $MARKET_ID "yes" \
	| jq -r .transactionHash)
export ASSERTION_ID=$(cast receipt --json $ASSERTION_TX | jq -r .logs[-1].topics[2])
```

Now, let's move forward 2 hours to go pass the challenge window of the assertion:

```bash
cast rpc evm_increaseTime 7200
cast rpc evm_mine
```

Now the assertion can be settled in the `OptimisticOracleV3`. We can do it by running the following command:

```bash
cast send --mnemonic "$MNEMONIC" \
	$OOV3_ADDRESS "settleAssertion(bytes32)" $ASSERTION_ID
```

We can now check how the `ASSERTER_WALLET` has received back the assertion bond plus the reward:

```bash
echo "ASSERTER WALLET BALANCE DEFAULT_CURRENCY" \
	$(cast call $DEFAULT_CURRENCY_ADDRESS "balanceOf(address)(uint256)" $ASSERTER_WALLET)
```

Now, both the `DEPLOYER_WALLET` and `USER_WALLET` can settle their outcome tokens:

```bash
cast send --mnemonic "$MNEMONIC" \
	$PREDICTION_MARKET_ADDRESS "settleOutcomeTokens(bytes32)" $MARKET_ID
cast send --mnemonic "$MNEMONIC" --mnemonic-index $USER_ID \
	$PREDICTION_MARKET_ADDRESS "settleOutcomeTokens(bytes32)" $MARKET_ID
```

Finally we can see how the `USER_WALLET` won the bet, as he got `OUTCOME_TOKEN_ONE` so he now has 5,000 `DEFAULT_CURRENCY` and the deployer wallet only has 5,000 `DEFAULT_CURRENCY` from his initial 10,000:

```bash
echo "DEPLOYER WALLET BALANCE OUTCOME TOKEN ONE" $(cast call $OUTCOME_TOKEN_ONE_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "DEPLOYER WALLET BALANCE OUTCOME TOKEN TWO" $(cast call $OUTCOME_TOKEN_TWO_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "DEPLOYER WALLET BALANCE DEFAULT_CURRENCY" $(cast call $DEFAULT_CURRENCY_ADDRESS \
	"balanceOf(address)(uint256)" $DEPLOYER_WALLET)
echo "USER WALLET BALANCE OUTCOME TOKEN ONE" $(cast call $OUTCOME_TOKEN_ONE_ADDRESS \
	"balanceOf(address)(uint256)" $USER_WALLET)
echo "USER WALLET BALANCE OUTCOME TOKEN TWO" $(cast call $OUTCOME_TOKEN_TWO_ADDRESS \
	"balanceOf(address)(uint256)" $USER_WALLET)
echo "USER WALLET BALANCE DEFAULT_CURRENCY" $(cast call $DEFAULT_CURRENCY_ADDRESS \
	"balanceOf(address)(uint256)" $USER_WALLET)
```


# Insurance

Using the Optimistic Oracle V3 to allow for verification of insurance claims.

This section covers the [Insurance contract](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/Insurance.sol), which is available in the Optimistic Oracle V3 [quick-start repo](https://github.com/UMAprotocol/dev-quickstart-oov3). This tutorial shows an example of how insurance claims can be resolved and settled through the [Optimistic Oracle V3 (OOV3)](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol) contract.

### Insurance Contract

This smart contract allows insurers to issue insurance policies by depositing the insured amount, designating the insured beneficiary, and describing the insured event.

Anyone can request payout to the insured beneficiary at any time. The Insurance contract resolves the claim through the Optimistic Oracle V3 by asserting that the insured event has occurred as of request time using the default identifier `ASSERT_TRUTH` specified in [UMIP-170](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-170.md).

If the claim is confirmed and settled through the Optimistic Oracle V3, this contract automatically pays out insurance coverage to the beneficiary. If the claim is rejected, the policy continues to be active and ready for subsequent claim attempts.

There is no limit to the number of payout requests that can be made of the same policy, however, only the first truthfully resolved request will settle the insurance payment, whereas the Optimistic Oracle V3 will settle bonds for all requests.

### Development environment

This project uses [forge](https://github.com/foundry-rs/foundry/tree/master/forge) as the Ethereum testing framework. You will also need to install Foundry, refer to [Foundry installation documentation](https://book.getfoundry.sh/getting-started/installation) if you don’t have it already.

You will also need `git` for cloning the repository, as well as `bash` shell and `jq` tool in order to parse transaction outputs when interacting with deployed contracts.

Clone the UMA [Optimistic Oracle V3 quick-start repository](https://github.com/UMAprotocol/dev-quickstart-oov3) and install the dependencies:

```bash
git clone https://github.com/UMAprotocol/dev-quickstart-oov3.git
cd dev-quickstart-oov3
forge install
```

### Contract implementation

The contract discussed in this tutorial can be found at `dev-quickstart-oov3/src/Insurance.sol` ([here](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/Insurance.sol)) within the repo.

#### Contract creation and initialization

To initialize the state variables of the contract, the constructor takes two parameters:

1. `_defaultCurrency` identifies the token used for settlement of insurance claims, as well as the bond currency for assertions and disputes. This token should be approved as whitelisted UMA collateral. Please check [Approved Collateral Types](/resources/approved-collateral-types) for production networks or call `getWhitelist()` on the [Address Whitelist](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/common/implementation/AddressWhitelist.sol) contract for any of the test networks. Note that the deployed Optimistic Oracle V3 instance already has its `defaultCurrency` added to the whitelist, so it can also be used by the Insurance contract. Alternatively, you can approve a new token address with `addToWhitelist` method in the Address Whitelist contract if working in a sandboxed UMA environment.
2. `_optimisticOracleV3` is used to locate the address of UMA Optimistic Oracle V3. Address of `OptimisticOracleV3` contact can be fetched from the relevant [networks](https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks) file, if you are on a live network, or you can provide your own contract instance if deploying UMA Oracle contracts in your own sandboxed testing environment.

```solidity
    constructor(address _defaultCurrency, address _optimisticOracleV3) {
        defaultCurrency = IERC20(_defaultCurrency);
        oo = OptimisticOracleV3Interface(_optimisticOracleV3);
        defaultIdentifier = oo.defaultIdentifier();
    }
```

#### Issuing insurance

`issueInsurance` method allows any insurer to deposit `insuranceAmount` of `defaultCurrency` tokens by designating an insurance beneficiary (`payoutAddress`) and defining the insured event (`insuredEvent`). Before calling this method, the insurer should have approved this contract to spend the required amount of `defaultCurrency` tokens.

```solidity
    function issueInsurance(
        uint256 insuranceAmount,
        address payoutAddress,
        bytes memory insuredEvent
    ) public returns (bytes32 policyId) { ... }
```

Internally, the issued policy is stored in the `policies` mapping using the calculated `policyId` key that is generated by hashing the insured event and beneficiary address.

After pulling `insuranceAmount` from the caller in the `issueInsurance` method, the contract emits a `InsuranceIssued` event including the `policyId` parameter that should be used when claiming insurance.

#### Submitting insurance claim

Anyone can submit an insurance claim on the issued policy by calling the `requestPayout` method with the relevant `policyId` parameter. This method will make an assertion with the Optimistic Oracle V3. An assertion bond is required, hence the caller should have approved this contract to spend the required minimum amount of `defaultCurrency` tokens for the proposal bond (call `getMinimumBond` method on the Optimistic Oracle V3).

```solidity
    function requestPayout(bytes32 policyId) public returns (bytes32 assertionId) { ... }
```

After checking that the `policyId` represents a valid insurance policy, the contract gets the current `timestamp` and composes claim that the insured event has occurred as of request time, that is passed to the Optimistic Oracle V3 when making the assertion with the `assertTruth` method:

```solidity
        assertionId = oo.assertTruth(
            abi.encodePacked(
                "Insurance contract is claiming that insurance event ",
                policies[policyId].insuredEvent,
                " had occurred as of ",
                ClaimData.toUtf8BytesUint(block.timestamp),
                "."
            ),
            msg.sender, // asserter
            address(this), // callbackRecipient (the contract)
            address(0), // No sovereign security.
            assertionLiveness,
            defaultCurrency,
            bond,
            defaultIdentifier,
            bytes32(0) // No domain.
        );
        assertedPolicies[assertionId] = policyId;
```

Optimistic Oracle V3 pulls the required bond and returns `assertionId` that is used as a key when storing the linked `policyId` in the `assertedPolicies` mapping. This information will be required when receiving a callback from the Optimistic Oracle V3.

#### Disputing insurance claim

For the sake of simplicity this contract does not implement a dispute method, but the disputer can dispute the submitted claim directly through Optimistic Oracle V3 before the liveness passes by calling its `disputeAssertion` method:

```solidity
    function disputeAssertion(bytes32 assertionId, address disputer) external nonReentrant { ... }
```

The disputer should pass the `assertionId` from the request above, as well as the address for receiving back bond and rewards if the disputer was right.

If the claim is disputed, the request is escalated to the UMA DVM and it can be settled only after UMA voters have resolved it. To learn more about the DVM, see the docs section on the DVM: [how does UMA's Oracle work](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism).

#### Settling insurance claim

Similar to disputes, claim settlement should be initiated through the Optimistic Oracle V3 contract by calling its `settleAssertion` method with the same `assertionId` parameter:

```solidity
    function settleAssertion(bytes32 assertionId) public nonReentrant { ... }
```

In case the liveness has expired or a dispute has been resolved by the UMA DVM, this call would initiate a `assertionResolvedCallback` callback in the Insurance contract:

```solidity
    function assertionResolvedCallback(bytes32 assertionId, bool assertedTruthfully) public { ... }
```

Importantly, all callbacks should be restricted to accept calls only from the Optimistic Oracle V3 to avoid someone spoofing a resolved answer:

```solidity
        require(msg.sender == address(oo));
```

Depending on the resolved answer received in the `assertedTruthfully` callback parameter, this contract would either pay out the insured beneficiary if this was the first successful claim (in case of `true` representing that insurance claim was valid) or reject the payout:

```solidity
        if (assertedTruthfully) _settlePayout(assertionId);
...
    function _settlePayout(bytes32 assertionId) internal {
        bytes32 policyId = assertedPolicies[assertionId];
        Policy storage policy = policies[policyId];
        if (policy.settled) return;
        policy.settled = true;
        defaultCurrency.safeTransfer(policy.payoutAddress, policy.insuranceAmount);
        emit InsurancePayoutSettled(policyId, assertionId);
    }
```

### Tests and deployment

All the unit tests covering the functionality described above are available [here](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/test/Insurance.t.sol). To execute all of them, run:

```bash
forge test --match-path *Insurance*
```

#### Deployment

Before deploying and interacting with the contracts export the required environment variables:

* `ETHERSCAN_API_KEY`: your secret API key used for contract verification on Etherscan if deploying on a public network
* `ETH_RPC_URL`: your RPC node used to interact with the selected network
* `MNEMONIC`: your passphrase used to derive private keys of deployer (index 0) and any other addresses interacting with the contracts
* `FINDER_ADDRESS`: address of the [Finder](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/implementation/Finder.sol) contract used to locate other UMA ecosystem contracts (in order to resolve disputes you would need to use the one from a sandboxed environment). For Goerli, you can use:

  ```bash
  export FINDER_ADDRESS=0xE60dBa66B85E10E7Fd18a67a6859E241A243950e
  ```
* `DEFAULT_CURRENCY_ADDRESS`: address of the token used for insurance claim settlement. This is also used as oracle bonding currency, thus, needs to be added to whitelist either by UMA governance (production networks) or testnet administrator. On Goerli you can use [`0xe9448D94C9b033Ff50d3B14089043bD976fC1394`](https://goerli.etherscan.io/address/0xe9448d94c9b033ff50d3b14089043bd976fc1394) that is already whitelisted and can be minted by anyone using its `allocateTo` method

Use `cast` command from Foundry to locate the address of Optimistic Oracle V3:

```bash
export OOV3_ADDRESS=$(cast call $FINDER_ADDRESS "getImplementationAddress(bytes32)(address)" \
	$(cast --format-bytes32-string "OptimisticOracleV3"))
```

To deploy the Insurance contract, run `forge create` command.

```bash
export INSURANCE_ADDRESS=$(forge create --json src/Insurance.sol:Insurance \
	--mnemonic "$MNEMONIC" \
	--constructor-args $DEFAULT_CURRENCY_ADDRESS $OOV3_ADDRESS \
	| jq -r .deployedTo)
```

Finally, we can verify the deployed (if deployed to a public network) contract with `forge verify-contract`:

```bash
forge verify-contract \
	--chain-id $(cast chain-id) \
	--constructor-args $(cast abi-encode "constructor(address,address)" \
	$DEFAULT_CURRENCY_ADDRESS $OOV3_ADDRESS) \
	$INSURANCE_ADDRESS Insurance
```

### Interacting with deployed contract

The following section provides instructions on how to interact with the deployed contract from the foundry `cast` tool, though one can also use it for guidance for interacting through another interface (e.g. Remix or Etherscan).

#### Initial setup

Export required user addresses and their derivation indices:

```bash
export INSURER_ID=1
export INSURED_ID=2
export INSURER_ADDRESS=$(cast wallet address --mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID)
export INSURED_ADDRESS=$(cast wallet address --mnemonic "$MNEMONIC" --mnemonic-index $INSURED_ID)
```

Make sure the user addresses above have sufficient funding for the gas to execute the transactions.

#### Issue insurance

Make sure to have some amount of `DEFAULT_CURRENCY_ADDRESS` tokens to back potential insurance claim. If [`0xe9448D94C9b033Ff50d3B14089043bD976fC1394`](https://goerli.etherscan.io/address/0xe9448d94c9b033ff50d3b14089043bd976fc1394) was used on Goerli you can mint 10,000 DBT tokens to insurance issuer account:

```bash
export INSURANCE_AMOUNT=$(cast --to-wei 10000)
cast send --mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID \
	$DEFAULT_CURRENCY_ADDRESS "allocateTo(address,uint256)" $INSURER_ADDRESS $INSURANCE_AMOUNT
```

Approve `DEFAULT_CURRENCY_ADDRESS` to be pulled by the Insurance contract:

```bash
cast send --mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" $INSURANCE_ADDRESS $INSURANCE_AMOUNT
```

Issue the insurance policy and grab the resulting `policyId` from the emitted `InsuranceIssued` event (it should be the last emitted event in the transaction and indexed `policyId` is at topic index `1`):

```bash
export ISSUE_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID \
	$INSURANCE_ADDRESS \
	"issueInsurance(uint256,address,bytes)" \
	$INSURANCE_AMOUNT $INSURED_ADDRESS $(cast --from-utf8 "Bad things have happened") \
	| jq -r .transactionHash)
export POLICY_ID=$(cast receipt --json $ISSUE_TX | jq -r .logs[-1].topics[1])
```

If in doubt on the parsing of transaction receipt you can manually view full logs from tracing:

```bash
cast run $ISSUE_TX
```

#### Submit insurance claim

Get the expected oracle bond:

```bash
export BOND_AMOUNT=$(cast call $OOV3_ADDRESS \
	"getMinimumBond(address)(uint256)" $DEFAULT_CURRENCY_ADDRESS)
```

This should be zero for [`0xe9448D94C9b033Ff50d3B14089043bD976fC1394`](https://goerli.etherscan.io/address/0xe9448d94c9b033ff50d3b14089043bd976fc1394) on Goerli, but in case of other currencies make sure to have this amount of `DEFAULT_CURRENCY_ADDRESS` both on the insured account (for submitting the claim) and on the insurer's account (for disputing the claim). If bond amount is non-zero, also make sure to add approval:

```bash
cast send --mnemonic "$MNEMONIC" --mnemonic-index $INSURED_ID \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" $INSURANCE_ADDRESS $BOND_AMOUNT
```

Now initiate the insurance claim and grab the resulting `assertionId` from the emitted `InsurancePayoutRequested` event (it should be the last emitted event and indexed `assertionId` is at topic index `2`):

```bash
export ASSERTION_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $INSURED_ID \
	$INSURANCE_ADDRESS \
	"requestPayout(bytes32)" $POLICY_ID | jq -r .transactionHash)
export ASSERTION_ID=$(cast receipt --json $ASSERTION_TX | jq -r .logs[-1].topics[2])
```

If in doubt on the parsing of transaction receipt you can manually view full logs from tracing:

```bash
cast run $ASSERTION_TX
```

#### Dispute insurance claim

Before liveness passes, anyone (e.g. insurer) can dispute the claim through the Optimistic Oracle V3. In case of non-zero bond amount, they must add approval for Optimistic Oracle V3 to pull the bond:

```bash
cast send --mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID \
	$DEFAULT_CURRENCY_ADDRESS "approve(address,uint256)" $OOV3_ADDRESS $BOND_AMOUNT
```

Now initiate the dispute and export related transaction hash that we will need to collect additional request parameters for resolving the dispute:

```bash
export DISPUTE_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $INSURER_ID \
	$OOV3_ADDRESS "disputeAssertion(bytes32,address)" $ASSERTION_ID $INSURER_ADDRESS \
	| jq -r .transactionHash)
```

#### Settle insurance claim

Resolving disputes in production environment involves UMA token holders to vote on the request. Thus, testing is possible only in a sandboxed UMA ecosystem environment where the [Mock Oracle](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/test/MockOracleAncillary.sol) is used to resolve requests:

```bash
export MOCK_ORACLE_ADDRESS=$(cast call \
	$FINDER_ADDRESS "getImplementationAddress(bytes32)(address)" \
	$(cast --format-bytes32-string "Oracle"))
```

In order to resolve request Mock Oracle expects the following parameters:

* `identifier`: identifier that references instructions to resolve the request (Optimistic Oracle V3 and Insurance contract uses default identifier `ASSERT_TRUTH` specified in [UMIP-170](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-170.md).)
* `time`: timestamp when the disputed assertion was made
* `ancillaryData`: bytes encoded additional data required to resolve the request (Optimistic Oracle V3 includes `assertionId` and the address of the claiming asserter)
* `price`: numerical value to decide the outcome of the request (Optimistic Oracle V3 requires this to be `1e18` in order to resolve assertion as truthful)

The commands below export the required parameters and resolves the request at the Mock Oracle:

{% code overflow="wrap" %}

```bash
export IDENTIFIER=$(cast call $INSURANCE_ADDRESS "defaultIdentifier()(bytes32)")
export ASSERTION_TIME=$(cast block --json \
	$(cast tx --json $ASSERTION_TX | jq -r .blockNumber) | jq -r .timestamp)
export ANCILLARY_DATA=$(cast --from-utf8 $(echo assertionId:$(echo $ASSERTION_ID | sed 's/0x//' | tr [:upper:] [:lower:]),ooAsserter:$(echo $INSURED_ADDRESS | sed 's/0x//' | tr [:upper:] [:lower:])))

export PRICE=$(cast --to-wei 1)
cast send --mnemonic "$MNEMONIC" --mnemonic-index $INSURED_ID \
	$MOCK_ORACLE_ADDRESS \
	"pushPrice(bytes32,uint256,bytes,int256)" \
	$IDENTIFIER $ASSERTION_TIME $ANCILLARY_DATA $PRICE
```

{% endcode %}

Note that the syntax above for getting ancillary data involves stripping `0x` and transforming `assertionId` and `ooAsserter` to lowercase to replicate the logic how Optimistic Oracle V3 contract is composing the ancillary data. If in doubt, you can always check the emitted `PriceRequestAdded` event parameters from the Mock Oracle contract in the dispute transaction traces:

```bash
cast run $DISPUTE_TX
```

Now we can settle the request through the Optimistic Oracle V3 and observe the emitted `InsurancePayoutSettled` from our Insurance contract:

```bash
export SETTLE_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $INSURED_ID \
	$OOV3_ADDRESS "settleAssertion(bytes32)" $ASSERTION_ID \
	| jq -r .transactionHash)
cast run $SETTLE_TX
```

The above settlement transaction should also transfer `INSURANCE_AMOUNT` tokens to the insured beneficiary as well as return the assertion bond plus half of disputer's bond to the claim initiator.

Alternatively, if `0` value was resolved in `PRICE` (representing the asserted claim was not true), the settlement transaction should only return the bond plus half of claim initiator's bond to the disputer.


# Data Asserter

Using the Optimistic Oracle V3 to assert arbitrary off-chain data on-chain.

This section covers the [Data Asserter contract](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/DataAsserter.sol), which is available in the Optimistic Oracle V3 [quick-start repo](https://github.com/UMAprotocol/dev-quickstart-oov3). This tutorial shows an example of how to build an arbitrary data asserter contract, using the UMA [Optimistic Oracle V3 (OOV3)](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol) contract. This enables you to assert to the correctness of some off-chain process and store the output on-chain. This tutorial acts as a starting point to show how the OOV3 can be flexibility used in a sample integration.

### Data Asserter

This smart contract allows data providers to assert the correctness of a data point, within any arbitrary data set, and bring the result on-chain. This contract is designed to be maximally open ended, enabling any kind of data to be asserted. For example, and for illustrative purposes of this tutorial, we will assert weather data on-chain. Note, though, that this kind of assertion flow and the associated OOV3 integration could build a wide range of assertions with arbitrary complexity. For example you could use the same structure to bring on-chain complex data associated with Beacon chain validator set to build a Liquid staking derivative, enable complex off-chain computation with on-chain outputs or anything that has a defined deterministic input output relationship.

Anyone can assert a data point against the Data Asserter contract, with a known dataId and datapoint. Independent 3rd parties can then verify the correctness of this data against the dataId.

For our example we will be asserting the output of a very simple numerical computation: `69+420`. This is meant to show the basic idea without overcomplicating the off-chain part. Note, though, that the `dataId` prop used here could represent any kind of unique data identifier (a row in a database table, a validator address or a complex structure used to associate any kind of data to a given value.

### Development environment

This project uses [forge](https://github.com/foundry-rs/foundry/tree/master/forge) as the Ethereum testing framework. You will also need to install Foundry, refer to [Foundry installation documentation](https://book.getfoundry.sh/getting-started/installation) if you don’t have it already.

You will also need `git` for cloning the repository, as well as `bash` shell and `jq` tool in order to parse transaction outputs when interacting with deployed contracts.

Clone the UMA [Optimistic Oracle V3 quick-start repository](https://github.com/UMAprotocol/dev-quickstart-oov3) and install the dependencies:

```bash
git clone https://github.com/UMAprotocol/dev-quickstart-oov3.git
cd dev-quickstart-oov3
forge install
```

### Contract implementation

The contract discussed in this tutorial can be found at `dev-quickstart-oov3/src/DataAsserter.sol` ([here](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/src/DataAsserter.sol)) within the repo.

#### Contract creation and initialization

To initialize the state variables of the contract, the constructor takes two parameters:

1. `_defaultCurrency` parameter in the constructor identifies the token used for bonds of data assertions. This token should be approved as whitelisted UMA collateral. Please check [Approved Collateral Types](/resources/approved-collateral-types) for production networks or call `getWhitelist()` on the [Address Whitelist](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/common/implementation/AddressWhitelist.sol) contract for any of the test networks. Note that the deployed Optimistic Oracle V3 instance already has its `defaultCurrency` added to the whitelist, so it can also be used by the Data Asserter contract. Alternatively, you can approve a new token address with `addToWhitelist` method in the Address Whitelist contract if working in a sandboxed UMA environment.
2. `_optimisticOracleV3` is used to locate the address of UMA Optimistic Oracle V3. Address of `OptimisticOracleV3` contact can be fetched from the relevant [networks](https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks) file, if you are on a live network, or you can provide your own contract instance if deploying UMA Oracle contracts in your own sandboxed testing environment.

```solidity
    constructor(address _defaultCurrency, address _optimisticOracleV3) {
        defaultCurrency = IERC20(_defaultCurrency);
        oo = OptimisticOracleV3Interface(_optimisticOracleV3);
        defaultIdentifier = oo.defaultIdentifier();
    }
```

#### Asserting data

`assertDataFor` method allows any data provider to assert some value of `data` for the associated `dataId`. The caller sets the `asserter` as the address that will receive bonds at resolution of the data assertion (this could be their address as well).

```solidity
    function assertDataFor(
        bytes32 dataId,
        bytes32 data,
        address asserter
    ) public returns (bytes32 assertionId) { ... }
```

Internally, this function pulls the associated assertion bond (global for all assertions within the DataAssertion contract) from the caller, makes an assertion call to the Optimistic Oracle V3 and stores the assertion within the contracts internal `assertionsData` mapping. Lastly, an event is emitted.

For the context of this example we will be considering the `dataId` simply as the URL that would be called to fetch the weather data from `wttr.in` and the `data` value as the result from this call. Both are bytes32 encoded.

The call to the Optimistic Oracle V3 looks as follows:

```solidity
        assertionId = oo.assertTruth(
            abi.encodePacked(
                "Data asserted: 0x", // in the example data is type bytes32 so we add the hex prefix 0x.
                ClaimData.toUtf8Bytes(data),
                " for dataId: 0x",
                ClaimData.toUtf8Bytes(dataId),
                " and asserter: 0x",
                ClaimData.toUtf8BytesAddress(asserter),
                " at timestamp: ",
                ClaimData.toUtf8BytesUint(block.timestamp),
                " in the DataAsserter contract at 0x",
                ClaimData.toUtf8BytesAddress(address(this)),
                " is valid."
            ),
            asserter,
            address(this), // Callback recipient
            address(0), // No sovereign security.
            assertionLiveness,
            defaultCurrency,
            bond,
            defaultIdentifier,
            bytes32(0) // No domain.
        );
```

Lets break this down step by step:

* First, the value for the `claim` filed is constructed to be a human readable string as the concatenation of a number of different data points. This is meant to be parsable by the validation network and enable both Humans and software verifiers alike to assess the correctness of the claim. Note that within the Optimistic Oracle V3 the `claim` field (this prop in this function call) is *not* stored on-chain; it is simply emitted and is used by off-chain actors to verify the correctness of the claim.
* Next, the `asserter` field is passed in from this function call. This is who will receive the bonds back at resolution of the assertion, assuming the asserter was correct.
* Next, we have the callback recipient set to `address(this)`. The OOV3 has the concept of callbacks wherein at the settlement of an assertion the asserter can choose to optionally call out from the OO into another contract. In this context, we want the OOV3 to call *back* into the Data Asserter contract when the assertion is settled (this will become clearer in the following section).
* We have no Sovereign security enabled here (this is an advanced topic and does not need to be covered here).
* Default liveness, bond and identifier are set.
* No domain is set (this is an external concept that helps 3rd parties identify assertions).

#### Resolving and subsequently fetching data assertions

Once data has been asserted, the act of verifying it is passed over to the Optimistic Oracle V3 and the associated network of data verifiers. Due to the presence of a bond (reward for catching invalid assertions), we can be confident that in real world someone would verify this assertion (if the bond is high enough, and the liveness is long enough we have high guarantees of this).

If the assertion passes the `assertionLiveness` without dispute then it can be settled. The data assertion contract showcases the OOV3's concept of `assertionResolvedCallback` which enables the OOV3 to settle downstream contracts that are dependent on the resolution of an assertion. Once an assertion is settleable (it has: a) passed liveness and b) not been disputed) anyone can call [`settleAssertion`](https://github.com/UMAprotocol/protocol/blob/922e5da60d1e71f64de50acdf9bf83a07be9449e/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol#L250) on the OOV3. This function acts to settle the assertion within the OO and call into the callback recipient, set during the `assertDataFor` function call.

Looking within the Data Asserter contract, we can see what happens when the `assertionResolvedCallback` is hit from the OOV3 on settlement:

```solidity
// OptimisticOracleV3 resolve callback.
    function assertionResolvedCallback(bytes32 assertionId, bool assertedTruthfully) public {
        require(msg.sender == address(oo));
        // If the assertion was true, then the data assertion is resolved.
        if (assertedTruthfully) {
            assertionsData[assertionId].resolved = true;
            DataAssertion memory dataAssertion = assertionsData[assertionId];
            emit DataAssertionResolved(dataAssertion.dataId, dataAssertion.data, dataAssertion.asserter, assertionId);
            // Else delete the data assertion if it was false to save gas.
        } else delete assertionsData[assertionId];
    }
```

This function effectively:

* enforce the inbound caller is the OOV3. Else, revert
* If the assertion was resolved as truthful (it would not be if it was disputed and the dispute proved to be correct) then:
  * set the data assertion status to true,
  * remove the data from the temporary `dataAssertion` structure
* if it was resolved as not truthful (deleted) then simply delete it from the temp structure `assertionsData`

Once settled, we can now call the `getData` function with the `assertionId.`This will return the data value and the accompanying resolution status (was it resolved truthfully, or not).

#### Disputing data assertions

If we want to dispute an asserted data point one must simply call the [`disputeAssertion(bytes32 assertionId, address disputer)`](https://github.com/UMAprotocol/protocol/blob/922e5da60d1e71f64de50acdf9bf83a07be9449e/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol#L220) method on the OOV3. In here, you provide the associated `assertionId` (the same field that was returned when calling `assertData`and the `disputer` address (the address that will receive the bonds back at the resolution of the dispute).

### Tests and deployment

All the unit tests covering the functionality described above are available [here](https://github.com/UMAprotocol/dev-quickstart-oov3/blob/master/test/DataAsserter.t.sol). To execute all of them, run:

```bash
forge test --match-path *DataAsserter*
```

#### Deployment

Before deploying and interacting with the contracts export the required environment variables. Note that there are a number of ways to interact with these contracts. The simplest setup is to fork Goerli testnet into a local development environment, using Anvil, and run the steps against the fork. To create an Anvil fork run the following:

```bash
anvil --fork-url https://goerli.infura.io/v3/xxx
```

Replacing `xxx` with your infura key. This will create a local fork of the Goerli testnet, running on `127.0.0.1:8545`. It will also expose a test mnemonic that you can use which has pre-loaded Eth within the fork.

(**Note:** to set environment variables, as those listed below, use the `export` syntax in your shell. for example `export ETH_RPC_URL=XXX`

* `ETHERSCAN_API_KEY`: your secret API key used for contract verification on Etherscan if deploying on a public network (not required if you want to do this against a local testnet).
* `ETH_RPC_URL`: your RPC node used to interact with the selected network. For this tutorial it will be easiest to do this against the Goerli fork by setting `ETH_RPC_URL=http://127.0.0.1:8545`
* `MNEMONIC`: your passphrase used to derive private keys of deployer (index 0) and any other addresses interacting with the contracts. The simplest setup with this is to re-use the Anvil default unlocked accounts, if you are using the local testnet environment, you can have:

  ```bash
  export MNEMONIC="test test test test test test test test test test test junk"
  ```
* `FINDER_ADDRESS`: address of the [Finder](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/implementation/Finder.sol) contract used to locate other UMA ecosystem contracts (in order to resolve disputes you would need to use the one from a sandboxed environment). For Goerli, you can use:

  ```bash
  export FINDER_ADDRESS=0xE60dBa66B85E10E7Fd18a67a6859E241A243950e
  ```

Use `cast` command from Foundry to locate the address of Optimistic Oracle V3 and its default bonding token:

```bash
export OOV3_ADDRESS=$(cast call $FINDER_ADDRESS "getImplementationAddress(bytes32)(address)" \
	$(cast --format-bytes32-string "OptimisticOracleV3"))
export DEFAULT_CURRENCY_ADDRESS=$(cast call $OOV3_ADDRESS "defaultCurrency()(address)")
```

You should be able to see both of these values set in your env with `echo $OOV3_ADDRESS` and `echo $DEFAULT_CURRENCY_ADDRESS`

To deploy the Data Asserter contract, run `forge create` command.

(**Note** if you hit an error like `Error accessing local wallet. Did you set a private key, mnemonic or keystore?` then you might be hitting a foundry related issue that should be resolved by updating your foundry installation with `foundryup`.

```bash
export DATA_ASSERTER=$(forge create --json src/DataAsserter.sol:DataAsserter \
	--mnemonic "$MNEMONIC" \
	--constructor-args $DEFAULT_CURRENCY_ADDRESS $OOV3_ADDRESS \
	| jq -r .deployedTo)
```

Finally, we can verify the deployed (if deployed to a public network) contract with `forge verify-contract`:

```bash
forge verify-contract \
	--chain-id $(cast chain-id) \
	--constructor-args $(cast abi-encode "constructor(address,address)" \
	$DEFAULT_CURRENCY_ADDRESS $OOV3_ADDRESS) $DATA_ASSERTER DataAsserter
```

### Interacting with deployed contract

The following section provides instructions on how to interact with the deployed contract from the foundry `cast` tool, though one can also use it for guidance for interacting through another interface (e.g. Remix or Etherscan).

#### Initial setup

Export derivation index for the asserting user that will be needed when signing transactions:

```bash
export ASSERTER_ID=1
```

Make sure the user address above have sufficient funding for the gas to execute the transactions.

Next, set up the bytes32 encoding for the `dataId` and `data`, as discussed in the [Asserting Data](#asserting-data) section. The `dataId` field will be the simplest command that can be re-produced by independent verifiers. We use `bc` (arbitrary-precision arithmetic language and calculator) that should be available on most operating systems:

```bash
export DATA_ID=$(cast --format-bytes32-string "echo '69+420' | bc")
echo "DATA_ID=$(echo $DATA_ID)"
```

The `data` field will be the asserted output of the above `bc` command:

```bash
export DATA=$(cast --format-bytes32-string $(echo '69+420' | bc))
echo "DATA=$(echo $DATA)"
```

Note that at any point you can look at your env by running `printenv` within your console to see the things we've set up to this point.

#### Asserting the data point

Next, we will call the Data Asserter contract and assert the data point and data ID that we set within our environment. Note that on the Goerli testnet the default bond for the default currency is set to 0. This is done to somewhat simplify the interactions: you don't need to mint yourself any USDC and you don't need to approve the Data Asserter to pull it from your account. Clearly, this is different to how this would be structured in reality but for demonstration purposes it keeps thing easier.

```bash
export ASSERTION_TX=$(cast send --json \
	--mnemonic "$MNEMONIC" --mnemonic-index $ASSERTER_ID \
	$DATA_ASSERTER \
	"assertDataFor(bytes32,bytes32,address)" $DATA_ID $DATA $DATA_ASSERTER \
	| jq -r .transactionHash)
```

What we are doing here is calling the `assertDataFor(bytes32,bytes32,address)`with the props `DATA_ID`, `DATA` and `DATA_ASSERTER`, that we set earlier. We are taking the output of the transaction and storing the `transactionHash` within the `ASSERTION_TX` variable.

We can fetch the associated `assertionId` from the `DataAssertionResolved` event that should be the last event in the assertion transaction. The emitted event has the structure:

```solidity
    event DataAssertionResolved(
        bytes32 indexed dataId,
        bytes32 data,
        address indexed asserter,
        bytes32 indexed assertionId
    );
```

We can fetch indexed props from cast by using the `receipt` function in conjunction with `jq`. To fetch the 3rd indexed prop (`assertionId`) from the last event, we would run:

```bash
export ASSERTION_ID=$(cast receipt --json $ASSERTION_TX | jq -r .logs[-1].topics[3])
```

Next, we can view the data asserted object using the `--abi-decode` call. This will return the props in the `assertionData` mapping, relating to this structure:

```solidity
    struct DataAssertion {
        bytes32 dataId; // The dataId that was asserted.
        bytes32 data; // This could be an arbitrary data type.
        address asserter; // The address that made the assertion.
        bool resolved; // Whether the assertion has been resolved.
    }
```

```bash
cast --abi-decode "assertionsData(bytes32)(bytes32,bytes32,address,bool)" \
	$(cast call $DATA_ASSERTER "assertionsData(bytes32)" $ASSERTION_ID)
```

The last prop in the output (`resolved`) should be `false` at this moment.

#### Settling the assertion

Now, let's move forward 2 hours to go pass the challenge window of the assertion:

```bash
cast rpc evm_increaseTime 7200
cast rpc evm_mine
```

Now, we can submit the settlement transaction to the OOV3 as we've passed liveness:

```bash
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "settleAssertion(bytes32)" $ASSERTION_ID
```

If we call the `assertionData` mapping again, we'll see that the assertion has settled. The last prop in the output (`resolved`) should be `true`:

```bash
cast --abi-decode "assertionsData(bytes32)(bytes32,bytes32,address,bool)" \
	$(cast call $DATA_ASSERTER "assertionsData(bytes32)" $ASSERTION_ID)
```

We can now call the `getData` method directly and store output in `IS_RESOLVED` and `RESOLVED_DATA` variables:

```bash
read -r IS_RESOLVED RESOLVED_DATA \
	< <(cast --abi-decode "getData(bytes32)(bool,bytes32)" \
	$(cast call $DATA_ASSERTER "getData(bytes32)" $ASSERTION_ID) \
	| awk '{s=(NR==1?s:s " ")$0}END{print s}') 
```

`IS_RESOLVED` should now be `true`:

```bash
echo $IS_RESOLVED
```

The decoded value of `RESOLVED_DATA` should be `489` as originally asserted:

```bash
cast --parse-bytes32-string $RESOLVED_DATA
```

### Conclusion

This tutorial has shown you how to use the Data Asserter sample tutorial, along with the Optimistic Oracle V3. This is meant to act as a starting point for further contracts to be built on. Next steps should be looking at the other OOV3 tutorials or trying to build your own integration! If you have questions please message us on Discord or create a Github issue and we'll happily help you with your integration.


# Escalation Managers

Using the full policy escalation manager as an example for modular functionality you could implement with custom escalation managers

### Table of Contents <a href="#table-of-contents" id="table-of-contents"></a>

1. [Introduction](#introduction)
2. [Use Cases](#use-cases)
3. [Events](#events)
4. [Functions](#functions)
5. [Getting Started](https://chat.openai.com/chat/9433ce54-7882-46cc-975a-4877b66aaf8c#getting-started)

### Introduction <a href="#introduction" id="introduction"></a>

The [Full Policy Escalation Manager](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/escalation-manager/FullPolicyEscalationManager.sol)[ ](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/escalation-manager/FullPolicyEscalationManager.sol#L6)is an example implementation of [Escalation Manager](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/escalation-manager/BaseEscalationManager.sol) that allows for a high level of control over the arbitration process in the [Optimistic Oracle V3](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/optimistic-oracle-v3/implementation/OptimisticOracleV3.sol). It can be used to set various policy parameters such as who is able to assert truths, who is able to file disputes, and whether disputes should be arbitrated via the escalation manager or the [UMA Data Verification Mechanism (DVM)](/protocol-overview/how-does-umas-oracle-work#umas-data-verification-mechanism). By using the Escalation Managers, such as the Full Policy Escalation Manager, users can better tailor the arbitration process to suit their specific needs and security requirements.

An Escalation Manager is a smart contract that is used in the Optimistic Oracle V3 to handle the escalation policy for assertions and can also be used to arbitrate disputes. When one party makes an assertion, it might be challenged by another, known as the disputer. In the event of a dispute, the Escalation Manager is used to decide how the dispute should be handled and how the Optimistic Oracle V3 should respond to it.

The Full Policy Escalation Manager is an Escalation Manager implementation that allows the contract owner to set all policy parameters and store arbitration resolutions for the Escalation Manager.

Some of the key functionality that the Full Policy Escalation Manager allows are:

* Enabling a whitelist of asserting callers, asserters and disputers. This allows the owner to limit who can assert truths, and who can dispute assertions.
* Determining whether disputes should be arbitrated by the Escalation Manager or by the Data Verification Mechanism (DVM).
* Determining whether the resolution of a potential dispute arbitrated by the Oracle should be disregarded (i.e a disputed assertion defaults to false outcome).
* Storing and managing the arbitration resolutions for a given identifier, time and ancillary data.

This smart contract is ideal for use cases where a high degree of control over the behaviour of the Escalation Manager is needed, and where the owner wants to have a say in how disputes are handled and acts as a good starting point for projects that want to build their own escalation managers that implement custom logic.

### Use Cases

The FullPolicyEscalationManager can be used in a variety of ways depending on the specific needs of the project. Below are a few examples of possible use cases and how to configure the escalation manager for each:

* **Blocking assertions by untrusted parties**: In this case, the project may want to prevent assertions from being made by certain parties that are not trusted. To accomplish this, the `blockByAssertingCaller` and/or `blockByAsserter` parameters can be set to `true` and the corresponding whitelists can be populated with trusted parties using the `setAssertingCallerWhitelist` and `setAsserterWhitelist` functions.
* **Validation of disputers**: In some cases, the project may want to prevent disputes from being filed by untrusted parties. To accomplish this, the `validateDisputers` parameter can be set to `true` and the `whitelistedDisputeCallers` whitelist can be populated with trusted parties using the `setDisputeCallerWhitelist` function.
* **Allowing arbitration via the escalation manager**: By default, disputes are arbitrated via the UMA DVM. However, in some cases, the project may want to use the escalation manager to arbitrate disputes instead. To accomplish this, the `arbitrateViaEscalationManager` parameter can be set to `true`.
* **Discarding the oracle resolution**: In some cases, the project may want to disregard the oracle's resolution when arbitrating disputes. To accomplish this, the `discardOracle` parameter can be set to `true`.
* **Custom arbitration resolution**: If the project wants to define a custom resolution for a given identifier, time, and ancillary data, it can be set using the `setArbitrationResolution` function.
* **Mixing different options**: The FullPolicyEscalationManager can be customized to mix different options, such as allowing arbitration via the escalation manager, but also blocking assertions by untrusted parties, or validating disputers but also allowing custom arbitration resolution.

It's important to note that the values of `blockByAssertingCaller`, `blockByAsserter`, `validateDisputers`, `arbitrateViaEscalationManager`, `discardOracle` are set to false by default.

### Events <a href="#events" id="events"></a>

* `EscalationManagerConfigured`: Emitted when the escalation manager is configured using the `configureEscalationManager` function. The event contains the following parameters:
  * `blockByAssertingCaller`: A boolean indicating whether assertions are allowed only by whitelisted asserting callers.
  * `blockByAsserter`: A boolean indicating whether assertions are allowed only by whitelisted asserters.
  * `validateDisputers`: A boolean indicating whether the escalation managershould validate disputers via the whitelistedDisputeCallers mapping.
  * `arbitrateViaEscalationManager`: A boolean indicating whether it is determined that the escalation manager should arbitrate disputes.
  * `discardOracle`: A boolean indicating whether the escalation manager should disregard the Oracle’s resolution of disputes.
* `ArbitrationResolutionSet`: Emitted when an arbitration resolution is set using the `setArbitrationResolution` function. The event contains the following parameters:
  * `identifier`: The identifier of the assertion.
  * `time`: The time of the assertion.
  * `ancillaryData`: The ancillary data of the assertion.
  * `resolution`: The resolution of the arbitration.
* `DisputeCallerWhitelistSet`: Emitted when a dispute caller is added or removed from the whitelistedDisputeCallers mapping using the `addDisputeCallerToWhitelist` and `removeDisputeCallerFromWhitelist` functions. The event contains the following parameters:
  * `disputeCaller`: The address of the dispute caller that was added or removed from the whitelist.
  * `whitelisted`: A boolean indicating whether the dispute caller was added (true) or removed (false) from the whitelist.
* `AssertingCallerWhitelistSet`: Emitted when an asserting caller is added or removed from the whitelistedAssertingCallers mapping using the `addAssertingCallerToWhitelist` and `removeAssertingCallerFromWhitelist` functions. The event contains the following parameters:
  * `assertingCaller`: The address of the asserting caller that was added or removed from the whitelist.
  * `whitelisted`: A boolean indicating whether the asserting caller was added (true) or removed (false) from the whitelist.
* `AsserterWhitelistSet`: Emitted when an asserter is added or removed from the whitelistedAsserters mapping using the `addAsserterToWhitelist` and `removeAsserterFromWhitelist` functions. The event contains the following parameters:
  * `asserter`: The address of the asserter to add.
  * `whitelisted`: true represents adding and false represents removing the asserter from the whitelist.

### Functions

### **`getAssertionPolicy`**

This function returns the Assertion Policy defined by the contract's parameters and functions.

**Parameters**

* `assertionId`: the ID of the assertion to get the policy for.

**Returns**

* `AssertionPolicy memory`: an object that contains the following properties:
  * `blockAssertion`: a boolean indicating whether the assertion is blocked or not.
  * `arbitrateViaEscalationManager`: a boolean indicating whether the arbitration should be done via the escalation manager if it is configured.
  * `discardOracle`: a boolean indicating whether to ignore the Oracle (DVM or EM) resolution if it is configured.
  * `validateDisputers`: a boolean indicating whether to validate disputers if it is configured.

**Notes**

If no configuration is done after deployment, this function returns an all false default policy.

### **`getPrice`**

The `getPrice` function allows developers to retrieve the resolved price for a given identifier and timestamp. It replicates the interface of the corresponding DVM function to allow the user to use his own dispute arbitration system when arbitrating via the escalation manager in a DVM-compatible manner. It is useful when you want to get the resolved price for a specific request.

**Parameters**

* `identifier`: A unique identifier that identifies the price requested.
* `time`: A unix timestamp of the price request.
* `ancillaryData`: Arbitrary data appended to a price request to give the voters more info from the caller.

**Returns**

* `int256`: representing the resolved price for the given identifier and timestamp.

**Notes**

If the price is not available, the method reverts. Also, the function requires that the arbitration resolution has been set before calling it.

### **`isDisputeAllowed`**

This function returns whether a given disputerCaller is authorized to dispute a given assertionId or not.

**Parameters**

* `assertionId`: The ID of the assertion to check the disputerCaller for.
* `disputeCaller`: The address of the disputeCaller to check.

**Returns**

* `bool`: true if the disputerCaller is authorized to dispute the assertion, false otherwise.

**Notes**

* In order for this function to be used by the Optimistic Oracle V3, validateDisputers must be set to true.
* The whitelistedDisputeCallers is a mapping that contains the addresses of the authorized disputers, which are set through the setDisputeCallerInWhitelist function.

### **`configureEscalationManager`**

This function is used to configure the escalation manager and define the assertion policy for each configuration's rules.

#### Parameters

* `_blockByAssertingCaller`: A boolean value that indicates if assertions are allowed only by whitelisted asserting callers.
* `_blockByAsserter`: A boolean value that indicates if assertions are allowed only by whitelisted asserters.
* `_validateDisputers`: A boolean value that indicates if the escalation manager should validate disputers via whitelistedDisputeCallers.
* `_arbitrateViaEscalationManager`: A boolean value that indicates if the escalation manager should arbitrate instead of the DVM.
* `_discardOracle`: A boolean value that indicates if the escalation manager should disregard the Oracle's (DVM or EM) resolution.

**Notes**

* This function can only be called by the contract's owner.
* It is important to note that this function only activates the rules that will be executed, each rule must additionally be defined using the other functions.
* The \_blockByAsserter must be true if \_blockByAssertingCaller is true.
* When this function is called, an event `EscalationManagerConfigured` is emitted with the parameters of the function call.

### **`setArbitrationResolution`**

This function is used to set the arbitration resolution for a given identifier, time, and ancillary data. This function should be used by the contract owner whenever a dispute arises and it should be arbitrated by the Escalation Manager. The owner of the contract is responsible for determining how to resolve the dispute.

#### Parameters

* **identifier**: A unique identifier that represents the price requested. This is used to identify the price request in question.
* **time**: A unix timestamp of the price request.
* **ancillaryData**: Arbitrary data that is appended to a price request to give the voters more information from the caller.
* **arbitrationResolution**: A boolean value that indicates if the assertion should be resolved as true (true) or false (false).

#### Notes

* This function is only callable by the owner of the contract.
* This function requires that the `arbitrationResolutions[requestId].valueSet` is equal to false, otherwise the function will revert with the error message "Arbitration already resolved".
* A `ArbitrationResolutionSet` event is emitted with the parameters `identifier`, `time`, `ancillaryData`, and `arbitrationResolution` when this function is called.
* The `getRequestId` function is used to generate a requestId to uniquely identify the price request. The requestId is generated from the identifier, time, and ancillaryData parameters.

### **`setDisputeCallerInWhitelist`**

The `setDisputeCallerInWhitelist` function allows the owner of the `FullPolicyEscalationManager` contract to add or remove a given address (`disputeCaller`) to the whitelist of addresses that are able to file disputes.

#### Parameters

* `disputeCaller`: The address of the dispute caller to add or remove from the whitelist.
* `value`: A boolean value that represents whether the `disputeCaller` is being added (`true`) or removed (`false`) from the whitelist.

#### Notes

This function is only used if the `validateDisputers` parameter is set to `true`. When a dispute caller is added or removed from the whitelist, the `DisputeCallerWhitelistSet` event is emitted with the `disputeCaller` and `value` as the arguments.

### **`setWhitelistedAssertingCallers`**

This function allows the owner of the contract to add or remove an address from the whitelist of asserting callers that are authorized to make assertions.

#### Parameters

* `assertingCaller`: The address of the asserting caller to add or remove from the whitelist.
* `value`: A boolean value that represents whether the asserting caller is being added (`true`) or removed (`false`) from the whitelist.

#### Notes

* This function can only be called by the owner of the contract.
* An event `AssertingCallerWhitelistSet` is emitted upon the successful execution of this function, which includes the `assertingCaller` and the `value` as the arguments.
* The whitelist of asserting callers is used when the `blockByAssertingCaller` configuration is set to `true`. In this case, only addresses in the whitelist are allowed to make assertions.

### **`setWhitelistedAsserters`**

This function allows an owner to add or remove an address from the whitelist of authorized asserters.

**Parameters**

* `asserter`: The address of the asserter to add or remove from the whitelist.
* `value`: A boolean value that determines whether the asserter is added or removed from the whitelist. `true` represents adding the asserter to the whitelist and `false` represents removing the asserter from the whitelist.

**Notes**

* This function can only be called by the contract owner.
* This function must be used in conjunction with `setWhitelistedAssertingCallers` in order to have an effect. This means that if `setWhitelistedAssertingCallers` is set to true, the assertion will only be made by an address that is in both whitelists `whitelistedAssertingCallers` and `whitelistedAsserters`.
* When an asserter is added or removed from the whitelist, an event `AsserterWhitelistSet` is emitted with the `asserter` and `value` as the arguments.

### Getting started <a href="#getting-started" id="getting-started"></a>

To get started with the Full Policy Escalation Manager, the first step is to deploy the contract on the EVM network. This can be done using hardhat and a library such as `ethers.js`. Here is an example of how to deploy the contract:

```typescript
const hre = require("hardhat");

import { FullPolicyEscalationManagerEthers__factory } from "@uma/contracts-node";

const { getContractFactory } = hre.ethers;

const { getAddress } = require("@uma/contracts-node");

async function main() {
  console.log("Running FullPolicyEscalationManager Deployments🔥");

  const networkId = Number(await hre.getChainId());

  const optimisticOracleV3 = getAddress("OptimisticOracleV3", networkId);

  const fullPolicyEscalationManagerFactory: FullPolicyEscalationManagerEthers__factory = await getContractFactory(
    "FullPolicyEscalationManager"
  );

  const fullPolicyEscalationManager = await fullPolicyEscalationManagerFactory.deploy(optimisticOracleV3);

  console.log("Deployed FullPolicyEscalationManager: ", fullPolicyEscalationManager.address);
}

main()
  .then(() => process.exit(0))
  .catch((error) => {
    console.error(error);
    process.exit(1);
  });
```

Once the contract is deployed, it can be configured to handle disputes in different ways, depending on the use case. The `configureEscalationManager` function can be used to set the rules for how disputes should be handled. Here are some examples of how to configure the contract for different use cases:

Example 1:

```javascript
//Configure the contract to block assertions by asserting callers that are not whitelisted
await fullPolicyEscalationManager.configureEscalationManager(true, false, false, false, false);

//Whitelist an asserting caller and asserter
await fullPolicyEscalationManager.setWhitelistedAssertingCallers("0x123456789abcdef0123456789abcdef0123456789", true);
```

Example 2:

```javascript
//Configure the contract to validate disputers and arbitrate via the escalation manager
await fullPolicyEscalationManager.configureEscalationManager(false, false, true, true, false);

//Whitelist a dispute caller
await fullPolicyEscalationManager.setDisputeCallerInWhitelist("0x123456789abcdef0123456789abcdef0123456789", true);
```

Example 3:

```javascript
//Configure the contract to disregard the oracle's resolution
await fullPolicyEscalationManager.configureEscalationManager(false, false, false, false, true);
```

The Full Policy Escalation Manager can be configured for a variety of use cases depending on the requirements. The examples above show how to deploy the contract, configure it for different use cases and whitelist asserting callers, asserters and disputers. Keep in mind that the contract can only be configured by the owner and that the parameters of configureEscalationManager function must be set according to the desired use case.


# Sandboxed Oracle Environment

Only required for testing the dispute flow

Whenever the assertion is disputed on a production network this triggers DVM request to be resolved by UMA voters. This might be impractical to fully simulate this in a testing environment, hence, developers can use [mocked oracle contract](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/data-verification-mechanism/test/MockOracleAncillary.sol) to resolve requests as they had been returned by DVM.

Interacting with Optimistic Oracle requires having whitelisted price identifier and bonding currency. In production networks these are approved by UMA token holders as part of governance voting. When testing, it might be easier for developers to use their own set of UMA ecosystem contracts where they have full control over such whitelisting process.\
\
Follow the written tutorial below or check out the video tutorial on [Youtube](https://www.youtube.com/watch?v=1sW8vcqTRCE).

### Deploying sandboxed Oracle environment

A set of sandboxed UMA ecosystem contracts can be deployed using forge script from [Optimistic Oracle V3 developers quick-start repository](https://github.com/UMAprotocol/dev-quickstart-oov3).

After cloning the repository make sure you have the latest Foundry version by running `foundryup` command (see more installation details on [Foundry book](https://book.getfoundry.sh/getting-started/installation.html)). From the root of the repository install dependencies with:

```bash
forge install
```

All script parameters are controlled through environment variables that should be exported before running the script:

* `ETH_RPC_URL`: URL of the RPC node to use for broadcasting deployment transactions on the desired test network.
* `MNEMONIC`: Mnemonic of the account to use for deployment (derived account with 0 index will be used by default).
* `ETHERSCAN_API_KEY`: API key for Etherscan, used for verifying deployed contracts.
* `DEFAULT_IDENTIFIER`: Default identifier used by Optimistic Oracle V3 when resolving disputes. If not provided, this defaults to `ASSERT_TRUTH` identifier. The script will also whitelist this identifier.
* `DEFAULT_LIVENESS`: Default liveness in seconds used by Optimistic Oracle V3 when settling assertions. If not provided, this defaults to `7200` seconds.
* `DEFAULT_CURRENCY`: Default currency used by Optimistic Oracle V3 when bonding assertions and disputes. The script will also whitelist this currency. If not provided, the script would also deploy a [mintable ERC20 token](https://github.com/UMAprotocol/protocol/blob/master/packages/core/contracts/common/implementation/TestnetERC20.sol) and use it as the default currency based on following parameters:
  * `DEFAULT_CURRENCY_NAME`: Name of the new token. If not provided, this defaults to `Default Bond Token`.
  * `DEFAULT_CURRENCY_SYMBOL`: Symbol of the new token. If not provided, this defaults to `DBT`.
  * `DEFAULT_CURRENCY_DECIMALS`: Number of decimals of the new token. If not provided, this defaults to `18`.
* `MINIMUM_BOND`: Minimum bond amount in Wei of default currency required by Optimistic Oracle V3 when accepting new assertions. If not provided, this defaults to `100e18` Wei.

Once all required environment variables are setup, deploy and verify the set of sandboxed UMA ecosystem contracts with:

```bash
forge script \
--broadcast \
--fork-url $ETH_RPC_URL \
--mnemonics "$MNEMONIC" \
--sender $(cast wallet address --mnemonic "$MNEMONIC") \
--verify \
script/OracleSandbox.s.sol
```

At the top of the script output the addresses of the deployed contracts should be logged:

* `Finder`: Used to locate other UMA ecosystem contract addresses. Deployment script will have added new `Store`, `AddressWhitelist`, `IdentifierWhitelist`, `MockOracleAncillary` and `OptimisticOracleV3` implementations.
* `Store`: Stores final fees of bonding currencies. The script will have set the final fee of default currency to half of provided minimum bond amount since Optimistic Oracle V3 calculates minimum bond twice the value of this final fee.
* `AddressWhitelist`: Stores approved currency addresses that can be used for bonding assertions and disputes. The script will have already whitelisted the default currency.
* `IdentifierWhitelist`: Stores approved identifiers that can be used to resolve disputes at DVM. The script will have already whitelisted the default identifier.
* `MockOracleAncillary`: Optimistic Oracle will use this contract to resolve disputes equivalent to DVM voting on mainnet. Once requested, anyone can push desired test price through this mock oracle contract.
* `TestnetERC20`: Unless existing default currency was provided the script will have deployed this new token that is whitelisted as a bonding currency. Anyone can mint new tokens by calling its `allocateTo(address ownerAddress, uint256 value)` method.
* `OptimisticOracleV3`: Optimistic Oracle that resolves assertions made by integrating contracts.

### Resolving dispute requests

Whenever the assertion is disputed, Optimistic Oracle V3 calls `requestPrice` method on the Oracle that triggers DVM vote on mainnet. In the sandboxed environment this would resolve to `MockOracleAncillary` contract instead.

The disputed request is uniquely identified by its identifier, timestamp and ancillary data that should be referenced by the testing developer pushing resolved price to the mocked oracle. The `MockOracleAncillary` contract will emit `PriceRequestAdded` event with following parameters:

* `address indexed requester`: Address of the calling contract (Optimistic Oracle V3 in the sandboxed setup).
* `bytes32 indexed identifier`: Identifier referencing the rules on how the request should be resolved. By default Optimistic Oracle V3 uses `ASSERT_TRUTH` identifier unless the integrating contract provided other value.
* `uint256 time`: Optimistic Oracle V3 will set this to timestamp when the original assertion was made.
* `bytes ancillaryData`: Optimistic Oracle V3 will put the `assertionId` and address of the asserter referencing the disputed assertion.
* `bytes32 indexed requestId`: This is hashed value of identifier, time and ancillary data provided by the `MockOracleAncillary` contract just as convenient method to resolve request by one parameter instead of three separate parameters.

Note that production voting contract on mainnet emits this differently and above `PriceRequestAdded` is provided only as a convenience for developers when interacting with the `MockOracleAncillary` contract in their testing environment.

`MockOracleAncillary` contract provides two alternative methods for testing developers to resolve request:

* `pushPrice(bytes32 identifier, uint256 time, bytes memory ancillaryData, int256 price)` where request is referenced by its individual parameters.
* `pushPriceByRequestId(bytes32 requestId, int256 price)` where request is referenced by the hashed value of its identifying parameters.

While testing developer can pass any `int256` price value, Optimistic Oracle V3 can only interpret value of `1e18` to represent truthful assertion. Any other value would settle assertion as being false. Also note that `MockOracleAncillary` contract will only accept the first pushed price to resolve a given request.

### Example dispute flow

As an illustration to the section above this example walks through resolving disputed assertion with `cast` tool from Foundry. This also requires `jq` being installed for parsing returned values.

Just for the sake of simplicity set `MINIMUM_BOND` environment variable to `0` when running `forge script` command as discussed in the [sandbox deployment section](#deploying-sandboxed-oracle-environment). This would allow to skip minting and approving assertion/dispute bonds. Take a note of deployed contracts log and export Optimistic Oracle V3 address to `OOV3_ADDRESS` and `MockOracleAncillary` address to `MOCK_ORACLE_ADDRESS` environment variables.

Also to keep this simple perform all steps from the same address:

{% code overflow="wrap" %}

```bash
export USER_ADDRESS=$(cast wallet address --mnemonic "$MNEMONIC")
```

{% endcode %}

Start with generating test assertion and grab the resulting `assertionId` emitted by Optimistic Oracle V3 (corresponds to topic index `1` in the `AssertionMade` event):

{% code overflow="wrap" %}

```bash
export ASSERTION_TX=$(cast send --mnemonic "$MNEMONIC" --json $OOV3_ADDRESS "assertTruthWithDefaults(bytes,address)" $(cast --from-utf8 "test claim") $USER_ADDRESS | jq -r '.transactionHash')
export ASSERTION_ID=$(cast receipt --json $ASSERTION_TX | jq -r --arg OOV3_LOWERCASE $(echo $OOV3_ADDRESS | tr [:upper:] [:lower:]) '.logs[] | select(.address==$OOV3_LOWERCASE).topics[1]')
```

{% endcode %}

Dispute the above assertion and grab the resulting `requestId` emitted by `MockOracleAncillary` (corresponds to topic index `3` in the `PriceRequestAdded` event):

{% code overflow="wrap" %}

```bash
export DISPUTE_TX=$(cast send --mnemonic "$MNEMONIC" --json $OOV3_ADDRESS "disputeAssertion(bytes32,address)" $ASSERTION_ID $USER_ADDRESS | jq -r '.transactionHash')
export REQUEST_ID=$(cast receipt --json $DISPUTE_TX | jq -r --arg MOCK_LOWERCASE $(echo $MOCK_ORACLE_ADDRESS | tr [:upper:] [:lower:]) '.logs[] | select(.address==$MOCK_LOWERCASE).topics[3]')
```

{% endcode %}

Now resolve the disputed assertion as truthful by pushing `1e18` as price to `MockOracleAncillary` contract:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $MOCK_ORACLE_ADDRESS "pushPriceByRequestId(bytes32,int256)" $REQUEST_ID $(cast --to-wei 1)
```

{% endcode %}

This allows us to settle the assertion at Optimistic Oracle V3:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "settleAssertion(bytes32)" $ASSERTION_ID
```

{% endcode %}

Finally, you can verify that Optimistic Oracle V3 has correctly settled the assertion (this should return `true`):

{% code overflow="wrap" %}

```bash
cast call $OOV3_ADDRESS "getAssertionResult(bytes32)(bool)" $ASSERTION_ID
```

{% endcode %}

### Further sandbox configuration

The deployment script documented in the [section above](#deploying-sandboxed-oracle-environment) covers most common use cases and should be sufficient to test example contracts from the [Optimistic Oracle V3 integration quickstart repository](https://github.com/UMAprotocol/dev-quickstart-oov3). Though, when developing your own integrating contracts it might be required to further configure the sandboxed oracle environment.

#### Whitelisting of bonding currencies

In case the integrating contract requires handling assertion bonds in multiple currencies all of them should be approved at the `AddressWhitelist` contract. On production this should go through UMA governance while in the sandboxed environment the deployer owns this contract and can call `addToWhitelist` method directly. Assuming the address of `AddressWhitelist` contract is exported to `ADDRESS_WHITELIST` and required bonding currency is exported to `BONDING_CURRENCY` environment variables, this can be done by:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $ADDRESS_WHITELIST "addToWhitelist(address)" $BONDING_CURRENCY
```

{% endcode %}

In order to test the scenario on how the integrating contracts would behave if UMA governance removed support for a particular bonding currency, you can test this by calling `removeFromWhitelist` method on the `AddressWhitelist` contract:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $ADDRESS_WHITELIST "removeFromWhitelist(address)" $BONDING_CURRENCY
```

{% endcode %}

Note that Optimistic Oracle V3 is caching whitelisted bonding currencies. Hence, before testing impact on the integrating contracts somebody should sync the contract cache (anyone has authority to do this):

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "") $BONDING_CURRENCY
```

{% endcode %}

#### Changing the final fee

In case the UMA governance was to change the configured final fee for a particular bonding currency this would also change the minimum assertion bond required by Optimistic Oracle V3. In order to test such impact on integrating contracts in the testing environment, the deployer would need to call `setFinalFee` on the `Store` contract (exported as `STORE_ADDRESS`). E.g. to set final fee to `1000e18`, you would call:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $STORE_ADDRESS "setFinalFee(address,(uint256))" $BONDING_CURRENCY "($(cast --to-wei 1000))"
```

{% endcode %}

Again, before testing impact on integrating contracts someone would need to sync new final fee at the Optimistic Oracle V3:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "") $BONDING_CURRENCY
```

{% endcode %}

#### Whitelisting of identifiers

If the integrating contract plans to make assertions with multiple identifiers, the developer would need to add them to `IdentifierWhitelist` contract (exported as `IDENTIFIER_ADDRESS`) in the sandboxed environment, e.g. adding `CUSTOM_IDENTIFIER`:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $IDENTIFIER_ADDRESS "addSupportedIdentifier(bytes32)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER")
```

{% endcode %}

In order to test the scenario on how the integrating contracts would behave if UMA governance removed support for a particular identifier, you can test this by calling `removeSupportedIdentifier` method on the `IdentifierWhitelist` contract:

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $IDENTIFIER_ADDRESS "removeSupportedIdentifier(bytes32)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER")
```

{% endcode %}

Note that Optimistic Oracle V3 is caching whitelisted identifiers. Hence, before testing impact on the integrating contracts somebody should sync the contract cache (anyone has authority to do this):

{% code overflow="wrap" %}

```bash
cast send --mnemonic "$MNEMONIC" $OOV3_ADDRESS "syncUmaParams(bytes32,address)" $(cast --format-bytes32-string "CUSTOM_IDENTIFIER") 0x0000000000000000000000000000000000000000
```

{% endcode %}


# Resolving Testnet Disputes

If the dispute is on a live network, it will be resolved by the [DVM](https://docs.umaproject.org/protocol-overview/how-does-umas-oracle-work) on Ethereum, and the results returned within 48-72 hours (depending on when the dispute was raised during the DVM voting cycle).

If you are testing the dispute flow on Görli, you will need to manually resolve the dispute through the [Mock Oracle](https://goerli.etherscan.io/address/0x20570e9e27920ac5e2601e0bef7244deff7f0b28#code). This contract stands in for the DVM on Görli and allows you to manually return your own values for testing purposes.

1. Go to the [Mock Oracle contract](https://goerli.etherscan.io/address/0x20570e9e27920ac5e2601e0bef7244deff7f0b28#code) on Görli Etherscan.
2. Click 'Write Contract.'
3. Click 'Connect to Web3' to connect your wallet.
4. Click 'OK' on the pop-up that warns you that writing to contracts on Etherscan is a beta test feature.
5. Connect through your wallet interface, and switch networks to Görli if necessary.
6. Click `pushPrice` to see the parameters you will need to enter.
7. Enter the original request's `identifier`, `time`, and `ancillaryData`, and enter the value you want to return for `price`.
8. Click 'Write' and submit the transaction.
9. Depending on how the contract you are testing was written, you may need to call `settleAndGetPrice` from your contract to the Optimistic Oracle contract to get the value returned from the mock oracle, or you may be able to call `settle` on the Optimistic Oracle and have your contract automatically receive and handle the return value with a `priceSettled` callback function.


# Setting Custom Bond and Liveness Parameters

Overview of Setting Customer Bond and Liveness Parameter in UMA's Optimistic Oracle

Every request to UMA's Optimistic Oracle includes bond and liveness settings that specify the size of the bond that proposers (and disputers) are required to post, the token used for bonding, and the liveness window, which is the challenge period during which a proposal can be challenged.

The minimum bond is the same as [final fee](/resources/approved-collateral-types) for Optimistic Oracle V2 (OOV2) or can be queried by calling getMinimumBond(token) for Optimistic Oracle V3 (OOV3). The default liveness window is two hours. For many cases, you may want to customize these values. The primary reason to increase the bond size or challenge window is to increase your security for complex requests or requests that could move large amounts of money.

### How to think about proposer and disputer bonds

In most cases, you will want to set a bond higher than the minimum. The reason for this is that the bond provides a financial incentive for disputers to dispute invalid proposals. Disputers receive half of the proposer bond in the case of a successful dispute in the OOV3. For the OOV2, it's half of the excess above the final fee.

The part of the bond that doesn't go to the disputer in a successful dispute goes to the UMA `Store` contract. The reason the disputer does not get the full amount is to prevent a malicious proposer from making a bad proposal and then front-running an honest disputer to dispute themselves if they get caught.

#### Example bond payouts (OOV3)

As an example, imagine an application that requires a 10,000 USDC bond to assert a claim via OOV3.

To make an assertion, a proposer must post a 10,000 USDC bond, which they will get back after the liveness window if the assertion is not disputed. To make a dispute, a disputer must also post a 10,000 USDC bond.

If the disputer wins, they receive 15,000 USDC (10,000 USDC disputer bond + 5,000 USDC from the excess proposer bond) and the `Store` receives 5,000 USDC.

If the proposer wins, they receive 15,000 USDC (10,000 USDC proposer bond + 5,000 USDC from the disputer bond) and the `Store` receives 5,000 USDC (half of the disputer bond).

#### Higher or lower bonds?

If data received from the oracle could potentially move large amounts of value, you may want to set a higher bond to make it more costly for an attacker to attempt to steal funds through a false proposal. (Remember that the proposer will lose their entire bond if they are disputed and found to be incorrect.)

The tradeoff to setting a higher bond is that it may be more difficult to run a project that requires frequent proposals, since good proposers will have funds locked up for at least the length of the challenge window. Another tradeoff is that a very high bond may make it difficult for disputers to post bonds to challenge bad proposals. Remember also that disputers will lose their bond if they are incorrect, so the disputer has a capital cost and a risk of loss.

Before launching your contract, you should think about who you expect to be proposers and disputers, whether they have adequate access to capital, how much value the oracle is securing, how many requests you expect to make in a given day, and whether you should add a proposer reward (to incentivize independent proposers to take on the capital cost and risk of proposing answers to your requests).

There are no exact answers to how high or low your bond should be, although it is almost always a good idea to set a bond that is larger than the final fee so that disputers have an incentive to dispute bad proposals.

### How to think about liveness

The liveness period is the challenge window where disputers can dispute a proposal, and is another knob you can turn to increase security (while accepting UX trade-offs).

A typical challenge period is two hours, which is usually plenty of time for disputers to recognize and dispute bad proposals. In some cases, you may want to increase the length of the challenge period. A classic example is an insurance contract, since insurance contracts usually have these qualities:

1. Large amounts of value are at stake.
2. Payouts are rare.
3. Proposals and disputes require a greater amount of thought and consideration.
4. Payouts are not very time sensitive, and a long delay (even a day or more) to allow proposers and disputers to think through the situation will not seriously inconvenience users.

You may want to consider a two-hour challenge window in these situations:

1. Requests happen frequently and proposers and disputers will need to reuse their capital regularly for bonding.
2. The dollar value secured by a request is lower.
3. The request is easy to reason about (and proposals and disputes could potentially be automated).
4. Users are sensitive to the settlement time.

At this time, it is generally not recommended to set a challenge window shorter than two hours.

If your users are very sensitive to settlement time, you may want to consider a smart contract architecture that allows a particular type of user to fully insure a piece of data that is acted on instantly, and then waiting to be reimbursed after the challenge window. Think of this user as a "designated waiter," who is probably compensated in some way.

A good example is the [relayer](https://docs.across.to/how-across-works/overview/roles-within-across#relayer) in the Across protocol. Users of the Across bridge receive their funds almost instantly, because a relayer sends them funds on the receiving chain and then submits for reimbursement from the protocol, and has to wait for several hours for their relay to be bundled with other relays, proposed, and then settled after the challenge window.

In this case, the UX for most users is excellent (nearly instant transfers) but the protocol is still secured by a long challenge window that is absorbed by a less time sensitive user type (the relayer) who is paid fees for their trouble.


# Network Information

#### Monitored

Risk Labs operates monitoring bots for the following networks and integrations for additional resiliency.

<table><thead><tr><th width="163">UMA Contract Addresses</th><th width="113">Chain ID</th><th width="199">Fully Permissionless Settlement</th><th width="139">DVM Support</th><th>Oracle UI</th></tr></thead><tbody><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/1.json">Ethereum Mainnet</a><br><em>(Across only)</em></td><td>1</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/137.json">Polygon Mainnet</a><br><em>(Polymarket &#x26; predict.fun only)</em></td><td>137</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr></tbody></table>

#### Unmonitored

The following networks are supported, but Risk Labs does not provide additional monitoring.

{% hint style="info" %}
Warning to Proposers: Making requests to the OOV2 contract is permissionless and open requests can not be deleted. Requests made by integrations not included in the Monitored section above may be misconfigured so that proposing them will lead to the proposer's bond being permanently stuck. Therefore proposing requests from any integration not listed in the Monitored section above is at the proposers own risk. Proposers who lose their bonds to Unmonitored requests will not be reimbursed.
{% endhint %}

{% hint style="info" %}
If you plan to integrate UMA's optimistic oracle, we recommend you run your own monitoring bots to provide additional resiliency.
{% endhint %}

{% hint style="info" %}
UMA's data verification mechanism (DVM), which is used to resolve disputes, is on Ethereum mainnet. Where possible, UMA uses a chain's native messaging bridge to relay dispute and governance information between that chain and Ethereum.

On chains where no such native messaging bridge exists, UMA uses a multi-sig controlled by UMA core engineers at Risk Labs to relay disputes to the DVM, to return data from the DVM to requesters on that chain, and to execute governance actions (for instance, adding new identifiers).
{% endhint %}

<table><thead><tr><th width="170">UMA Contract Addresses</th><th width="113">Chain ID</th><th width="175">Fully Permissionless Settlement</th><th width="139">DVM Support</th><th>Oracle UI</th></tr></thead><tbody><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/1.json">Ethereum Mainnet</a><br></td><td>1</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/11155111.json">Ethereum Sepolia</a><br><em>(testnet)</em></td><td>11155111</td><td>Yes</td><td>No</td><td><a href="https://testnet.oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/137.json">Polygon Mainnet</a></td><td>137</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/80002.json">Polygon Amoy</a><br><em>(testnet)</em></td><td>80002</td><td>Yes</td><td>No</td><td><a href="https://testnet.oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/10.json">Optimism Mainnet</a></td><td>10</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/42161.json">Arbitrum Mainnet</a></td><td>42161</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/8453.json">Base Mainnet</a></td><td>8453</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/84532.json">Base Sepolia</a><br><em>(testnet)</em></td><td>84532</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/81457.json">Blast Mainnet</a></td><td>81457</td><td>Yes</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/168587773.json">Blast Sepolia</a><br><em>(testnet)</em></td><td>168587773</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/1514.json">Story Mainnet</a></td><td>1514</td><td>Multi-sig relay</td><td>Yes</td><td><a href="https://oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/43114.json">Avalanche C-Chain</a></td><td>43114</td><td>Multi-sig relay</td><td>Yes</td><td>No</td></tr></tbody></table>

#### Deprecated

<table><thead><tr><th width="124">UMA Contract Addresses</th><th width="113">Chain ID</th><th width="199">Fully Permissionless Settlement</th><th width="139">DVM Support</th><th>Oracle UI</th></tr></thead><tbody><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/1516.json">Story Odyssey</a><br><em>(testnet)(deprecated)</em></td><td>1516</td><td>Yes</td><td>No</td><td><a href="https://testnet.oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/288.json">Boba Mainnet </a>(<em>Deprecated)</em></td><td>288</td><td>n/a</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/1116.json">Core Mainnet</a><br>(<em>Deprecated)</em></td><td>1116</td><td>n/a</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/42.json">Ethereum Kovan </a>(<em>(testnet)(deprecated)</em></td><td>42</td><td>n/a</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/4.json">Ethereum Rinkeby </a><em>(testnet)(deprecated)</em></td><td>4</td><td>n/a</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/5.json">Ethereum Göerli </a>(<em>(testnet)(deprecated)</em></td><td>5</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/80001.json">Polygon Mumbai</a><br><em>(testnet)(deprecated)</em></td><td>80001</td><td>Yes</td><td>No</td><td><a href="https://testnet.oracle.uma.xyz">Yes</a></td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/421611.json">Arbitrum Rinkeby</a><br><em>(testnet)(deprecated)</em></td><td>421611</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td><a href="https://github.com/UMAprotocol/protocol/blob/master/packages/core/networks/100.json">Gnosis Chain Mainnet</a><br>(<em>Deprecated)</em></td><td>100</td><td>n/a</td><td>No</td><td>No</td></tr></tbody></table>


# New Network Requests

The following are the evaluation criteria UMA considers when adding Optimistic Oracle support for a new network. UMA aims to support as many networks as possible to deliver the best experience for our integrating protocols and users. However, integrating a new network requires a significant investment. Therefore, the evaluation criteria were established to ensure that the benefits to UMA's integrating protocols and users outweigh the development, security, and operational costs of adding a new network.

Please note that these requirements are subject to change as the EVM (Ethereum Virtual Machine) and Layer 2 ecosystem continue to evolve. Meeting these requirements is the initial step in our evaluation process and does not guarantee support.

### Non-Negotiable Criteria:

**The below are a pre-requisite to any consideration:**

* EVM-Compatible Network a canonical token and message bridge to mainnet
* ≥ 2 or more Independent RPC Node Providers (without block range limits for log queries)
* 30-Day Moving Average TVL on the network > $75M
* Block Explorer with Etherscan-Compatible API, contract verification and ABI fetching
* Availability of token mappings data source with Ethereum mainnet
* Dedicated support channel containing technical members of the network with priority support commitment to advise on contract development and debug data quality
* Subgraph support for testnet and mainnet
* Wallet connector libraries support for testnet and mainnet

### Get in Touch with UMA

* Drop a message in the [UMA Discord Server](https://discord.com/invite/jsb9XQJ)
* Send an email to <integrations@umaproject.org>


# Audit & Bug Bounty Programs

Security of the platform is our highest priority. All contract code and balances are publicly verifiable, and security researchers are eligible for a bug bounty for reporting undiscovered vulnerabilities.

### Audits <a href="#audits" id="audits"></a>

OpenZeppelin has performed the following audits on UMA contracts:

* [Common and oracle directory contracts: April 28, 2020](https://blog.openzeppelin.com/uma-audit-phase-1/)
* [Financial-templates directory contracts: May 12, 2020](https://blog.openzeppelin.com/uma-audit-phase-2/)
* [Updates to the Expiring Multiparty contracts and flash loan mitigations for the voting contracts: September 9, 2020](https://blog.openzeppelin.com/uma-audit-phase-3/)
* [Perpetual Multiparty template contracts: February 2, 2021](https://blog.openzeppelin.com/uma-audit-phase-4/)
* [Insured Bridge contracts: December 1, 2021](https://blog.openzeppelin.com/uma-audit-l2-bridges/)
* [Governance, cross-chain oracle, and optimistic rewarder contracts: January 7, 2022](https://blog.openzeppelin.com/uma-audit-phase-6/)
* [UMA Optimistic Governor Audit: July 21, 2022](https://blog.openzeppelin.com/uma-optimistic-governor-audit/)
* [DVM 2.0 voting and governance contracts: December 2, 2022](https://blog.openzeppelin.com/uma-dvm-2-0-audit/)
* [DVM 2.0 incremental updates: December 8, 2022](https://blog.openzeppelin.com/uma-dvm-2-0-incremental-audit)
* [Optimistic Asserter contract: January 30, 2023](https://blog.openzeppelin.com/uma-optimistic-asserter-audit)
* [Optimistic Governor migration to Optimistic Oracle v3: June 26, 2023](https://blog.openzeppelin.com/uma-optimistic-governor-incremental-audit)
* [Oracle bridging contracts upgrade: April 30, 2025](https://www.openzeppelin.com/news/uma-oracle-bridging-contracts-upgrade-audit)
* [Managed Optimistic Oracle contract: August 26, 2025](https://www.openzeppelin.com/news/managed-optimistic-oracle-audit)
* [Dispute Router module and Across diff audit: March 2, 2026](https://www.openzeppelin.com/news/uma-and-across-incremental-audit)

Additionally, OpenZeppelin audits incremental upgrades to UMA's contracts on a continuous basis. The continuous audit report can be found [here](https://blog.openzeppelin.com/uma-continuous-audit/).

### Bug Bounty Rewards <a href="#bug-bounty-rewards" id="bug-bounty-rewards"></a>

UMA encourages the community to audit our contracts and security; we also encourage the responsible disclosure of any issues. The bug bounty program is intended to recognize the value of working with the community of independent security researchers and sets out our definition of good faith in the context of finding and reporting vulnerabilities, as well as what you can expect from us in return.

UMA offers substantial rewards for discoveries that can prevent the loss of assets, the freezing of assets, or harm to users.

All rewards will be paid in $UMA, and the amount of compensation will vary depending on bug severity. Reward amounts typically correspond to severity in the following manner.

| Severity | Reward amount in USD |
| -------- | -------------------- |
| Low      | $250                 |
| Medium   | $3,000               |
| High     | $10,000              |
| Critical | up to $1,000,000     |

Severity is calculated according to the [OWASP](https://owasp.org/www-project-risk-assessment-framework/) risk rating model based on Impact and Likelihood.

![](/files/eTdqMXvp8nZj3x3ZBmj1)

### Scope <a href="#scope" id="scope"></a>

The scope of our bug bounty program includes any and all of UMA's production smart contracts. It does not include known issues with the intended behavior.

#### In scope <a href="#in-scope" id="in-scope"></a>

* All UMA, Oval, or Across smart contracts that are deployed to mainnet or are otherwise noted as being applicable.
* Bot or other offchain code to support deployed smart contracts.

#### Examples of what’s in scope: <a href="#examples-of-whats-in-scope" id="examples-of-whats-in-scope"></a>

* Being able to steal funds
* Being able to freeze funds or render them inaccessible by their owners

#### Out of scope: <a href="#out-of-scope" id="out-of-scope"></a>

* Issues that have already been submitted by another user or are already known to the UMA team
  * Note: this includes bugs known to the UMA team, but have not been disclosed due to active mitigation efforts.
* Vulnerabilities in contracts built on top of the protocol by third-party developers (such as smart contract wallets)
* Vulnerabilities that require ownership of an admin key
* Any files, modules or libraries other than the ones mentioned above
* More efficient gas solutions (although these suggestions are appreciated)
* Any points listed as an already known weaknesses
* Any points listed in an audit report

### Submissions <a href="#submissions" id="submissions"></a>

Please email your submissions to <bugs@umaproject.org>.

The submission must include clear and concise steps to reproduce the discovered vulnerability.

### Terms & Conditions <a href="#terms--conditions" id="terms--conditions"></a>

If you comply with the policies below when reporting a security issue to us, we will not initiate a lawsuit or law enforcement investigation against you in response to your report.

We ask that you:

* Report any vulnerability you’ve discovered promptly.
* Avoid violating the privacy of others, disrupting our systems, destroying data, or harming user experience.
* Use only <bugs@umaproject.org> to discuss vulnerabilities with us.
* Keep the details of any discovered vulnerabilities confidential until they are publicly announced by Risk Labs.
* Perform testing only on in-scope systems, and respect systems and activities which are out-of-scope.
* Not engage in blackmail, extortion, or any other unlawful conduct.
* Not be a current or former UMA Foundation employee, vendor, contractor, or the employee of an UMA vendor or contractor.

Public disclosure of the bug or the indication of an intention to exploit it on Mainnet will make the report ineligible for a bounty. If in doubt about other aspects of the bounty, most of the [Ethereum Foundation bug bounty program rules](https://bounty.ethereum.org/) will apply.

Any questions? Reach us via email (<bugs@umaproject.org>). For more information on the UMA platform, check out our [website](http://www.umaproject.org/) and [Github](https://github.com/UMAprotocol/).

All reward determinations, including eligibility and payment amount, are made at UMA’s sole discretion. UMA reserves the right to reject submissions and alter the terms and conditions of this program without notice.


# Approved Price Identifiers

This table includes identifiers that could be useful for new contracts and are likely to be encountered by proposers, disputers, and voters. It does not include identifiers that have been deprecated, identifiers that have fallen out of use, or specific token pairs.

If you need an identifier that is not included in this list, you can [submit a new UMIP](https://docs.umaproject.org/community/governance/the-umip-process) for approval. If a price request is made with an identifier that is not on this table, please refer to the canonical [UMIP directory](https://github.com/UMAprotocol/UMIPs/tree/master/UMIPs) on GitHub. The absence of an identifier from this table does *not* necessarily mean the identifier is not approved, it just means it is not recommended for new contracts.

If you want to use an approved identifier that is not on this list, let us know and we can add the identifier back to this table for easier voter/disputer reference.

<table><thead><tr><th width="290"></th><th width="333"></th><th></th></tr></thead><tbody><tr><td>ACROSS-V2</td><td>Verification of whether a bundle of Across bridge transactions submitted to mainnet is valid.</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-179.md">UMIP-179</a></td></tr><tr><td>ASSERT_TRUTH<br>(DEPRECATED as of Dec 15, 2025)</td><td>Former default price identifier for Optimistic Oracle V3</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-170.md">UMIP-170</a></td></tr><tr><td>ASSERT_TRUTH2<br>(as of Dec 8, 2025)</td><td>New intended default price identifier for Optimistic Oracle V3</td><td><a href="https://github.com/UMAprotocol/UMIPs/pull/629/files">UMIP-191</a></td></tr><tr><td>MULTIPLE_CHOICE_QUERY</td><td>Allows requests with a predefined set of valid responses.</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-181.md">UMIP-181</a></td></tr><tr><td>NUMERICAL</td><td>Returns a number value based on a question asked in the ancillary data.</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-165.md">UMIP-165</a></td></tr><tr><td>ROPU_ETHx</td><td>Reflects violations of the MEV-policy committed by validators of the ETHx staking protocol</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-177.md">UMIP-177</a></td></tr><tr><td>YES_OR_NO_QUERY</td><td>Returns an answer to a "yes or no" question.</td><td><a href="https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-107.md">UMIP-107</a></td></tr></tbody></table>


# Approved Collateral Types

| Collateral Currency | Final Fee | UMIP                                                                           | Chains                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
| ------------------- | --------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| ABT                 | 0.055     | [UMIP-178](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-178.md) | [Ethereum](https://etherscan.io/address/0xee1DC6BCF1Ee967a350e9aC6CaaAA236109002ea)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| USDB                | 250       | [UMIP-182](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-182.md) | [Blast](https://blastscan.io/address/0x4300000000000000000000000000000000000003)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| USDC                | 250       | [UMIP-18](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-18.md)   | [Arbitrum](https://arbiscan.io/address/0xaf88d065e77c8cC2239327C5EDb3A432268e5831), [Arbitrum (USDC.e)](https://arbiscan.io/address/0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8), [Base](https://basescan.org/address/0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913), [Core](https://scan.coredao.org/address/0xa4151B2B3e269645181dCcF2D426cE75fcbDeca9), [Ethereum](https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48), [Optimism](https://optimistic.etherscan.io/address/0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85), [Optimism (USDC.e)](https://optimistic.etherscan.io/address/0x7F5c764cBc14f9669B88837ca1490cCa17c31607), [Polygon](https://polygonscan.com/address/0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359), [Polygon (USDC.e)](https://polygonscan.com/address/0x2791bca1f2de4661ed88a30c99a7a9449aa84174) |
| WETH                | 0.055     | [UMIP-10](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-10.md)   | [Arbitrum](https://arbiscan.io/address/0x82aF49447D8a07e3bd95BD0d56f35241523fBab1), [Base](https://basescan.org/address/0x4200000000000000000000000000000000000006), [Blast](https://blastscan.io/address/0x4300000000000000000000000000000000000004), [Core](https://scan.coredao.org/address/0xeAB3aC417c4d6dF6b143346a46fEe1B847B50296), [Ethereum](https://etherscan.io/token/0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2), [Optimism](https://optimistic.etherscan.io/address/0x4200000000000000000000000000000000000006), [Polygon](https://polygonscan.com/address/0x7ceb23fd6bc0add59e62ac25578270cff1b9f619),                                                                                                                                                                                                                    |
| WIP                 | 50        |                                                                                | [Story](https://www.storyscan.io/address/0x1514000000000000000000000000000000000000)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |


# Subgraphs

This section explains the UMA subgraph and how to interact with it. The UMA subgraph indexes data from UMA contracts over time. It organizes data about tokenholders, contracts, DVM requests, voting, and more. The subgraph updates for each UMA contract interaction. The subgraph runs on [The Graph](https://thegraph.com/) protocol’s hosted service and can be openly queried.

UMA has a GraphQL API Endpoint hosted by [The Graph](https://thegraph.com/docs/about/introduction#what-the-graph-is) called a subgraph for indexing and organizing data from the smart contracts. The schema of GraphQL elements available is defined in [`the subgraphs repo` ](https://github.com/UMAprotocol/subgraphs/).

## Ethereum Mainnet

[Creating an API Key Video Tutorial](https://www.youtube.com/watch?v=UrfIpm-Vlgs)

* [Explorer Page](https://thegraph.com/explorer/subgraph?id=41LCrgtCNBQyDiVVyZEuPxbvkBH9BxxLU3nEZst77V8o\&view=Overview)
* Graphql Endpoint: <https://gateway.thegraph.com/api/\\[api-key]/subgraphs/id//41LCrgtCNBQyDiVVyZEuPxbvkBH9BxxLU3nEZst77V8o>
* [Code Repo](https://github.com/UMAprotocol/subgraphs)

## Helpful Links

[Querying from an Application](https://thegraph.com/docs/en/developer/querying-from-your-app/)

[Managing your API Key & Setting your indexer preferences](https://thegraph.com/docs/en/studio/managing-api-keys/)

## Resources

LSP Subgraphs

* [Mainnet Subgraph](https://thegraph.com/hosted-service/subgraph/umaprotocol/mainnet-lsp)
* [Polygon Subgraph](https://thegraph.com/hosted-service/subgraph/umaprotocol/polygon-lsp)
* [Kovan Subgraph](https://thegraph.com/hosted-service/subgraph/umaprotocol/kovan-lsp)

EMP Subgraphs (includes query for whitelisted collateral)

* [Mainnet Subgraph](https://thegraph.com/explorer/subgraph/umaprotocol/mainnet-contracts)
* [Kovan Subgraph](https://thegraph.com/explorer/subgraph/umaprotocol/kovan-contracts)

Voting Subgraphs

* [Mainnet Subgraph](https://thegraph.com/explorer/subgraph/umaprotocol/mainnet-voting)
* [Kovan Subgraph](https://thegraph.com/explorer/subgraph/umaprotocol/kovan-voting)

Here is the [source code](https://github.com/UMAprotocol/subgraphs) for deployed subgraphs.

## Making Queries

To learn more about querying a subgraph refer to [The Graph’s documentation](https://thegraph.com/docs/about/introduction).


# Mainnet Voting Entities

### Entities

* [`User`](#user)
* [`Collateral`](#collateral)
* [`PriceIdentifier`](#priceidentifier)
* [`PriceRequest`](#pricerequest)
* [`PriceRequestRound`](#pricerequestround)
* [`VoterGroup`](#votergroup)
* [`CommittedVote`](#committedvote)
* [`RevealedVote`](#revealedvote)
* [`RewardsClaimed`](#rewardsclaimed)
* [`FinancialContract`](#financialcontract)

### User

Description:

| Field           | Type   | Description                                                                                                                                       |
| --------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| id              | ID!    | Utility entity that links data from a single Ethereum address. Id of the entity is the Ethereum address itself                                    |
| address         | Bytes! |                                                                                                                                                   |
| countReveals    | BigInt | Number of price requests that this user has revealed a vote for, and therefore participated in as a voter                                         |
| countRetrievals | BigInt | Provides a lower bound on the number of votes a user has correctly voted for. Users may not have retrieved rewards for all of their correct votes |
| votesCommited   | Int!   |                                                                                                                                                   |

### Collateral

Description:

| Field         | Type       | Description                                                                                                                                     |
| ------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
| id            | ID!        | Represents approved collateral that is whitelisted in the AddressWhitelist and whose fees are set in the Store. Id of the entity is its address |
| decimals      | Int!       |                                                                                                                                                 |
| name          | String!    |                                                                                                                                                 |
| symbol        | String!    |                                                                                                                                                 |
| isOnWhitelist | Boolean!   | Is token currently whitelisted as collateral                                                                                                    |
| finalFee      | BigDecimal |                                                                                                                                                 |

### PriceIdentifier

Description:

| Field         | Type                             | Description                                                                                                                                                    |
| ------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| id            | ID!                              |                                                                                                                                                                |
| isSupported   | Boolean!                         | Depicts whether this PriceIdentifier is currently among the identifiers supported on the whitelist. It will only be false if it was removed from the whitelist |
| priceRequests | [`PriceRequest!`](#pricerequest) | List of all the PriceRequest entities related to this particular PriceIdentifier                                                                               |

### PriceRequest

Description:

| Field                 | Type                                       | Description                                                        |
| --------------------- | ------------------------------------------ | ------------------------------------------------------------------ |
| id                    | ID!                                        | ID is the PriceIdentifier ID + the timestamp                       |
| isResolved            | Boolean!                                   | Depicts whether the request has been resolved                      |
| price                 | BigInt                                     | Price resolved for this request                                    |
| latestRound           | PriceRequestRound                          | PriceRequestRound entity corresponding to the last round of voting |
| time                  | BigInt!                                    |                                                                    |
| identifier            | PriceIdentifier!                           | PriceIdentifier for the request                                    |
| ancillaryData         | String                                     |                                                                    |
| resolutionTransaction | Bytes                                      | Transaction where the resolution of the request took place         |
| resolutionTimestamp   | Bigint                                     | Timestamp when the resolution of the request took place            |
| resolutionBlock       | BigInt                                     | Block number when the resolution of the request took place         |
| rounds                | [`PriceRequestRound!`](#pricerequestround) | List of all the rounds involved in this PriceRequest               |
| committedVotes        | [`CommittedVote!`](#committedvote)         | List of all the votes committed on this request                    |
| revealedVotes         | [`RevealedVote!`](#revealedvote)           | List of all the votes revealed on this request                     |
| rewardsClaimed        | [`RewardsClaimed!`](#rewardsclaimed)       | List of all the rewards claimed events for this request            |

### PriceRequestRound

Description:

| Field                              | Type                                 | Description                                                                               |
| ---------------------------------- | ------------------------------------ | ----------------------------------------------------------------------------------------- |
| id                                 | ID!                                  | ID is the PriceIdentifier ID + the timestamp + the roundId + ancillaryData (if available) |
| request                            | PriceRequest!                        |                                                                                           |
| identifier                         | PriceIdentifier!                     |                                                                                           |
| ancillaryData                      | String                               |                                                                                           |
| time                               | BigInt!                              |                                                                                           |
| snapshotId                         | BigInt                               |                                                                                           |
| votersAmount                       | BigDecimal!                          | Total amount of users who voted on this round                                             |
| votersClaimedAmount                | BigDecimal!                          | Total amount of users who claimed rewards on this round                                   |
| totalVotesRevealed                 | BigDecimal!                          |                                                                                           |
| totaRewardsClaimed                 | BigDecimal!                          |                                                                                           |
| totalSupplyAtSnapshot              | BigDecimal                           |                                                                                           |
| tokenVoteParticipationRatio        | BigDecimal                           | Ratio of the total supply of tokens that were weighted on this vote                       |
| tokenVoteParticipationPercentage   | BigDecimal                           | Ratio of correct voters over total voters on this price request                           |
| votersEligibleForRewardRatio       | BigDecimal                           | Ratio of correct voters over total voters on this price request                           |
| votersEligibleForRewardsPercentage | BigDecimal                           | Percentage of correct voters over total voters on this price request                      |
| votersClaimRatio                   | BigDecimal                           | Ratio of correct voters who claimed their rewards                                         |
| votersClaimedPercentage            | BigDecimal                           | Percentage of correct voters who claimed their rewards                                    |
| tokensClaimedRatio                 | BigDecimal                           | Ratio of rewards claimed over total supply of voting token                                |
| tokensClaimedPercentage            | BigDecimal                           | Percentage of rewards claimed over total supply of voting token                           |
| getPercentageRaw                   | BigDecimal                           | getPercentage expressed exactly as in the contract. 1 = 100%                              |
| getPercentage                      | BigDecimal                           | getPercentage expressed as a percentage value                                             |
| inflationRateRaw                   | BigDecimal                           | inflationRate expressed exactly as in the contract. 1 = 100%                              |
| inflationRate                      | BigDecimal                           | inflationRate expressed as a percentage value                                             |
| winnerGroup                        | VoterGroup                           |                                                                                           |
| committedVotes                     | [`CommittedVote!`](#committedvote)   |                                                                                           |
| revealedVotes                      | [`RevealedVote!`](#revealedvote)     |                                                                                           |
| groups                             | [`VoterGroup!`](#votergroup)         |                                                                                           |
| rewardsClaimed                     | [`RewardsClaimed!`](#rewardsclaimed) |                                                                                           |

### VoterGroup

Description:

| Field           | Type                             | Description                                                                                                    |
| --------------- | -------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| id              | ID!                              | Just a helper entity to group voters who voted the same price result. ID is composed of round ID + voted price |
| price           | BigInt!                          |                                                                                                                |
| round           | PriceRequestRound!               |                                                                                                                |
| votes           | [`RevealedVote!`](#revealedvote) |                                                                                                                |
| totalVoteAmount | BigDecimal!                      |                                                                                                                |
| totalVoteAmount | BigInt                           |                                                                                                                |
| votersAmount    | BigDecimal!                      |                                                                                                                |
| won             | Boolean!                         |                                                                                                                |

### CommittedVote

Description:

| Field         | Type               | Description                                                                               |
| ------------- | ------------------ | ----------------------------------------------------------------------------------------- |
| id            | ID!                | Committed votes won't show the price until a reveal happens and a RevealedVote is created |
| identifier    | PriceIdentifier!   |                                                                                           |
| ancillaryData | String             |                                                                                           |
| request       | PriceRequest!      |                                                                                           |
| time          | BigInt!            |                                                                                           |
| round         | PriceRequestRound! |                                                                                           |
| voter         | User!              |                                                                                           |

### RevealedVote

Description:

| Field         | Type               | Description |
| ------------- | ------------------ | ----------- |
| id            | ID!                |             |
| identifier    | PriceIdentifier!   |             |
| ancillaryData | String             |             |
| request       | PriceRequest!      |             |
| time          | BigInt!            |             |
| round         | PriceRequestRound! |             |
| price         | BigInt!            |             |
| voter         | User!              |             |
| numTokens     | BigInt!            |             |
| group         | VoterGroup!        |             |

### RewardsClaimed

Description:

| Field         | Type               | Description                                                                                                                                         |
| ------------- | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- |
| id            | ID!                |                                                                                                                                                     |
| identifier    | PriceIdentifier!   |                                                                                                                                                     |
| ancillaryData | String             |                                                                                                                                                     |
| request       | PriceRequest!      |                                                                                                                                                     |
| time          | BigInt!            |                                                                                                                                                     |
| round         | PriceRequestRound! |                                                                                                                                                     |
| claimer       | User!              |                                                                                                                                                     |
| numTokens     | BigInt!            | NumTokens will be 0 if the claim is not 'valid'. This can happen if the function was called for a voter who didn't get the correct vote for example |
| group         | VoterGroup!        |                                                                                                                                                     |

### FinancialContract

Description:

| Field                 | Type    | Description                                                                                                  |
| --------------------- | ------- | ------------------------------------------------------------------------------------------------------------ |
| id                    | ID!     | This entity represents a contract that can make price requests to the DVM. ID is the address of the contract |
| creator               | Bytes!  |                                                                                                              |
| registrationTimestamp | BigInt! |                                                                                                              |


# Queries

Below are some sample queries you can use to gather information from UMA contracts.

You can build your own queries using a [GraphQL Explorer](https://graphiql-online.com/graphiql) and enter your endpoint to limit the data to exactly what you need.

## Financial Contracts

```graphql
{
  financialContracts {
    id
    creator {
      id
      isRemoved
      manager
    }
    deployer {
      id
    }
    address
    collateralToken {
      id
      name
    }
    collateralRequirement
    expirationTimestamp
    totalSyntheticTokensBurned
    totalSyntheticTokensCreated
    totalTokensOutstanding
    cumulativeFeeMultiplier
    globalCollateralizationRatio
    rawTotalPositionCollateral
    totalCollateralDeposited
    totalCollateralWithdrawn
    totalPositionCollateral
    rawLiquidationCollateral
    positions {
      id
      rawCollateral
      collateral
      tokensOutstanding
      isEnded
    }
    liquidations {
      id
      status
      amountWithdrawn
    }
  }
}
```

## Liquidations

```graphql
{
  liquidations {
    id
    status
    sponsor {
      id
    }
    liquidator {
      id
    }
    disputer {
      id
    }
    liquidationId
    tokensLiquidated
    lockedCollateral
    liquidatedCollateral
    disputeBondAmount
    disputeSucceeded
    amountWithdrawn
    events {
      __typename
    }
  }
}
```

## Price Requests

```graphql
{
  priceRequests {
    id
    isResolved
    price
    latestRound {
      roundId
      snapshotId
      totalVotesRevealed
      totalRewardsClaimed
      totalSupplyAtSnapshot
      votersAmount
      votersClaimedAmount
      tokenVoteParticipationRatio
      tokenVoteParticipationPercentage
      votersEligibleForRewardsRatio
      votersEligibleForRewardsPercentage
      votersClaimedRatio
      votersClaimedPercentage
      tokensClaimedRatio
      tokensClaimedPercentage
      winnerGroup {
        votersAmount
        totalVoteAmount
      }
      groups {
        price
        totalVoteAmount
        won
        votersAmount
      }
    }
    time
    identifier {
      id
      isSupported
    }
    committedVotes(first: 2) {
      time
    }
    revealedVotes(first: 2) {
      price
      time
    }
  }
}
```


# Voting with a 2-Key Contract

The UMA DVM two-key contract enables you to actively engage in voting using hot keys (private keys stored on machine and used often), but protect your funds with the stronger, and preferable, use of cold keys (private keys stored offline and used rarely). Another way this contract can be used is to delegate voting rights to an EOA, while still maintaining token ownership with a multisig or other ownership structure.

Each two-key contract is a unique smart contract that you deploy. It keeps track of:

* The number of UMA tokens you have deposited into the two-key contract
* The address of your hot wallet; the hot wallet is permissioned to sign transactions and vote in DVM governance
* The address of your cold wallet; only the cold wallet (and importantly, not the voting wallet or any other address) is permissioned to withdraw UMA tokens from the two-key contract

> **DANGER**
>
> If you do not know exactly why you are setting up a 2-Key contract, then you likely do not need one. Deploying the contract is expensive and unnecessary unless you need to vote with separate hot and cold keys.
>
> Voting with a hardware wallet is not a reason that necessitates needing to set up a 2-Key contract. You can likely vote by connecting your hardware wallet to MetaMask or another wallet provider.
>
> If you have any doubt about your need for a 2-Key contract, please ask in the UMA Discord before proceeding.

### Prerequisites

* You need ETH in your hot wallet for the initial deployment of the 2-key contract and for submitting all subsequent votes.
* You need ETH in your cold wallet to make the initial UMA token deposit, and also if you ever want to withdraw tokens from the 2-key contract.

### Instructions to deploy your own 2-key contract

#### Connect

Navigate to the UMA voter dApp (vote.umaproject.org) and connect your MetaMask hot wallet. This will be your voting wallet going forward.

#### Deploy

Click on the gear icon on the far right.

In the modal, click `Add Cold Wallet Address`

Input your cold wallet address and click `Save`.

#### Verify your deployment was correct

The following steps help you verify that your two-key contract was deployed correctly and check that the permissions are held by the correct address.

* Copy the 2-key smart contract address (i.e., DesignatedVoting Escrow Account) from the voter dApp and view it on Etherscan
* Under `Read Contract` input roleID `0` in function `1. getMember`, and click query. Check that the address output matches your cold wallet address.
* Input roleID `1` in function `1. getMember`, and click query. Check that the address output matches your hot wallet address.

#### Voting

To vote with the 2-key contract, you will need to send UMA tokens to it. After sending UMA tokens to the two-key contract address, you can immediately start voting with your hot keys at the voter dApp. This will exclude you from voting with any additional UMA tokens that are in your hot wallet through the voter dApp.

#### Withdraw tokens

The following steps let you withdraw UMA tokens from the two-key contract using your cold keys. You will have to withdraw and deploy a new two-key contract if you want to designate a new hot key for voting.

* Navigate to your DesignatedVotingContract on Etherscan
* Connect your cold wallet by clicking the “Connect to web3” button
* Use function `11. withdrawErc20`
* Input erc20Address: `0x04fa0d235c4abf4bcf4787af4cf447de572ef828`
* Input the number of tokens that you want to withdraw \* 10^18 (for example, if you want to withdraw 1.1 tokens, you should input `1100000000000000000`)
* Click “Write”
* Confirm the transaction with your cold keys and wait for the transaction to finish mining

**Changing the voter address**

To change the voter address for a DesignatedVotingContract, you just need to call `resetMember(newAddress)` with your cold keys (owner address) and pass in the new address that you wish to use as the voter.


# Unsupported Contracts

Risk Labs/UMA protocol no longer actively supports or improves:

* The [Expiring Multi-Party (EMP)](https://github.com/UMAprotocol/launch-emp)
* The[ Perpetual Multi-Party](https://github.com/UMAprotocol/protocol/tree/master/packages/core/contracts/financial-templates/perpetual-multiparty)

These contracts are open-source code, and anyone is free to use, improve, or fork the code. However note that for these contracts to be safe, it requires a robust system of well capitalized off-chain watchers (liquidator & disputer bots) to continually check that positions are appropriately collateralized, or that positions are not being liquidated unfairly. Anyone using this code in production must ensure that there are well-capitalized liquidators and disputers running at all times; failure to do so could result in a loss of all locked funds. Risk Labs does not and will not run liquidators for these contracts, nor does it give any security assurances about any EMP or Perp contracts.\
\
As always, UMA protocol and Risk Labs will continue to support proposed addition or request of price identifiers for general use including within EMP/Perp contracts.


# oSnap (Deprecated)

Support for oSnap will be deprecated on December 15th, 2025. After this date, oSnap will not be able to execute transactions from your DAO’s Safe treasury.

oSnap integrations should take the following steps to prepare:

* Ensure their Safe signers are prepared to sign and execute any necessary DAO transactions
* Disable oSnap on all DAO treasuries by following these steps: <https://docs.uma.xyz/developers/osnap/disabling-osnap>


# Disabling oSnap via Zodiac Safe App

Steps to remove oSnap from your DAO Snapshot space and Safe treasury

Requirements:

* The below steps propose a Safe transaction to disable oSnap. Transactions can only be proposed to a Safe by that Safe's Signers or Proposers. You can view these roles on your Safe's settings page.
* Executing the proposed Safe transaction requires your minimum number of Signers to sign the transaction and one Signer to execute the transaction.

Steps:

1. Navigate to your DAO's Safe treasury on the Safe(Wallet) app: <https://app.safe.global/>
2. Click "Apps" on the left side bar and open on the "Zodiac" App<br>

   <figure><img src="/files/newSIddbH9HCEiT3Aea5" alt=""><figcaption></figcaption></figure>
3. Under the Modules and Modifiers heading, click on the "UMA OSNAP MODULE" and then the "Remove" button.\
   Note: if no "UMA OSNAP MODULE" shows under the "Modules and Modifiers" heading, your treasury does not an oSnap module.<br>

   <figure><img src="/files/jxxRlzDGCytumcHWvSFq" alt=""><figcaption></figcaption></figure>
4. Click "Submit Transactions" in the Zodiac "Bundle Transactions" modal<br>

   <figure><img src="/files/zvVAVPH4sL991sipDFIF" alt=""><figcaption></figcaption></figure>
5. This will propose a "disableModule" transaction in the Safe(Wallet) dapp. This transaction can be proposed, reviewed, signed, and executed as per the typical Safe(Wallet) transaction flow.

<figure><img src="/files/Ta4NBt7PaJnohg5ASPOM" alt=""><figcaption></figcaption></figure>

6. After this transaction is executed, oSnap is disabled from your Safe treasury. You can verify that your Treasury has no remaining oSnap modules as per the note in step number 3 above.


# oSnap Proposal Verification

oSnap Proposal Verification with UMA's Optimistic Oracle

## Verifying Proposals

After transactions have been requested for execution, the challenge period begins. The challenge period length is set by the oSnap Safe module advanced settings. Invalid requests must be disputed within the challenge period. oSnap requests can be reviewed in the [Optimistic Oracle dApp](https://oracle.umaproject.org/)'s Verify tab.

<figure><img src="/files/PFcsOE61aQDO5CpXJh5U" alt=""><figcaption></figcaption></figure>

After selecting a request, a sidebar will show additional request details including the bond amount, when the challenge period ends, a dispute button (when a wallet is connected), a link to the corresponding Snapshot proposal, the oSnap rules that the request should be reviewed against (including minimum quorum and voting period for the Snapshot proposal), and links to relevant block explorer pages.

The explanation includes an IPFS hash that is a unique identifier for the Snapshot proposal and can be used to retrieve details of the proposal. For example, <https://ipfs.io/ipfs/bafkreie2hujdeenwz7dcbqcvwq37vjgu6g3alvkk5ysxbm4yqpeahm37xa>.

<figure><img src="/files/aUcErwIEGYyKlpJ4dzXv" alt=""><figcaption></figcaption></figure>

## oSnap Proposal Pass Criteria

An oSnap proposal's voting results must meet both criteria below to be considered passed:

1. Quorum: FOR + AGAINST + ABSTAIN ≥ Required Quorum
2. 'FOR' majority: FOR > 50% \* (FOR + AGAINST + ABSTAIN)

## Disputing Proposals

If the oSnap request does not meet the Rules, it should be disputed. Disputed requests can not be executed no matter how UMA resolves the dispute. This [section](/using-uma/disputing-oracle-data) outlines the steps to dispute an invalid oSnap request, or any other invalid assertions to UMA's Optimistic Oracle.


# Additional Resources

Additional tutorials and resources about UMA that are not featured on this docs site

**Internal Learning Sessions**

* [Validating an Across v2 Dispute](https://www.youtube.com/watch?v=OjwzNlCwUsY\&ab_channel=UMAProtocol)
* [Across v2 Architecture](https://www.youtube.com/watch?v=iuxf6Crv8MI\&ab_channel=UMAProtocol)
* [Optimistic Distributor](https://www.youtube.com/watch?v=-N8UNHDxzYg\&ab_channel=UMAProtocol)
* [L2 Bridge Architecture](https://www.youtube.com/watch?v=G6tIiJUXIMg\&ab_channel=UMAProtocol)
* [How to use Storybook](https://www.youtube.com/watch?v=sS_FT-2_u-8\&ab_channel=UMAProtocol)
* [LM 2.0](https://www.youtube.com/watch?v=I8wDrvms_BI\&ab_channel=UMAProtocol)
* [Protected Tokens](https://www.youtube.com/watch?v=3h5SJVXw9pA\&ab_channel=UMAProtocol)
* [KPI Options 2.0](https://www.youtube.com/watch?v=cnIfqOvWpZ4\&ab_channel=UMAProtocol)
* [Optimistic Governance](https://www.youtube.com/watch?v=ohIHd1Ll-1U\&ab_channel=UMAProtocol)
* [Introduction to using UMA's Optimistic Oracle](https://www.youtube.com/watch?v=GhMfzKvA9Qk\&ab_channel=UMAProtocol)
* [Introduction to Merkle Roots](https://www.youtube.com/watch?v=JXn4GqyS7Gg\&t=1s\&ab_channel=UMAProtocol)
* [UMA Call Options](https://www.youtube.com/watch?v=3wQUnTHl8jw\&ab_channel=UMAProtocol)
* [Introduction to the LSP](https://www.youtube.com/watch?v=KNKSNKdUVl4\&t=2407s\&ab_channel=UMAProtocol)
* [Range Tokens](https://www.youtube.com/watch?v=l3WsUsRjzBk\&ab_channel=UMAProtocol)


# Glossary

#### Optimistic Oracle

The "Optimistic Oracle" (OO) allows contracts to quickly request and receive price information. Unlike mechanically restrictive price feed oracles, an optimistic oracle can serve any arbitrary data on-chain.

#### DVM <a href="#dvm" id="dvm"></a>

The “Data Verification Mechanism” (DVM) is the name of the oracle service provided by UMA. The DVM does not provide an on-chain price feed. Instead, it is only used to resolve disputes of liquidations and to settle synthetic token contracts upon expiration.

### Parameters of a Long Short Pair (LSP) smart contract: <a href="#parameters-of-a-long-short-pair-lsp-smart-contract" id="parameters-of-a-long-short-pair-lsp-smart-contract"></a>

#### Financial Product Library (FPL) <a href="#financial-product-library-fpl" id="financial-product-library-fpl"></a>

The financial product libraries are used to transform the value returned by the price identifier into a final settlement value. Financial product libraries can be applied to create different types of financial contracts and payout functions.

Refer to [github](https://github.com/UMAprotocol/protocol/tree/master/packages/core/networks) for a list of deployed financial product libraries for each network. If your desired financial product library is not already deployed, refer [here](https://github.com/UMAprotocol/launch-emp#deploying-financial-product-libraries) for instructions on deploying and verifying your own financial product library contract.

#### collateralPerPair <a href="#collateralperpair" id="collateralperpair"></a>

The collateralPerPair parameter determines the amount of collateral required to mint each pair of long and short tokens.

* Example: If a contract uses WETH as collateral and the collateralPerPair parameter is set to 0.25 on deployment, each long and short token that is minted would require 0.25 WETH as collateral.

#### expiryPercentLong <a href="#expirypercentlong" id="expirypercentlong"></a>

ExpiryPercentLong is used to determine the redemption rate between long and short tokens. ExpiryPercentLong is a number between 0 and 1, where 0 assigns all collateral to the short tokens and 1 assigns all collateral to the long tokens.

### UMA Product Types: <a href="#uma-product-types" id="uma-product-types"></a>

#### Range Tokens <a href="#range-tokens" id="range-tokens"></a>

Range tokens enable a DAO to use its native token as collateral to borrow funds. At maturity, if the debt is not paid, the range token holder is instead compensated with an amount of the collateral (the native token) using the settlement price of the native token to determine the number of tokens. This is similar to a tokenized convertible debt structure.

#### Success Tokens <a href="#success-tokens" id="success-tokens"></a>

Success tokens offer an alternative way for DAOs to diversify their treasury and sell tokens to investors in an incentive-aligned way. Success tokens are two financial products wrapped into one token: a set amount of a project token which is combined with a covered call option on that token backed by a set amount of the same token.

#### KPI Options <a href="#kpi-options" id="kpi-options"></a>

Key Performance Indicator (KPI) Options are synthetic tokens that will pay out more rewards if a project’s KPI reaches predetermined targets before a given expiry date. Every KPI Option holder has an incentive to improve that KPI because their option will be worth more. This is intended to align individual token holder interests with the collective interests of the protocol.


# Links

#### Links <a href="#links" id="links"></a>

You can keep up to date with what is happening with UMA across our various social media platforms and community resources:

* [Discord](https://discord.umaproject.org/)
* [Twitter](https://twitter.com/UMAprotocol)
* [Discourse](https://discourse.umaproject.org/)
* [Medium](https://medium.com/uma-project)
* [YouTube](https://www.youtube.com/channel/UC-3qS7FXxCd7gBMLttmTirw/playlists)
* [Example Projects](https://docs.uma.xyz/protocol-overview/example-projects)
* [UMA Protocol Calendar](http://calendar.umaproject.org/)
* [UMA Press Kit](https://github.com/UMAprotocol/website/tree/master/documents/press-kit)

#### Employment at UMA

If you are interested in working at UMA, please see our vacancies below. If you know someone that might be a good fit, check out our “talent referral options” program that offers up to 1000 $UMA to anyone who refers suitably qualified candidates for any of our open positions.

* [Risk Labs Job Postings](https://jobs.lever.co/risklabs)
* ["Talent Referral Options" Program](https://medium.com/uma-project/talent-referral-options-program-170bc347542a)


