Files
protocol/contracts/staking/contracts/src/StakingProxy.sol
2019-10-10 15:00:35 +09:00

222 lines
7.5 KiB
Solidity

/*
Copyright 2019 ZeroEx Intl.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
pragma solidity ^0.5.9;
pragma experimental ABIEncoderV2;
import "./libs/LibProxy.sol";
import "./libs/LibSafeDowncast.sol";
import "./immutable/MixinStorage.sol";
import "./immutable/MixinConstants.sol";
import "./interfaces/IStorageInit.sol";
import "./interfaces/IStakingProxy.sol";
contract StakingProxy is
IStakingProxy,
MixinStorage,
MixinConstants
{
using LibProxy for address;
using LibSafeDowncast for uint256;
/// @dev Constructor.
/// @param _stakingContract Staking contract to delegate calls to.
/// @param _readOnlyProxy The address of the read only proxy.
constructor(
address _stakingContract,
address _readOnlyProxy
)
public
MixinStorage()
{
readOnlyProxy = _readOnlyProxy;
// Deployer address must be authorized in order to call `init`
_addAuthorizedAddress(msg.sender);
// Attach the staking contract and initialize state
_attachStakingContract(_stakingContract);
// Remove the sender as an authorized address
_removeAuthorizedAddressAtIndex(msg.sender, 0);
}
/// @dev Delegates calls to the staking contract, if it is set.
function ()
external
payable
{
stakingContract.proxyCall(
LibProxy.RevertRule.REVERT_ON_ERROR,
bytes4(0), // no custom egress selector
false // do not ignore ingress selector
);
}
/// @dev Attach a staking contract; future calls will be delegated to the staking contract.
/// Note that this is callable only by an authorized address.
/// @param _stakingContract Address of staking contract.
function attachStakingContract(address _stakingContract)
external
onlyAuthorized
{
_attachStakingContract(_stakingContract);
}
/// @dev Detach the current staking contract.
/// Note that this is callable only by an authorized address.
function detachStakingContract()
external
onlyAuthorized
{
stakingContract = NIL_ADDRESS;
emit StakingContractDetachedFromProxy();
}
/// @dev Set read-only mode (state cannot be changed).
function setReadOnlyMode(bool shouldSetReadOnlyMode)
external
onlyAuthorized
{
// solhint-disable-next-line not-rely-on-time
uint96 timestamp = block.timestamp.downcastToUint96();
if (shouldSetReadOnlyMode) {
stakingContract = readOnlyProxy;
readOnlyState = IStructs.ReadOnlyState({
isReadOnlyModeSet: true,
lastSetTimestamp: timestamp
});
} else {
stakingContract = readOnlyProxyCallee;
readOnlyState.isReadOnlyModeSet = false;
}
emit ReadOnlyModeSet(
shouldSetReadOnlyMode,
timestamp
);
}
/// @dev Batch executes a series of calls to the staking contract.
/// @param data An array of data that encodes a sequence of functions to
/// call in the staking contracts.
function batchExecute(bytes[] calldata data)
external
returns (bytes[] memory batchReturnData)
{
// Initialize commonly used variables.
bool success;
bytes memory returnData;
uint256 dataLength = data.length;
batchReturnData = new bytes[](dataLength);
address staking = stakingContract;
// Ensure that a staking contract has been attached to the proxy.
if (staking == address(0)) {
LibRichErrors.rrevert(
LibStakingRichErrors.ProxyDestinationCannotBeNilError()
);
}
// Execute all of the calls encoded in the provided calldata.
for (uint256 i = 0; i != dataLength; i++) {
// Call the staking contract with the provided calldata.
(success, returnData) = staking.delegatecall(data[i]);
// Revert on failure.
if (!success) {
assembly {
revert(add(0x20, returnData), mload(returnData))
}
}
// Add the returndata to the batch returndata.
batchReturnData[i] = returnData;
}
return batchReturnData;
}
/// @dev Asserts that an epoch is between 5 and 30 days long.
// Asserts that 0 < cobb douglas alpha value <= 1.
// Asserts that a stake weight is <= 100%.
// Asserts that pools allow >= 1 maker.
// Asserts that all addresses are initialized.
function _assertValidStorageParams()
internal
view
{
// Epoch length must be between 5 and 30 days long
uint256 _epochDurationInSeconds = epochDurationInSeconds;
if (_epochDurationInSeconds < 5 days || _epochDurationInSeconds > 30 days) {
LibRichErrors.rrevert(
LibStakingRichErrors.InvalidParamValueError(
LibStakingRichErrors.InvalidParamValueErrorCodes.InvalidEpochDuration
));
}
// Alpha must be 0 < x <= 1
uint32 _cobbDouglasAlphaDenominator = cobbDouglasAlphaDenominator;
if (cobbDouglasAlphaNumerator > _cobbDouglasAlphaDenominator || _cobbDouglasAlphaDenominator == 0) {
LibRichErrors.rrevert(
LibStakingRichErrors.InvalidParamValueError(
LibStakingRichErrors.InvalidParamValueErrorCodes.InvalidCobbDouglasAlpha
));
}
// Weight of delegated stake must be <= 100%
if (rewardDelegatedStakeWeight > PPM_DENOMINATOR) {
LibRichErrors.rrevert(
LibStakingRichErrors.InvalidParamValueError(
LibStakingRichErrors.InvalidParamValueErrorCodes.InvalidRewardDelegatedStakeWeight
));
}
// Minimum stake must be > 1
if (minimumPoolStake < 2) {
LibRichErrors.rrevert(
LibStakingRichErrors.InvalidParamValueError(
LibStakingRichErrors.InvalidParamValueErrorCodes.InvalidMinimumPoolStake
));
}
}
/// @dev Attach a staking contract; future calls will be delegated to the staking contract.
/// @param _stakingContract Address of staking contract.
function _attachStakingContract(address _stakingContract)
internal
{
// Attach the staking contract
stakingContract = readOnlyProxyCallee = _stakingContract;
emit StakingContractAttachedToProxy(_stakingContract);
// Call `init()` on the staking contract to initialize storage.
(bool didInitSucceed, bytes memory initReturnData) = stakingContract.delegatecall(
abi.encodeWithSelector(IStorageInit(0).init.selector)
);
if (!didInitSucceed) {
assembly {
revert(add(initReturnData, 0x20), mload(initReturnData))
}
}
// Assert initialized storage values are valid
_assertValidStorageParams();
}
}