Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.22;

/**
* @title OptimisticOracleV3CallbackRecipientInterface
* @notice Interface for contracts implementing callbacks to be received from the Optimistic Oracle V3
*
* The ERC-165 identifier for this interface is: 0x25f9f25e
*/
interface OptimisticOracleV3CallbackRecipientInterface {
/**
* @notice Callback function that is called by Optimistic Oracle V3 when an assertion is resolved.
* @param assertionId The identifier of the assertion that was resolved.
* @param assertedTruthfully Whether the assertion was resolved as truthful or not.
*/
function assertionResolvedCallback(bytes32 assertionId, bool assertedTruthfully) external;

/**
* @notice Callback function that is called by Optimistic Oracle V3 when an assertion is disputed.
* @param assertionId The identifier of the assertion that was disputed.
*/
function assertionDisputedCallback(bytes32 assertionId) external;
}
180 changes: 180 additions & 0 deletions contracts/interfaces/clients/OptimisticOracleV3Interface.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.22;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title OptimisticOracleV3Interface
* @notice Optimistic Oracle V3 Interface that callers must use to assert truths about the world
*
* The ERC-165 identifier for this interface is: 0x6e6309de
*/
interface OptimisticOracleV3Interface {
// Struct grouping together the settings related to the escalation manager stored in the assertion.
struct EscalationManagerSettings {
bool arbitrateViaEscalationManager; // False if the DVM is used as an oracle (EscalationManager on True).
bool discardOracle; // False if Oracle result is used for resolving assertion after dispute.
bool validateDisputers; // True if the EM isDisputeAllowed should be checked on disputes.
address assertingCaller; // Stores msg.sender when assertion was made.
address escalationManager; // Address of the escalation manager (zero address if not configured).
}

// Struct for storing properties and lifecycle of an assertion.
struct Assertion {
EscalationManagerSettings escalationManagerSettings; // Settings related to the escalation manager.
address asserter; // Address of the asserter.
uint64 assertionTime; // Time of the assertion.
bool settled; // True if the request is settled.
IERC20 currency; // ERC20 token used to pay rewards and fees.
uint64 expirationTime; // Unix timestamp marking threshold when the assertion can no longer be disputed.
bool settlementResolution; // Resolution of the assertion (false till resolved).
bytes32 domainId; // Optional domain that can be used to relate the assertion to others in the escalationManager.
bytes32 identifier; // UMA DVM identifier to use for price requests in the event of a dispute.
uint256 bond; // Amount of currency that the asserter has bonded.
address callbackRecipient; // Address that receives the callback.
address disputer; // Address of the disputer.
}

// Struct for storing cached currency whitelist.
struct WhitelistedCurrency {
bool isWhitelisted; // True if the currency is whitelisted.
uint256 finalFee; // Final fee of the currency.
}

/**
* @notice Disputes an assertion. Depending on how the assertion was configured, this may either escalate to the UMA
* DVM or the configured escalation manager for arbitration.
* @dev The caller must approve this contract to spend at least bond amount of currency for the associated assertion.
* @param assertionId unique identifier for the assertion to dispute.
* @param disputer receives bonds back at settlement.
*/
function disputeAssertion(bytes32 assertionId, address disputer) external;

/**
* @notice Returns the default identifier used by the Optimistic Oracle V3.
* @return The default identifier.
*/
function defaultIdentifier() external view returns (bytes32);

/**
* @notice Fetches information about a specific assertion and returns it.
* @param assertionId unique identifier for the assertion to fetch information for.
* @return assertion information about the assertion.
*/
function getAssertion(bytes32 assertionId) external view returns (Assertion memory);

/**
* @notice Asserts a truth about the world, using the default currency and liveness. No callback recipient or
* escalation manager is enabled. The caller is expected to provide a bond of finalFee/burnedBondPercentage
* (with burnedBondPercentage set to 50%, the bond is 2x final fee) of the default currency.
* @dev The caller must approve this contract to spend at least the result of getMinimumBond(defaultCurrency).
* @param claim the truth claim being asserted. This is an assertion about the world, and is verified by disputers.
* @param asserter receives bonds back at settlement. This could be msg.sender or
* any other account that the caller wants to receive the bond at settlement time.
* @return assertionId unique identifier for this assertion.
*/
function assertTruthWithDefaults(bytes memory claim, address asserter) external returns (bytes32);

/**
* @notice Asserts a truth about the world, using a fully custom configuration.
* @dev The caller must approve this contract to spend at least bond amount of currency.
* @param claim the truth claim being asserted. This is an assertion about the world, and is verified by disputers.
* @param asserter receives bonds back at settlement. This could be msg.sender or
* any other account that the caller wants to receive the bond at settlement time.
* @param callbackRecipient if configured, this address will receive a function call assertionResolvedCallback and
* assertionDisputedCallback at resolution or dispute respectively. Enables dynamic responses to these events. The
* recipient _must_ implement these callbacks and not revert or the assertion resolution will be blocked.
* @param escalationManager if configured, this address will control escalation properties of the assertion. This
* means a) choosing to arbitrate via the UMA DVM, b) choosing to discard assertions on dispute, or choosing to
* validate disputes. Combining these, the asserter can define their own security properties for the assertion.
* escalationManager also _must_ implement the same callbacks as callbackRecipient.
* @param liveness time to wait before the assertion can be resolved. Assertion can be disputed in this time.
* @param currency bond currency pulled from the caller and held in escrow until the assertion is resolved.
* @param bond amount of currency to pull from the caller and hold in escrow until the assertion is resolved. This
* must be >= getMinimumBond(address(currency)).
* @param identifier UMA DVM identifier to use for price requests in the event of a dispute. Must be pre-approved.
* @param domainId optional domain that can be used to relate this assertion to others in the escalationManager and
* can be used by the configured escalationManager to define custom behavior for groups of assertions. This is
* typically used for "escalation games" by changing bonds or other assertion properties based on the other
* assertions that have come before. If not needed this value should be 0 to save gas.
* @return assertionId unique identifier for this assertion.
*/
function assertTruth(
bytes memory claim,
address asserter,
address callbackRecipient,
address escalationManager,
uint64 liveness,
IERC20 currency,
uint256 bond,
bytes32 identifier,
bytes32 domainId
) external returns (bytes32);

/**
* @notice Fetches information about a specific identifier & currency from the UMA contracts and stores a local copy
* of the information within this contract. This is used to save gas when making assertions as we can avoid an
* external call to the UMA contracts to fetch this.
* @param identifier identifier to fetch information for and store locally.
* @param currency currency to fetch information for and store locally.
*/
function syncUmaParams(bytes32 identifier, address currency) external;

/**
* @notice Resolves an assertion. If the assertion has not been disputed, the assertion is resolved as true and the
* asserter receives the bond. If the assertion has been disputed, the assertion is resolved depending on the oracle
* result. Based on the result, the asserter or disputer receives the bond. If the assertion was disputed then an
* amount of the bond is sent to the UMA Store as an oracle fee based on the burnedBondPercentage. The remainder of
* the bond is returned to the asserter or disputer.
* @param assertionId unique identifier for the assertion to resolve.
*/
function settleAssertion(bytes32 assertionId) external;

/**
* @notice Settles an assertion and returns the resolution.
* @param assertionId unique identifier for the assertion to resolve and return the resolution for.
* @return resolution of the assertion.
*/
function settleAndGetAssertionResult(bytes32 assertionId) external returns (bool);

/**
* @notice Fetches the resolution of a specific assertion and returns it. If the assertion has not been settled then
* this will revert. If the assertion was disputed and configured to discard the oracle resolution return false.
* @param assertionId unique identifier for the assertion to fetch the resolution for.
* @return resolution of the assertion.
*/
function getAssertionResult(bytes32 assertionId) external view returns (bool);

/**
* @notice Returns the minimum bond amount required to make an assertion. This is calculated as the final fee of the
* currency divided by the burnedBondPercentage. If burn percentage is 50% then the min bond is 2x the final fee.
* @param currency currency to calculate the minimum bond for.
* @return minimum bond amount.
*/
function getMinimumBond(address currency) external view returns (uint256);

event AssertionMade(
bytes32 indexed assertionId,
bytes32 domainId,
bytes claim,
address indexed asserter,
address callbackRecipient,
address escalationManager,
address caller,
uint64 expirationTime,
IERC20 currency,
uint256 bond,
bytes32 indexed identifier
);

event AssertionDisputed(bytes32 indexed assertionId, address indexed caller, address indexed disputer);

event AssertionSettled(
bytes32 indexed assertionId,
address indexed bondRecipient,
bool disputed,
bool settlementResolution,
address settleCaller
);

event AdminPropertiesSet(IERC20 defaultCurrency, uint64 defaultLiveness, uint256 burnedBondPercentage);
}
93 changes: 93 additions & 0 deletions contracts/mock/MockOptimisticOracleV3.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity 0.8.22;

import {
OptimisticOracleV3CallbackRecipientInterface
} from "../interfaces/clients/OptimisticOracleV3CallbackRecipientInterface.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

/**
* @title MockOptimisticOracleV3
* @notice Simplified mock implementation of UMA's OptimisticOracleV3 for testing UMADisputeResolverAdapter
* @dev This mock only implements the minimal functionality needed for testing:
* - Simple bond management (getter/setter)
* - Basic assertTruth that returns predictable IDs
* - Direct callback triggers for testing adapter callbacks
*/
contract MockOptimisticOracleV3 {
mapping(address => uint256) public minimumBonds;

uint256 private constant DEFAULT_MINIMUM_BOND = 1e18; // 1 token

/**
* @notice Get minimum bond for a currency
* @param currency The currency address
* @return The minimum bond amount
*/
function getMinimumBond(address currency) external view returns (uint256) {
uint256 customBond = minimumBonds[currency];
return customBond > 0 ? customBond : DEFAULT_MINIMUM_BOND;
}

/**
* @notice Set minimum bond for testing purposes
* @param currency The currency address
* @param bond The minimum bond amount
*/
function setMinimumBond(address currency, uint256 bond) external {
minimumBonds[currency] = bond;
}

/**
* @notice Simplified assertion creation for testing
* @param claim The claim being asserted
* @param asserter The account that will receive the bond back
* @param currency The currency for the bond
* @param bond The bond amount
* @return assertionId The unique assertion identifier
*/
function assertTruth(
bytes memory claim,
address asserter,
address, // callbackRecipient - not used in simplified mock
address, // escalationManager - not used in simplified mock
uint64, // liveness - not used in simplified mock
IERC20 currency,
uint256 bond,
bytes32, // identifier - not used in simplified mock
bytes32 // domainId - not used in simplified mock
) external returns (bytes32 assertionId) {
require(asserter != address(0), "Asserter cannot be zero");
require(bond >= this.getMinimumBond(address(currency)), "Bond too low");

// Transfer bond from caller for realistic testing
currency.transferFrom(msg.sender, address(this), bond);

// Generate simple, predictable assertion ID
assertionId = keccak256(abi.encode(claim, asserter, block.timestamp, msg.sender));

return assertionId;
}

/**
* @notice Test helper to trigger resolved callback on the adapter
* @param callbackRecipient The adapter address to call back
* @param assertionId The assertion ID
* @param assertedTruthfully Whether the assertion was deemed truthful
*/
function triggerResolvedCallback(address callbackRecipient, bytes32 assertionId, bool assertedTruthfully) external {
OptimisticOracleV3CallbackRecipientInterface(callbackRecipient).assertionResolvedCallback(
assertionId,
assertedTruthfully
);
}

/**
* @notice Test helper to trigger disputed callback on the adapter
* @param callbackRecipient The adapter address to call back
* @param assertionId The assertion ID
*/
function triggerDisputedCallback(address callbackRecipient, bytes32 assertionId) external {
OptimisticOracleV3CallbackRecipientInterface(callbackRecipient).assertionDisputedCallback(assertionId);
}
}
Loading
Loading