Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Feat: event based OO #45

Merged
merged 4 commits into from
Jun 21, 2022
Merged
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
8 changes: 4 additions & 4 deletions .github/workflows/CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ All contributions are welcome and greatly appreciated! This document serves to o
1. Fork this repo
2. Clone your fork
3. Follow the [installation instructions](./README.md) in the monorepo's top level README.
4. Open pull requests with the `[WIP]` flag against the `main` branch and include a description of the intended change in the PR description.
4. Open pull requests with the `WIP` flag against the `main` branch and include a description of the intended change in the PR description.

Before removing the `[WIP]` tag and submitting a PR for review, make sure that:
Before removing the `WIP` tag and submitting a PR for review, make sure that:

- it passes our linter checks
- the test suite passes for all packages
Expand All @@ -20,10 +20,10 @@ Before removing the `[WIP]` tag and submitting a PR for review, make sure that:

Our main branch, `main`, represents the current development state of the codebase. All pull requests should be opened against `main`.

Name your branch with the format `{fix | feature | refactor }/{ description }`
Name your branch with the format `{fix | feat | refactor }/{ description }`

- A `fix` addresses a bug or other issue
- A `feature` adds new functionality/interface surface area
- A `feat` adds new functionality/interface surface area
- A `refactor` changes no business logic or interfaces, but improves implementation

### Additional Details
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<!-- Pull request template for the Adapter -->
<!-- Pull request template for the UMA CTF Adapter -->
<!-- Delete any sub-sections not used rather than leaving them empty. -->

## Overview
Expand Down Expand Up @@ -32,7 +32,7 @@
<!-- Check any boxes that are already complete upon creation of the PR, and update whenever necessary. -->
<!-- Make sure to check the "Ready for review" box when you are signing off on your changes for merge! -->

- [ ] Prefix PR title with `[WIP]` if necessary (changes not yet made).
- [ ] Prefix PR title with `WIP` if necessary (changes not yet made).
- [ ] Add tests to cover changes as needed.
- [ ] Update documentation/changelog as needed.
- [ ] Verify all tests run correctly in CI and pass.
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/.solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
],
"compiler-version": [
"error",
"0.8.0"
"0.8.10"
],
"const-name-snakecase": "off",
"constructor-syntax": "error",
Expand Down
103 changes: 37 additions & 66 deletions packages/contracts/contracts/UmaConditionalTokensBinaryAdapter.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
Expand All @@ -11,33 +11,11 @@ import { IConditionalTokens } from "./interfaces/IConditionalTokens.sol";
import { OptimisticOracleInterface } from "./interfaces/OptimisticOracleInterface.sol";
import { AddressWhitelistInterface } from "./interfaces/AddressWhitelistInterface.sol";

/// @title UmaConditionalTokensBinaryAdapter
/// @notice Enables Conditional Token resolution via UMA's Optimistic Oracle
contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {
/// @notice Auth
mapping(address => uint256) public wards;

/// @notice Authorizes a user
function rely(address usr) external auth {
wards[usr] = 1;
emit AuthorizedUser(usr);
}

/// @notice Deauthorizes a user
function deny(address usr) external auth {
wards[usr] = 0;
emit DeauthorizedUser(usr);
}

event AuthorizedUser(address indexed usr);
event DeauthorizedUser(address indexed usr);

/// @notice - Authorization modifier
modifier auth() {
require(wards[msg.sender] == 1, "Adapter/not-authorized");
_;
}
import { Auth } from "./mixins/Auth.sol";

/// @title UmaConditionalTokensBinaryAdapter
/// @notice Enables CTF resolution via UMA's Optimistic Oracle
contract UmaConditionalTokensBinaryAdapter is Auth, ReentrancyGuard {
/// @notice Conditional Tokens
IConditionalTokens public immutable conditionalTokenContract;

Expand Down Expand Up @@ -78,11 +56,9 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {
/// @notice Mapping of questionID to QuestionData
mapping(bytes32 => QuestionData) public questions;

/*
////////////////////////////////////////////////////////////////////
/*////////////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////////////
*/
///////////////////////////////////////////////////////////////////*/

/// @notice Emitted when the UMA Finder is changed
event NewFinderAddress(address oldFinder, address newFinder);
Expand Down Expand Up @@ -228,7 +204,7 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {

/// @notice Request resolution data from the Optimistic Oracle
/// @param questionID - The unique questionID of the question
function requestResolutionData(bytes32 questionID) public nonReentrant {
function requestResolutionData(bytes32 questionID) external nonReentrant {
require(
readyToRequestResolution(questionID),
"Adapter::requestResolutionData: Question not ready to be resolved"
Expand Down Expand Up @@ -272,6 +248,13 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {

/// @notice Request a price from the Optimistic Oracle
/// @dev Transfers reward token from the requestor if non-zero reward is specified
/// @param requestor - Address of the requestor
/// @param priceIdentifier - Bytes32 identifier for the OO
/// @param timestamp - Timestamp used in the OO request
/// @param ancillaryData - Data used to resolve a question
/// @param rewardToken - Address of the reward token
/// @param reward - Reward token amount
/// @param bond - Bond amount used, also in rewardToken
function _requestPrice(
address requestor,
bytes32 priceIdentifier,
Expand Down Expand Up @@ -337,7 +320,7 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {
/// @notice Settle/finalize the resolution data of a question
/// @notice If the OO returns the ignore price, this method resets the question, allowing new price requests
/// @param questionID - The unique questionID of the question
function settle(bytes32 questionID) public {
function settle(bytes32 questionID) external {
require(readyToSettle(questionID), "Adapter::settle: questionID is not ready to be settled");
QuestionData storage questionData = questions[questionID];
require(!questionData.paused, "Adapter::settle: Question is paused");
Expand All @@ -346,36 +329,26 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {
}

function _settle(bytes32 questionID, QuestionData storage questionData) internal {
OptimisticOracleInterface optimisticOracle = getOptimisticOracle();

int256 proposedPrice = optimisticOracle
.getRequest(address(this), identifier, questionData.requestTimestamp, questionData.ancillaryData)
.proposedPrice;
// Get the price from the OO
int256 price = getOptimisticOracle().settleAndGetPrice(
identifier,
questionData.requestTimestamp,
questionData.ancillaryData
);

// NOTE: If the proposed price is the ignore price, reset the question, allowing new resolution requests
if (proposedPrice == ignorePrice()) {
_resetQuestion(questionID, questionData, optimisticOracle);
return;
// If the price from the OO is the ignore price, reset the question by setting requestTimestamp to 0
// allowing new resolution requests
if (price == ignorePrice()) {
return _resetQuestion(questionID, questionData);
}

// Set the settled block number
questionData.settled = block.number;

// Settle the price
int256 settledPrice = optimisticOracle.settleAndGetPrice(
identifier,
questionData.requestTimestamp,
questionData.ancillaryData
);
emit QuestionSettled(questionID, settledPrice, questionData.requestTimestamp < questionData.resolutionTime);
emit QuestionSettled(questionID, price, questionData.requestTimestamp < questionData.resolutionTime);
}

function _resetQuestion(
bytes32 questionID,
QuestionData storage questionData,
OptimisticOracleInterface optimisticOracle
) internal {
optimisticOracle.settleAndGetPrice(identifier, questionData.requestTimestamp, questionData.ancillaryData);
function _resetQuestion(bytes32 questionID, QuestionData storage questionData) internal {
questionData.requestTimestamp = 0;
emit QuestionReset(questionID);
}
Expand All @@ -390,7 +363,6 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {
resolutionDataRequested(questionData),
"Adapter::getExpectedPayouts: resolutionData has not been requested"
);
require(!questionData.resolved, "Adapter::getExpectedPayouts: questionID is already resolved");
require(questionData.settled > 0, "Adapter::getExpectedPayouts: questionID is not settled");
require(!questionData.paused, "Adapter::getExpectedPayouts: Question is paused");

Expand Down Expand Up @@ -431,28 +403,27 @@ contract UmaConditionalTokensBinaryAdapter is ReentrancyGuard {

/// @notice Resolves a question
/// @param questionID - The unique questionID of the question
function reportPayouts(bytes32 questionID) public {
function reportPayouts(bytes32 questionID) external {
QuestionData storage questionData = questions[questionID];

// Payouts: [YES, NO]
// getExpectedPayouts verifies that questionID is settled and can be resolved
uint256[] memory payouts = getExpectedPayouts(questionID);

require(!questionData.resolved, "Adapter::getExpectedPayouts: questionID is already resolved");
require(
block.number > questionData.settled,
"Adapter::reportPayouts: Attempting to settle and reportPayouts in the same block"
);

// Payouts: [YES, NO]
// getExpectedPayouts verifies that questionID is settled and can be resolved
uint256[] memory payouts = getExpectedPayouts(questionID);

questionData.resolved = true;
conditionalTokenContract.reportPayouts(questionID, payouts);
emit QuestionResolved(questionID, false);
}

/*
////////////////////////////////////////////////////////////////////
AUTHORIZED ONLY FUNCTIONS
////////////////////////////////////////////////////////////////////
*/
/*////////////////////////////////////////////////////////////////////
AUTHORIZED FUNCTIONS
///////////////////////////////////////////////////////////////////*/

/// @notice Allows an authorized user to update a question
/// @param questionID - The unique questionID of the question
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.0;
pragma solidity ^0.8.10;

interface AddressWhitelistInterface {
function addToWhitelist(address newElement) external;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

/**
* @title Provides addresses of the live contracts implementing certain interfaces.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

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

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/contracts/libraries/TransferHelper.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

Expand Down
30 changes: 30 additions & 0 deletions packages/contracts/contracts/mixins/Auth.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: None
pragma solidity ^0.8.10;

/// @title Auth
/// @notice Provides access control modifiers
abstract contract Auth {
/// @notice Auth
mapping(address => uint256) public wards;

/// @notice Authorizes a user
function rely(address usr) external auth {
wards[usr] = 1;
emit AuthorizedUser(usr);
}

/// @notice Deauthorizes a user
function deny(address usr) external auth {
wards[usr] = 0;
emit DeauthorizedUser(usr);
}

event AuthorizedUser(address indexed usr);
event DeauthorizedUser(address indexed usr);

/// @notice - Authorization modifier
modifier auth() {
require(wards[msg.sender] == 1, "Adapter/not-authorized");
_;
}
}
2 changes: 1 addition & 1 deletion packages/contracts/contracts/mocks/CTHelpers.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: LGPL-3.0

pragma solidity 0.8.0;
pragma solidity ^0.8.10;

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.0;
pragma solidity ^0.8.10;

import { SafeMath } from "@openzeppelin/contracts/utils/math/SafeMath.sol";
import { CTHelpers } from "./CTHelpers.sol";
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/contracts/test/Griefer.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

interface AdapterInterface {
function settle(bytes32 questionID) external;
Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/contracts/test/TestERC20.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;
pragma solidity ^0.8.10;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Expand Down
2 changes: 1 addition & 1 deletion packages/contracts/hardhat.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ const config: HardhatUserConfig = {
solidity: {
compilers: [
{
version: "0.8.0",
version: "0.8.10",
settings: {
// https://hardhat.org/hardhat-network/#solidity-optimizer-support
optimizer: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1407,7 +1407,7 @@ describe("", function () {
request.proposedPrice = BigNumber.from(IGNORE_PRICE);
await optimisticOracle.mock.getRequest.returns(request);
await optimisticOracle.mock.hasPrice.returns(true);
await optimisticOracle.mock.settleAndGetPrice.returns(1);
await optimisticOracle.mock.settleAndGetPrice.returns(IGNORE_PRICE);

expect(await umaBinaryAdapter.connect(this.signers.tester).settle(qID))
.to.emit(umaBinaryAdapter, "QuestionReset")
Expand Down