Contract Name:
ShortsTrackerTimelock
Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
contract Governable {
address public gov;
constructor() public {
gov = msg.sender;
}
modifier onlyGov() {
require(msg.sender == gov, "Governable: forbidden");
_;
}
function setGov(address _gov) external onlyGov {
gov = _gov;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
interface IShortsTracker {
function isGlobalShortDataReady() external view returns (bool);
function globalShortAveragePrices(address _token) external view returns (uint256);
function getNextGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
uint256 _nextPrice,
uint256 _sizeDelta,
bool _isIncrease
) external view returns (uint256, uint256);
function updateGlobalShortData(
address _account,
address _collateralToken,
address _indexToken,
bool _isLong,
uint256 _sizeDelta,
uint256 _markPrice,
bool _isIncrease
) external;
function setIsGlobalShortDataReady(bool value) external;
function setInitData(address[] calldata _tokens, uint256[] calldata _averagePrices) external;
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.12;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}
// SPDX-License-Identifier: MIT
import "../libraries/math/SafeMath.sol";
import "../access/Governable.sol";
import "../core/interfaces/IShortsTracker.sol";
import "./interfaces/IHandlerTarget.sol";
pragma solidity 0.6.12;
contract ShortsTrackerTimelock {
using SafeMath for uint256;
uint256 public constant BASIS_POINTS_DIVISOR = 10000;
uint256 public constant MAX_BUFFER = 5 days;
mapping (bytes32 => uint256) public pendingActions;
address public admin;
uint256 public buffer;
mapping (address => bool) public isHandler;
mapping (address => uint256) public lastUpdated;
uint256 public averagePriceUpdateDelay;
uint256 public maxAveragePriceChange;
event GlobalShortAveragePriceUpdated(address indexed token, uint256 oldAveragePrice, uint256 newAveragePrice);
event SignalSetGov(address target, address gov);
event SetGov(address target, address gov);
event SignalSetAdmin(address admin);
event SetAdmin(address admin);
event SetContractHandler(address indexed handler, bool isHandler);
event SignalSetHandler(address target, address handler, bool isActive, bytes32 action);
event SignalSetMaxAveragePriceChange(uint256 maxAveragePriceChange);
event SetMaxAveragePriceChange(uint256 maxAveragePriceChange);
event SignalSetAveragePriceUpdateDelay(uint256 averagePriceUpdateDelay);
event SetAveragePriceUpdateDelay(uint256 averagePriceUpdateDelay);
event SignalSetIsGlobalShortDataReady(address target, bool isGlobalShortDataReady);
event SetIsGlobalShortDataReady(address target, bool isGlobalShortDataReady);
event SignalPendingAction(bytes32 action);
event ClearAction(bytes32 action);
constructor(
address _admin,
uint256 _buffer,
uint256 _averagePriceUpdateDelay,
uint256 _maxAveragePriceChange
) public {
admin = _admin;
buffer = _buffer;
averagePriceUpdateDelay = _averagePriceUpdateDelay;
maxAveragePriceChange = _maxAveragePriceChange;
}
modifier onlyAdmin() {
require(msg.sender == admin, "ShortsTrackerTimelock: admin forbidden");
_;
}
modifier onlyHandler() {
require(isHandler[msg.sender] || msg.sender == admin, "ShortsTrackerTimelock: handler forbidden");
_;
}
function setBuffer(uint256 _buffer) external onlyAdmin {
require(_buffer <= MAX_BUFFER, "ShortsTrackerTimelock: invalid buffer");
require(_buffer > buffer, "ShortsTrackerTimelock: buffer cannot be decreased");
buffer = _buffer;
}
function signalSetAdmin(address _admin) external onlyAdmin {
require(_admin != address(0), "ShortsTrackerTimelock: invalid admin");
bytes32 action = keccak256(abi.encodePacked("setAdmin", _admin));
_setPendingAction(action);
emit SignalSetAdmin(_admin);
}
function setAdmin(address _admin) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setAdmin", _admin));
_validateAction(action);
_clearAction(action);
admin = _admin;
emit SetAdmin(_admin);
}
function setContractHandler(address _handler, bool _isActive) external onlyAdmin {
isHandler[_handler] = _isActive;
emit SetContractHandler(_handler, _isActive);
}
function signalSetGov(address _shortsTracker, address _gov) external onlyAdmin {
require(_gov != address(0), "ShortsTrackerTimelock: invalid gov");
bytes32 action = keccak256(abi.encodePacked("setGov", _shortsTracker, _gov));
_setPendingAction(action);
emit SignalSetGov(_shortsTracker, _gov);
}
function setGov(address _shortsTracker, address _gov) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setGov", _shortsTracker, _gov));
_validateAction(action);
_clearAction(action);
Governable(_shortsTracker).setGov(_gov);
emit SetGov(_shortsTracker, _gov);
}
function signalSetHandler(address _target, address _handler, bool _isActive) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive));
_setPendingAction(action);
emit SignalSetHandler(_target, _handler, _isActive, action);
}
function setHandler(address _target, address _handler, bool _isActive) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setHandler", _target, _handler, _isActive));
_validateAction(action);
_clearAction(action);
IHandlerTarget(_target).setHandler(_handler, _isActive);
}
function signalSetAveragePriceUpdateDelay(uint256 _averagePriceUpdateDelay) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setAveragePriceUpdateDelay", _averagePriceUpdateDelay));
_setPendingAction(action);
emit SignalSetAveragePriceUpdateDelay(_averagePriceUpdateDelay);
}
function setAveragePriceUpdateDelay(uint256 _averagePriceUpdateDelay) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setAveragePriceUpdateDelay", _averagePriceUpdateDelay));
_validateAction(action);
_clearAction(action);
averagePriceUpdateDelay = _averagePriceUpdateDelay;
emit SetAveragePriceUpdateDelay(_averagePriceUpdateDelay);
}
function signalSetMaxAveragePriceChange(uint256 _maxAveragePriceChange) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setMaxAveragePriceChange", _maxAveragePriceChange));
_setPendingAction(action);
emit SignalSetMaxAveragePriceChange(_maxAveragePriceChange);
}
function setMaxAveragePriceChange(uint256 _maxAveragePriceChange) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setMaxAveragePriceChange", _maxAveragePriceChange));
_validateAction(action);
_clearAction(action);
maxAveragePriceChange = _maxAveragePriceChange;
emit SetMaxAveragePriceChange(_maxAveragePriceChange);
}
function signalSetIsGlobalShortDataReady(IShortsTracker _shortsTracker, bool _value) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setIsGlobalShortDataReady", address(_shortsTracker), _value));
_setPendingAction(action);
emit SignalSetIsGlobalShortDataReady(address(_shortsTracker), _value);
}
function setIsGlobalShortDataReady(IShortsTracker _shortsTracker, bool _value) external onlyAdmin {
bytes32 action = keccak256(abi.encodePacked("setIsGlobalShortDataReady", address(_shortsTracker), _value));
_validateAction(action);
_clearAction(action);
_shortsTracker.setIsGlobalShortDataReady(_value);
emit SetIsGlobalShortDataReady(address(_shortsTracker), _value);
}
function disableIsGlobalShortDataReady(IShortsTracker _shortsTracker) external onlyAdmin {
_shortsTracker.setIsGlobalShortDataReady(false);
emit SetIsGlobalShortDataReady(address(_shortsTracker), false);
}
function setGlobalShortAveragePrices(IShortsTracker _shortsTracker, address[] calldata _tokens, uint256[] calldata _averagePrices) external onlyHandler {
_shortsTracker.setIsGlobalShortDataReady(false);
for (uint256 i = 0; i < _tokens.length; i++) {
address token = _tokens[i];
uint256 oldAveragePrice = _shortsTracker.globalShortAveragePrices(token);
uint256 newAveragePrice = _averagePrices[i];
uint256 diff = newAveragePrice > oldAveragePrice ? newAveragePrice.sub(oldAveragePrice) : oldAveragePrice.sub(newAveragePrice);
require(diff.mul(BASIS_POINTS_DIVISOR).div(oldAveragePrice) < maxAveragePriceChange, "ShortsTrackerTimelock: too big change");
require(block.timestamp >= lastUpdated[token].add(averagePriceUpdateDelay), "ShortsTrackerTimelock: too early");
lastUpdated[token] = block.timestamp;
emit GlobalShortAveragePriceUpdated(token, oldAveragePrice, newAveragePrice);
}
_shortsTracker.setInitData(_tokens, _averagePrices);
}
function cancelAction(bytes32 _action) external onlyAdmin {
_clearAction(_action);
}
function _setPendingAction(bytes32 _action) private {
require(pendingActions[_action] == 0, "ShortsTrackerTimelock: action already signalled");
pendingActions[_action] = block.timestamp.add(buffer);
emit SignalPendingAction(_action);
}
function _validateAction(bytes32 _action) private view {
require(pendingActions[_action] != 0, "ShortsTrackerTimelock: action not signalled");
require(pendingActions[_action] <= block.timestamp, "ShortsTrackerTimelock: action time not yet passed");
}
function _clearAction(bytes32 _action) private {
require(pendingActions[_action] != 0, "ShortsTrackerTimelock: invalid _action");
delete pendingActions[_action];
emit ClearAction(_action);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
interface IHandlerTarget {
function isHandler(address _account) external returns (bool);
function setHandler(address _handler, bool _isActive) external;
}