Skip to content

Commit

Permalink
Merge pull request #61 from Polymarket/fix/stuck-reward-tokens
Browse files Browse the repository at this point in the history
Fix: H01
  • Loading branch information
JonathanAmenechi authored Aug 22, 2023
2 parents 11647f1 + d905721 commit 2953a59
Show file tree
Hide file tree
Showing 5 changed files with 258 additions and 55 deletions.
87 changes: 45 additions & 42 deletions .gas-snapshot
Original file line number Diff line number Diff line change
Expand Up @@ -9,49 +9,52 @@ BulletinBoardTest:testGetLatestUpdate() (gas: 88406)
BulletinBoardTest:testGetUpdate() (gas: 87904)
BulletinBoardTest:testPostUpdate() (gas: 83918)
BulletinBoardTest:testPostUpdateMultiple() (gas: 142834)
UmaCtfAdapterTest:testEmergencyResolve() (gas: 674039)
UmaCtfAdapterTest:testEmergencyResolvePaused() (gas: 669958)
UmaCtfAdapterTest:testEmergencyResolveRevertInvalidPayouts() (gas: 597594)
UmaCtfAdapterTest:testEmergencyResolveRevertNotAdmin() (gas: 600990)
UmaCtfAdapterTest:testEmergencyResolveRevertNotFlagged() (gas: 574689)
UmaCtfAdapterTest:testEmergencyResolve() (gas: 674897)
UmaCtfAdapterTest:testEmergencyResolvePaused() (gas: 670573)
UmaCtfAdapterTest:testEmergencyResolveRevertInvalidPayouts() (gas: 597973)
UmaCtfAdapterTest:testEmergencyResolveRevertNotAdmin() (gas: 601367)
UmaCtfAdapterTest:testEmergencyResolveRevertNotFlagged() (gas: 575001)
UmaCtfAdapterTest:testEmergencyResolveRevertNotInitialized() (gas: 18313)
UmaCtfAdapterTest:testEmergencyResolveRevertSafetyPeriod() (gas: 597849)
UmaCtfAdapterTest:testExpectedPayouts() (gas: 708827)
UmaCtfAdapterTest:testExpectedPayoutsRevertIgnorePriceReceived() (gas: 1202332)
UmaCtfAdapterTest:testExpectedPayoutsRevertNotInitialized() (gas: 13300)
UmaCtfAdapterTest:testExpectedPayoutsRevertPriceNotAvailable() (gas: 575538)
UmaCtfAdapterTest:testFlag() (gas: 601560)
UmaCtfAdapterTest:testFlagRevertNotAdmin() (gas: 574852)
UmaCtfAdapterTest:testEmergencyResolveRevertSafetyPeriod() (gas: 598226)
UmaCtfAdapterTest:testEmergencyResolveWhenRefundExists() (gas: 1191277)
UmaCtfAdapterTest:testExpectedPayouts() (gas: 709449)
UmaCtfAdapterTest:testExpectedPayoutsRevertIgnorePriceReceived() (gas: 1203605)
UmaCtfAdapterTest:testExpectedPayoutsRevertNotInitialized() (gas: 13343)
UmaCtfAdapterTest:testExpectedPayoutsRevertPriceNotAvailable() (gas: 575872)
UmaCtfAdapterTest:testFlag() (gas: 601916)
UmaCtfAdapterTest:testFlagRevertNotAdmin() (gas: 575208)
UmaCtfAdapterTest:testFlagRevertNotInitialized() (gas: 17492)
UmaCtfAdapterTest:testInitialize() (gas: 641835)
UmaCtfAdapterTest:testInitializeCustomLiveness() (gas: 549484)
UmaCtfAdapterTest:testInitializeRevertCustomLiveness() (gas: 450507)
UmaCtfAdapterTest:testInitializeRevertInsufficientRewardBalance() (gas: 312973)
UmaCtfAdapterTest:testInitializeRevertInvalidAncillaryData() (gas: 20703)
UmaCtfAdapterTest:testInitializeRevertOnSameQuestion() (gas: 435398)
UmaCtfAdapterTest:testInitializeRevertUnsupportedRewardToken() (gas: 854143)
UmaCtfAdapterTest:testInitializeZeroRewardAndBond() (gas: 494815)
UmaCtfAdapterTest:testPause() (gas: 590533)
UmaCtfAdapterTest:testPauseRevertNotAdmin() (gas: 583918)
UmaCtfAdapterTest:testPauseRevertNotInitialized() (gas: 17523)
UmaCtfAdapterTest:testPriceDisputed() (gas: 945593)
UmaCtfAdapterTest:testPriceDisputedAlreadyReset() (gas: 1216563)
UmaCtfAdapterTest:testPriceDisputedIgnorePriceReceived() (gas: 1373772)
UmaCtfAdapterTest:testPriceDisputedResolveAfterDispute() (gas: 1149312)
UmaCtfAdapterTest:testPriceDisputedRevertNotOO() (gas: 11901)
UmaCtfAdapterTest:testProposed() (gas: 681763)
UmaCtfAdapterTest:testReady() (gas: 779898)
UmaCtfAdapterTest:testReset() (gas: 755607)
UmaCtfAdapterTest:testResetAlreadyReset() (gas: 938667)
UmaCtfAdapterTest:testInitialize() (gas: 642410)
UmaCtfAdapterTest:testInitializeCustomLiveness() (gas: 550060)
UmaCtfAdapterTest:testInitializeRevertCustomLiveness() (gas: 450885)
UmaCtfAdapterTest:testInitializeRevertInsufficientRewardBalance() (gas: 313330)
UmaCtfAdapterTest:testInitializeRevertInvalidAncillaryData() (gas: 20681)
UmaCtfAdapterTest:testInitializeRevertOnSameQuestion() (gas: 435754)
UmaCtfAdapterTest:testInitializeRevertUnsupportedRewardToken() (gas: 854099)
UmaCtfAdapterTest:testInitializeZeroRewardAndBond() (gas: 495389)
UmaCtfAdapterTest:testPause() (gas: 591326)
UmaCtfAdapterTest:testPauseRevertNotAdmin() (gas: 584275)
UmaCtfAdapterTest:testPauseRevertNotInitialized() (gas: 17502)
UmaCtfAdapterTest:testPriceDisputed() (gas: 946424)
UmaCtfAdapterTest:testPriceDisputedDvmRespondsIgnore() (gas: 1375613)
UmaCtfAdapterTest:testPriceDisputedDvmRespondsNo() (gas: 1213524)
UmaCtfAdapterTest:testPriceDisputedDvmRespondsYes() (gas: 1253323)
UmaCtfAdapterTest:testPriceDisputedResolveAfterDispute() (gas: 1150359)
UmaCtfAdapterTest:testPriceDisputedRevertNotOO() (gas: 11944)
UmaCtfAdapterTest:testProposed() (gas: 682381)
UmaCtfAdapterTest:testReady() (gas: 780512)
UmaCtfAdapterTest:testReset() (gas: 756570)
UmaCtfAdapterTest:testResetAlreadyReset() (gas: 940335)
UmaCtfAdapterTest:testResetRevertNotInitialized() (gas: 17468)
UmaCtfAdapterTest:testResetRevertResolved() (gas: 773205)
UmaCtfAdapterTest:testResolve() (gas: 768886)
UmaCtfAdapterTest:testResolveIgnorePrice() (gas: 1352578)
UmaCtfAdapterTest:testResolveNo() (gas: 728760)
UmaCtfAdapterTest:testResolveRevertAlreadyResolved() (gas: 770772)
UmaCtfAdapterTest:testResolveRevertNotInitialized() (gas: 12872)
UmaCtfAdapterTest:testResolveRevertPaused() (gas: 575854)
UmaCtfAdapterTest:testResolveRevertUnavailablePrice() (gas: 575393)
UmaCtfAdapterTest:testResolveUnknown() (gas: 788454)
UmaCtfAdapterTest:testResolveYes() (gas: 768577)
UmaCtfAdapterTest:testResetRevertResolved() (gas: 774063)
UmaCtfAdapterTest:testResetWhenRefundExists() (gas: 1236223)
UmaCtfAdapterTest:testResolve() (gas: 769743)
UmaCtfAdapterTest:testResolveIgnorePrice() (gas: 1354088)
UmaCtfAdapterTest:testResolveNo() (gas: 729355)
UmaCtfAdapterTest:testResolveRevertAlreadyResolved() (gas: 771652)
UmaCtfAdapterTest:testResolveRevertNotInitialized() (gas: 12895)
UmaCtfAdapterTest:testResolveRevertPaused() (gas: 576210)
UmaCtfAdapterTest:testResolveRevertUnavailablePrice() (gas: 575749)
UmaCtfAdapterTest:testResolveUnknown() (gas: 789093)
UmaCtfAdapterTest:testResolveYes() (gas: 769195)
UmaCtfAdapterTest:testSetup() (gas: 23659)
38 changes: 30 additions & 8 deletions src/UmaCtfAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -148,11 +148,14 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
bytes32 questionID = keccak256(ancillaryData);
QuestionData storage questionData = questions[questionID];

if (questionData.reset) return;
if (questionData.reset) {
questionData.refund = true;
return;
}

// If the question has not been reset previously, reset the question
// Ensures that there are at most 2 OO Requests at a time for a question
_reset(address(this), questionID, questionData);
_reset(address(this), questionID, false, questionData);
}

/// @notice Checks if a question is initialized
Expand Down Expand Up @@ -199,8 +202,11 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
if (!_isInitialized(questionData)) revert NotInitialized();
if (questionData.resolved) revert Resolved();

// Refund the reward to the question creator if necessary
if (questionData.refund) _refund(questionData);

// Reset the question, paying for the price request from the caller
_reset(msg.sender, questionID, questionData);
_reset(msg.sender, questionID, true, questionData);
}

/// @notice Allows an admin to resolve a CTF market in an emergency
Expand All @@ -215,6 +221,10 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
if (block.timestamp < questionData.emergencyResolutionTimestamp) revert SafetyPeriodNotPassed();

questionData.resolved = true;

// Refund the reward to the question creator if necessary
if (questionData.refund) _refund(questionData);

ctf.reportPayouts(questionID, payouts);
emit QuestionEmergencyResolved(questionID, payouts);
}
Expand Down Expand Up @@ -270,6 +280,7 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
resolved: false,
paused: false,
reset: false,
refund: false,
rewardToken: rewardToken,
creator: creator,
ancillaryData: ancillaryData
Expand Down Expand Up @@ -332,11 +343,14 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques

/// @notice Reset the question by updating the requestTimestamp field and sending a new price request to the OO
/// @param questionID - The unique questionID
function _reset(address requestor, bytes32 questionID, QuestionData storage questionData) internal {
function _reset(address requestor, bytes32 questionID, bool resetRefund, QuestionData storage questionData)
internal
{
uint256 requestTimestamp = block.timestamp;
// Update the question parameters in storage
questionData.requestTimestamp = requestTimestamp;
questionData.reset = true;
if (resetRefund) questionData.refund = false;

// Send out a new price request with the new timestamp
_requestPrice(
Expand All @@ -362,14 +376,18 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
);

// If the OO returns the ignore price, reset the question
if (price == _ignorePrice()) return _reset(address(this), questionID, questionData);

// Construct the payout array for the question
uint256[] memory payouts = _constructPayouts(price);
if (price == _ignorePrice()) return _reset(address(this), questionID, true, questionData);

// Set resolved flag
questionData.resolved = true;

// If refund flag is set, this indicates that the question's reward now sits on the Adapter.
// Refund the reward to the question creator on resolution
if (questionData.refund) _refund(questionData);

// Construct the payout array for the question
uint256[] memory payouts = _constructPayouts(price);

// Resolve the underlying CTF market
ctf.reportPayouts(questionID, payouts);

Expand All @@ -382,6 +400,10 @@ contract UmaCtfAdapter is IUmaCtfAdapter, Auth, BulletinBoard, IOptimisticReques
);
}

function _refund(QuestionData storage questionData) internal {
return TransferHelper._transfer(questionData.rewardToken, questionData.creator, questionData.reward);
}

function _isFlagged(QuestionData storage questionData) internal view returns (bool) {
return questionData.emergencyResolutionTimestamp > 0;
}
Expand Down
2 changes: 2 additions & 0 deletions src/interfaces/IUmaCtfAdapter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ struct QuestionData {
bool paused;
/// @notice Flag marking whether a question has been reset. A question can only be reset once
bool reset;
/// @notice Flag marking whether a question's reward should be refunded.
bool refund;
/// @notice ERC20 token address used for payment of rewards, proposal bonds and fees
address rewardToken;
/// @notice The address of the question creator
Expand Down
8 changes: 8 additions & 0 deletions src/libraries/TransferHelper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ library TransferHelper {
function _transferFromERC20(address token, address from, address to, uint256 amount) internal {
SafeTransferLib.safeTransferFrom(ERC20(token), from, to, amount);
}

/// @notice Transfers tokens from the current address to the given destination
/// @param token - The contract address of the token to be transferred
/// @param to - The destination address of the transfer
/// @param amount - The amount to be transferred
function _transfer(address token, address to, uint256 amount) internal {
SafeTransferLib.safeTransfer(ERC20(token), to, amount);
}
}
Loading

0 comments on commit 2953a59

Please sign in to comment.