Skip to content

Commit

Permalink
feat: uma storage and pending approach optimised
Browse files Browse the repository at this point in the history
  • Loading branch information
0xnyxos committed Dec 12, 2023
1 parent 9e526e2 commit 54a9339
Show file tree
Hide file tree
Showing 5 changed files with 176 additions and 75 deletions.
2 changes: 1 addition & 1 deletion script/v2/V2DeployContracts.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ contract V2DeployContracts is Script, HelperV2 {
uint256 timeOut = 2 hours;
address umaCurrency = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8;
address umaV2Finder = 0xB0b9f73B424AD8dc58156C2AE0D7A1115D1EcCd1;
uint256 reward = 5e6;
uint128 reward = 5e6;

// uint256 umaDecimals = 18;
// string memory umaDescription = "FUSD/USD";
Expand Down
55 changes: 26 additions & 29 deletions src/v2/oracles/individual/UmaV2AssertionProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
*/
contract UmaV2AssertionProvider is Ownable {
struct AssertionAnswer {
uint80 roundId;
int256 assertion;
uint256 startedAt;
uint8 roundId;
uint8 answeredInRound;
int8 assertion;
uint128 pendingRequestAt;
uint256 updatedAt;
uint80 answeredInRound;
}

uint256 public constant REQUEST_TIMEOUT = 3600 * 3;
uint256 public constant ORACLE_LIVENESS_TIME = 3600 * 2;
bytes32 public constant PRICE_IDENTIFIER = "YES_OR_NO_QUERY";
string public constant ANCILLARY_TAIL =
". P1: 0 for NO, P2: 1 for YES, P3: 2 for UNDETERMINED";

uint256 public immutable timeOut;
uint256 public immutable assertionTimeOut;
IUmaV2 public immutable oo;
IFinder public immutable finder;
IERC20 public immutable currency;

AssertionAnswer public answer;
uint128 public reward;
uint128 public coverageStart;
string public description;
string public ancillaryData;
AssertionAnswer public answer;
AssertionAnswer public pendingAnswer;
uint256 public reward;
uint256 public coverageStart;

mapping(uint256 => uint256) public marketIdToConditionType;

Expand All @@ -50,14 +50,14 @@ contract UmaV2AssertionProvider is Ownable {
event PriceRequested();

constructor(
uint256 _timeOut,
uint256 _assertionTimeOut,
string memory _description,
address _finder,
address _currency,
string memory _ancillaryData,
uint256 _reward
uint128 _reward
) {
if (_timeOut == 0) revert InvalidInput();
if (_assertionTimeOut == 0) revert InvalidInput();
if (keccak256(bytes(_description)) == keccak256(""))
revert InvalidInput();
if (_finder == address(0)) revert ZeroAddress();
Expand All @@ -66,15 +66,15 @@ contract UmaV2AssertionProvider is Ownable {
revert InvalidInput();
if (_reward == 0) revert InvalidInput();

timeOut = _timeOut;
assertionTimeOut = _assertionTimeOut;
description = _description;

finder = IFinder(_finder);
oo = IUmaV2(finder.getImplementationAddress("OptimisticOracleV2"));
currency = IERC20(_currency);
ancillaryData = _ancillaryData;
reward = _reward;
coverageStart = block.timestamp;
coverageStart = uint128(block.timestamp);
}

/*//////////////////////////////////////////////////////////////
Expand All @@ -90,13 +90,13 @@ contract UmaV2AssertionProvider is Ownable {
emit MarketConditionSet(_marketId, _condition);
}

function updateCoverageStart(uint256 _coverageStart) external onlyOwner {
function updateCoverageStart(uint128 _coverageStart) external onlyOwner {
if (_coverageStart < coverageStart) revert InvalidInput();
coverageStart = _coverageStart;
emit CoverageStartUpdated(_coverageStart);
}

function updateReward(uint256 newReward) external onlyOwner {
function updateReward(uint128 newReward) external onlyOwner {
if (newReward == 0) revert InvalidInput();
reward = newReward;
emit RewardUpdated(newReward);
Expand All @@ -113,14 +113,12 @@ contract UmaV2AssertionProvider is Ownable {
) external {
if (msg.sender != address(oo)) revert InvalidCaller();

AssertionAnswer memory _pendingAnswer = pendingAnswer;
AssertionAnswer memory _answer = answer;

_answer.startedAt = _pendingAnswer.startedAt;
_answer.updatedAt = _timestamp;
_answer.assertion = _price;
_answer.roundId = 1;
_answer.answeredInRound = 1;
AssertionAnswer memory _answer;
_answer.updatedAt = uint128(_timestamp);
_answer.assertion = int8(_price);
_answer.roundId = answer.roundId + 1;
_answer.answeredInRound = answer.answeredInRound + 1;
_answer.pendingRequestAt = answer.pendingRequestAt;
answer = _answer;

emit PriceSettled(_price);
Expand All @@ -130,7 +128,8 @@ contract UmaV2AssertionProvider is Ownable {
PUBLIC
//////////////////////////////////////////////////////////////*/
function requestLatestAssertion() external {
if (pendingAnswer.startedAt != 0) revert RequestInProgress();
if (answer.pendingRequestAt + REQUEST_TIMEOUT > block.timestamp)
revert RequestInProgress();

bytes memory _bytesAncillary = abi.encodePacked(
ancillaryData,
Expand Down Expand Up @@ -161,9 +160,7 @@ contract UmaV2AssertionProvider is Ownable {
true
);

AssertionAnswer memory _pendingAnswer;
_pendingAnswer.startedAt = block.timestamp;
pendingAnswer = _pendingAnswer;
answer.pendingRequestAt = uint128(block.timestamp);

emit PriceRequested();
}
Expand All @@ -175,7 +172,7 @@ contract UmaV2AssertionProvider is Ownable {
AssertionAnswer memory assertionAnswer = answer;

if (assertionAnswer.updatedAt == 0) revert OraclePriceZero();
if ((block.timestamp - assertionAnswer.updatedAt) > timeOut)
if ((block.timestamp - assertionAnswer.updatedAt) > assertionTimeOut)
revert PriceTimedOut();

if (assertionAnswer.assertion == 1) return true;
Expand Down
45 changes: 21 additions & 24 deletions src/v2/oracles/individual/UmaV2PriceProvider.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,27 +19,27 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
*/
contract UmaV2PriceProvider is Ownable {
struct PriceAnswer {
uint80 roundId;
int256 price;
uint256 startedAt;
uint256 updatedAt;
uint80 answeredInRound;
uint128 roundId;
uint128 answeredInRound;
int128 price;
uint128 updatedAt;
uint256 pendingRequestAt;
}

uint256 public constant ORACLE_LIVENESS_TIME = 3600 * 2;
bytes32 public constant PRICE_IDENTIFIER = "TOKEN_PRICE";
uint256 public constant REQUEST_TIMEOUT = 3600 * 3;

uint256 public immutable timeOut;
IUmaV2 public immutable oo;
IFinder public immutable finder;
uint256 public immutable decimals;
IERC20 public immutable currency;

string public description;
string public ancillaryData;
PriceAnswer public answer;
PriceAnswer public pendingAnswer;
uint256 public reward;
string public description;
string public ancillaryData;

mapping(uint256 => uint256) public marketIdToConditionType;

Expand Down Expand Up @@ -107,14 +107,12 @@ contract UmaV2PriceProvider is Ownable {
) external {
if (msg.sender != address(oo)) revert InvalidCaller();

PriceAnswer memory _pendingAnswer = pendingAnswer;
PriceAnswer memory _answer = answer;

_answer.startedAt = _pendingAnswer.startedAt;
_answer.updatedAt = _timestamp;
_answer.price = _price;
_answer.roundId = 1;
_answer.answeredInRound = 1;
PriceAnswer memory _answer;
_answer.updatedAt = uint128(_timestamp);
_answer.price = int128(_price);
_answer.roundId = answer.roundId + 1;
_answer.answeredInRound = answer.answeredInRound + 1;
_answer.pendingRequestAt = answer.pendingRequestAt;
answer = _answer;

emit PriceSettled(_price);
Expand All @@ -124,7 +122,8 @@ contract UmaV2PriceProvider is Ownable {
PUBLIC
//////////////////////////////////////////////////////////////*/
function requestLatestPrice() external {
if (pendingAnswer.startedAt != 0) revert RequestInProgress();
if (answer.pendingRequestAt + REQUEST_TIMEOUT > block.timestamp)
revert RequestInProgress();

bytes memory _bytesAncillary = abi.encodePacked(ancillaryData);
currency.transferFrom(msg.sender, address(this), reward);
Expand All @@ -151,9 +150,7 @@ contract UmaV2PriceProvider is Ownable {
true
);

PriceAnswer memory _pendingAnswer;
_pendingAnswer.startedAt = block.timestamp;
pendingAnswer = _pendingAnswer;
answer.pendingRequestAt = block.timestamp;

emit PriceRequested();
}
Expand All @@ -164,17 +161,17 @@ contract UmaV2PriceProvider is Ownable {
returns (
uint80 roundId,
int256 price,
uint256 startedAt,
uint256 pendingRequestAt,
uint256 updatedAt,
uint80 answeredInRound
)
{
PriceAnswer memory _answer = answer;
roundId = uint80(_answer.roundId);
price = _answer.price;
updatedAt = _answer.updatedAt;
roundId = _answer.roundId;
startedAt = _answer.startedAt;
answeredInRound = _answer.answeredInRound;
pendingRequestAt = _answer.pendingRequestAt;
answeredInRound = uint80(_answer.answeredInRound);
}

/** @notice Fetch token price from priceFeed (Chainlink oracle address)
Expand Down
80 changes: 67 additions & 13 deletions test/V2/oracles/individual/UmaV2AssertionProvider.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract UmaV2AssertionProviderTest is Helper {
string public umaDescription;
address public umaCurrency;
string public ancillaryData;
uint256 public reward;
uint128 public reward;

function setUp() public {
arbForkId = vm.createFork(ARBITRUM_RPC_URL);
Expand Down Expand Up @@ -68,7 +68,7 @@ contract UmaV2AssertionProviderTest is Helper {
function testUmaV2AssertionProvider() public {
assertEq(umaV2AssertionProvider.ORACLE_LIVENESS_TIME(), 3600 * 2);
assertEq(umaV2AssertionProvider.PRICE_IDENTIFIER(), "YES_OR_NO_QUERY");
assertEq(umaV2AssertionProvider.timeOut(), TIME_OUT);
assertEq(umaV2AssertionProvider.assertionTimeOut(), TIME_OUT);
assertEq(address(umaV2AssertionProvider.oo()), umaV2);
assertEq(address(umaV2AssertionProvider.finder()), UMAV2_FINDER);
assertEq(address(umaV2AssertionProvider.currency()), umaCurrency);
Expand Down Expand Up @@ -104,7 +104,7 @@ contract UmaV2AssertionProviderTest is Helper {
}

function testUpdateCoverageStartUmaV2Assert() public {
uint256 newCoverageStart = block.timestamp + 1 days;
uint128 newCoverageStart = uint128(block.timestamp + 1 days);
vm.expectEmit(true, true, true, true);
emit CoverageStartUpdated(newCoverageStart);
umaV2AssertionProvider.updateCoverageStart(newCoverageStart);
Expand All @@ -113,7 +113,7 @@ contract UmaV2AssertionProviderTest is Helper {
}

function testUpdateRewardUmaV2Assert() public {
uint256 newReward = 1000;
uint128 newReward = 1000;
vm.expectEmit(true, true, true, true);
emit RewardUpdated(newReward);
umaV2AssertionProvider.updateReward(newReward);
Expand Down Expand Up @@ -156,17 +156,17 @@ contract UmaV2AssertionProviderTest is Helper {
price
);
(
uint80 roundId,
int256 _price,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
uint8 roundId,
uint8 answeredInRound,
int8 _price,
uint256 pendingRequestAt,
uint256 updatedAt
) = umaV2AssertionProvider.answer();

// Checking the data
assertEq(roundId, 1);
assertEq(_price, price);
assertEq(startedAt, previousTimestamp);
assertEq(pendingRequestAt, previousTimestamp);
assertEq(updatedAt, block.timestamp);
assertEq(answeredInRound, 1);
}
Expand All @@ -176,8 +176,8 @@ contract UmaV2AssertionProviderTest is Helper {
////////////////////////////////////////////////
function testrequestLatestAssertionUmaV2Assert() public {
umaV2AssertionProvider.requestLatestAssertion();
(, , uint256 startedAt, , ) = umaV2AssertionProvider.pendingAnswer();
assertEq(startedAt, block.timestamp);
(, , , uint128 pendingRequestAt, ) = umaV2AssertionProvider.answer();
assertEq(pendingRequestAt, block.timestamp);
}

function testCheckAssertionUmaV2Assert() public {
Expand Down Expand Up @@ -217,6 +217,36 @@ contract UmaV2AssertionProviderTest is Helper {
assertEq(condition, false);
}

function testPriceDeliveredRound2UmaV2Assert() public {
// Config the data with mock oracle
address mockUmaV2 = _configureSettledPrice(true);

uint256 conditionType = 2;
umaV2AssertionProvider.setConditionType(marketId, conditionType);
(bool condition, int256 price) = umaV2AssertionProvider.conditionMet(
2 ether,
marketId
);
(uint8 roundId, uint8 answeredInRound, , , ) = umaV2AssertionProvider
.answer();
assertEq(price, 0);
assertEq(condition, true);
assertEq(roundId, 1);
assertEq(answeredInRound, 1);

// Updating the price
_updatePrice(false, mockUmaV2);
(condition, price) = umaV2AssertionProvider.conditionMet(
2 ether,
marketId
);
(roundId, answeredInRound, , , ) = umaV2AssertionProvider.answer();
assertEq(price, 0);
assertEq(condition, false);
assertEq(roundId, 2);
assertEq(answeredInRound, 2);
}

////////////////////////////////////////////////
// REVERT CASES //
////////////////////////////////////////////////
Expand Down Expand Up @@ -342,7 +372,9 @@ contract UmaV2AssertionProviderTest is Helper {
////////////////////////////////////////////////
// HELPER //
////////////////////////////////////////////////
function _configureSettledPrice(bool condition) internal {
function _configureSettledPrice(
bool condition
) internal returns (address mockUma) {
// Config the data using the mock oracle
bytes32 emptyBytes32;
bytes memory emptyBytes;
Expand Down Expand Up @@ -373,5 +405,27 @@ contract UmaV2AssertionProviderTest is Helper {
emptyBytes,
price
);

return address(mockUmaV2);
}

function _updatePrice(bool condition, address mockUmaV2) internal {
// Config the data using the mock oracle
bytes32 emptyBytes32;
bytes memory emptyBytes;
int256 price = condition ? int256(1) : int256(0);

// Configuring the pending answer
umaV2AssertionProvider.requestLatestAssertion();
vm.warp(block.timestamp + 1 days);

// Configuring the answer via the callback
vm.prank(address(mockUmaV2));
umaV2AssertionProvider.priceSettled(
emptyBytes32,
block.timestamp,
emptyBytes,
price
);
}
}
Loading

0 comments on commit 54a9339

Please sign in to comment.