Skip to content
Permalink
main
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/* solium-disable function-order */
pragma solidity 0.5.17;
import {
IBondedECDSAKeepFactory
} from "@keep-network/keep-ecdsa/contracts/api/IBondedECDSAKeepFactory.sol";
import {VendingMachine} from "./VendingMachine.sol";
import {DepositFactory} from "../proxy/DepositFactory.sol";
import {IRelay} from "@summa-tx/relay-sol/contracts/Relay.sol";
import "../external/IMedianizer.sol";
import {ITBTCSystem} from "../interfaces/ITBTCSystem.sol";
import {ISatWeiPriceFeed} from "../interfaces/ISatWeiPriceFeed.sol";
import {DepositLog} from "../DepositLog.sol";
import {TBTCDepositToken} from "./TBTCDepositToken.sol";
import "./TBTCToken.sol";
import "./FeeRebateToken.sol";
import "openzeppelin-solidity/contracts/ownership/Ownable.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "./KeepFactorySelection.sol";
/// @title TBTC System.
/// @notice This contract acts as a central point for access control,
/// value governance, and price feed.
/// @dev Governable values should only affect new deposit creation.
contract TBTCSystem is Ownable, ITBTCSystem, DepositLog {
using SafeMath for uint256;
using KeepFactorySelection for KeepFactorySelection.Storage;
event EthBtcPriceFeedAdditionStarted(
address _priceFeed,
uint256 _timestamp
);
event LotSizesUpdateStarted(uint64[] _lotSizes, uint256 _timestamp);
event SignerFeeDivisorUpdateStarted(
uint16 _signerFeeDivisor,
uint256 _timestamp
);
event CollateralizationThresholdsUpdateStarted(
uint16 _initialCollateralizedPercent,
uint16 _undercollateralizedThresholdPercent,
uint16 _severelyUndercollateralizedThresholdPercent,
uint256 _timestamp
);
event KeepFactoriesUpdateStarted(
address _keepStakedFactory,
address _fullyBackedFactory,
address _factorySelector,
uint256 _timestamp
);
event EthBtcPriceFeedAdded(address _priceFeed);
event LotSizesUpdated(uint64[] _lotSizes);
event AllowNewDepositsUpdated(bool _allowNewDeposits);
event SignerFeeDivisorUpdated(uint16 _signerFeeDivisor);
event CollateralizationThresholdsUpdated(
uint16 _initialCollateralizedPercent,
uint16 _undercollateralizedThresholdPercent,
uint16 _severelyUndercollateralizedThresholdPercent
);
event KeepFactoriesUpdated(
address _keepStakedFactory,
address _fullyBackedFactory,
address _factorySelector
);
uint256 initializedTimestamp = 0;
uint256 pausedTimestamp;
uint256 constant pausedDuration = 10 days;
ISatWeiPriceFeed public priceFeed;
IRelay public relay;
KeepFactorySelection.Storage keepFactorySelection;
uint16 public keepSize;
uint16 public keepThreshold;
// Parameters governed by the TBTCSystem owner
bool private allowNewDeposits = false;
uint16 private signerFeeDivisor = 2000; // 1/2000 == 5bps == 0.05% == 0.0005
uint16 private initialCollateralizedPercent = 150; // percent
uint16 private undercollateralizedThresholdPercent = 125; // percent
uint16 private severelyUndercollateralizedThresholdPercent = 110; // percent
uint64[] lotSizesSatoshis = [10**6, 10**7, 2 * 10**7, 5 * 10**7, 10**8]; // [0.01, 0.1, 0.2, 0.5, 1.0] BTC
uint256 constant governanceTimeDelay = 48 hours;
uint256 constant keepFactoriesUpgradeabilityPeriod = 180 days;
uint256 private signerFeeDivisorChangeInitiated;
uint256 private lotSizesChangeInitiated;
uint256 private collateralizationThresholdsChangeInitiated;
uint256 private keepFactoriesUpdateInitiated;
uint16 private newSignerFeeDivisor;
uint64[] newLotSizesSatoshis;
uint16 private newInitialCollateralizedPercent;
uint16 private newUndercollateralizedThresholdPercent;
uint16 private newSeverelyUndercollateralizedThresholdPercent;
address private newKeepStakedFactory;
address private newFullyBackedFactory;
address private newFactorySelector;
// price feed
uint256 constant priceFeedGovernanceTimeDelay = 90 days;
uint256 ethBtcPriceFeedAdditionInitiated;
IMedianizer nextEthBtcPriceFeed;
constructor(address _priceFeed, address _relay) public {
priceFeed = ISatWeiPriceFeed(_priceFeed);
relay = IRelay(_relay);
}
/// @notice Initialize contracts
/// @dev Only the Deposit factory should call this, and only once.
/// @param _defaultKeepFactory ECDSA keep factory backed by KEEP stake.
/// @param _depositFactory Deposit Factory. More info in `DepositFactory`.
/// @param _masterDepositAddress Master Deposit address. More info in `Deposit`.
/// @param _tbtcToken TBTCToken. More info in `TBTCToken`.
/// @param _tbtcDepositToken TBTCDepositToken (TDT). More info in `TBTCDepositToken`.
/// @param _feeRebateToken FeeRebateToken (FRT). More info in `FeeRebateToken`.
/// @param _keepThreshold Signing group honesty threshold.
/// @param _keepSize Signing group size.
function initialize(
IBondedECDSAKeepFactory _defaultKeepFactory,
DepositFactory _depositFactory,
address payable _masterDepositAddress,
TBTCToken _tbtcToken,
TBTCDepositToken _tbtcDepositToken,
FeeRebateToken _feeRebateToken,
VendingMachine _vendingMachine,
uint16 _keepThreshold,
uint16 _keepSize
) external onlyOwner {
require(initializedTimestamp == 0, "already initialized");
keepFactorySelection.initialize(_defaultKeepFactory);
keepThreshold = _keepThreshold;
keepSize = _keepSize;
initializedTimestamp = block.timestamp;
allowNewDeposits = true;
setTbtcDepositToken(_tbtcDepositToken);
_vendingMachine.setExternalAddresses(
_tbtcToken,
_tbtcDepositToken,
_feeRebateToken
);
_depositFactory.setExternalDependencies(
_masterDepositAddress,
this,
_tbtcToken,
_tbtcDepositToken,
_feeRebateToken,
address(_vendingMachine)
);
}
/// @notice Returns whether new deposits should be allowed.
/// @return True if new deposits should be allowed by the emergency pause button
function getAllowNewDeposits() external view returns (bool) {
return allowNewDeposits;
}
/// @notice Return the lowest lot size currently enabled for deposits.
/// @return The lowest lot size, in satoshis.
function getMinimumLotSize() public view returns (uint256) {
return lotSizesSatoshis[0];
}
/// @notice Return the largest lot size currently enabled for deposits.
/// @return The largest lot size, in satoshis.
function getMaximumLotSize() external view returns (uint256) {
return lotSizesSatoshis[lotSizesSatoshis.length - 1];
}
/// @notice One-time-use emergency function to disallow future deposit creation for 10 days.
function emergencyPauseNewDeposits() external onlyOwner {
require(
pausedTimestamp == 0,
"emergencyPauseNewDeposits can only be called once"
);
uint256 sinceInit = block.timestamp - initializedTimestamp;
require(
sinceInit < 180 days,
"emergencyPauseNewDeposits can only be called within 180 days of initialization"
);
pausedTimestamp = block.timestamp;
allowNewDeposits = false;
emit AllowNewDepositsUpdated(false);
}
/// @notice Anyone can reactivate deposit creations after the pause duration is over.
function resumeNewDeposits() external {
require(!allowNewDeposits, "New deposits are currently allowed");
require(pausedTimestamp != 0, "Deposit has not been paused");
require(
block.timestamp.sub(pausedTimestamp) >= pausedDuration,
"Deposits are still paused"
);
allowNewDeposits = true;
emit AllowNewDepositsUpdated(true);
}
function getRemainingPauseTerm() external view returns (uint256) {
require(!allowNewDeposits, "New deposits are currently allowed");
return
(block.timestamp.sub(pausedTimestamp) >= pausedDuration)
? 0
: pausedDuration.sub(block.timestamp.sub(pausedTimestamp));
}
/// @notice Set the system signer fee divisor.
/// @dev This can be finalized by calling `finalizeSignerFeeDivisorUpdate`
/// Anytime after `governanceTimeDelay` has elapsed.
/// @param _signerFeeDivisor The signer fee divisor.
function beginSignerFeeDivisorUpdate(uint16 _signerFeeDivisor)
external
onlyOwner
{
require(
_signerFeeDivisor > 9,
"Signer fee divisor must be greater than 9, for a signer fee that is <= 10%"
);
require(
_signerFeeDivisor < 5000,
"Signer fee divisor must be less than 5000, for a signer fee that is > 0.02%"
);
newSignerFeeDivisor = _signerFeeDivisor;
signerFeeDivisorChangeInitiated = block.timestamp;
emit SignerFeeDivisorUpdateStarted(_signerFeeDivisor, block.timestamp);
}
/// @notice Set the allowed deposit lot sizes.
/// @dev Lot size array should always contain 10**8 satoshis (1 BTC) and
/// cannot contain values less than 50000 satoshis (0.0005 BTC) or
/// greater than 10**10 satoshis (100 BTC). Lot size array must not
/// have duplicates and it must be sorted.
/// This can be finalized by calling `finalizeLotSizesUpdate`
/// anytime after `governanceTimeDelay` has elapsed.
/// @param _lotSizes Array of allowed lot sizes.
function beginLotSizesUpdate(uint64[] calldata _lotSizes)
external
onlyOwner
{
bool hasSingleBitcoin = false;
for (uint256 i = 0; i < _lotSizes.length; i++) {
if (_lotSizes[i] == 10**8) {
hasSingleBitcoin = true;
} else if (_lotSizes[i] < 50 * 10**3) {
// Failed the minimum requirement, break on out.
revert("Lot sizes less than 0.0005 BTC are not allowed");
} else if (_lotSizes[i] > 10 * 10**9) {
// Failed the maximum requirement, break on out.
revert("Lot sizes greater than 100 BTC are not allowed");
} else if (i > 0 && _lotSizes[i] == _lotSizes[i - 1]) {
revert("Lot size array must not have duplicates");
} else if (i > 0 && _lotSizes[i] < _lotSizes[i - 1]) {
revert("Lot size array must be sorted");
}
}
require(hasSingleBitcoin, "Lot size array must always contain 1 BTC");
emit LotSizesUpdateStarted(_lotSizes, block.timestamp);
newLotSizesSatoshis = _lotSizes;
lotSizesChangeInitiated = block.timestamp;
}
/// @notice Set the system collateralization levels
/// @dev This can be finalized by calling `finalizeCollateralizationThresholdsUpdate`
/// Anytime after `governanceTimeDelay` has elapsed.
/// @param _initialCollateralizedPercent default signing bond percent for new deposits
/// @param _undercollateralizedThresholdPercent first undercollateralization trigger
/// @param _severelyUndercollateralizedThresholdPercent second undercollateralization trigger
function beginCollateralizationThresholdsUpdate(
uint16 _initialCollateralizedPercent,
uint16 _undercollateralizedThresholdPercent,
uint16 _severelyUndercollateralizedThresholdPercent
) external onlyOwner {
require(
_initialCollateralizedPercent <= 300,
"Initial collateralized percent must be <= 300%"
);
require(
_initialCollateralizedPercent > 100,
"Initial collateralized percent must be >= 100%"
);
require(
_initialCollateralizedPercent >
_undercollateralizedThresholdPercent,
"Undercollateralized threshold must be < initial collateralized percent"
);
require(
_undercollateralizedThresholdPercent >
_severelyUndercollateralizedThresholdPercent,
"Severe undercollateralized threshold must be < undercollateralized threshold"
);
newInitialCollateralizedPercent = _initialCollateralizedPercent;
newUndercollateralizedThresholdPercent = _undercollateralizedThresholdPercent;
newSeverelyUndercollateralizedThresholdPercent = _severelyUndercollateralizedThresholdPercent;
collateralizationThresholdsChangeInitiated = block.timestamp;
emit CollateralizationThresholdsUpdateStarted(
_initialCollateralizedPercent,
_undercollateralizedThresholdPercent,
_severelyUndercollateralizedThresholdPercent,
block.timestamp
);
}
/// @notice Sets the addresses of the KEEP-staked ECDSA keep factory,
/// ETH-only-backed ECDSA keep factory and the selection strategy
/// that will choose between the two factories for new deposits.
/// When the ETH-only-backed factory and strategy are not set TBTCSystem
/// will use KEEP-staked factory. When both factories and strategy
/// are set, TBTCSystem load balances between two factories based on
/// the selection strategy.
/// @dev It can be finalized by calling `finalizeKeepFactoriesUpdate`
/// any time after `governanceTimeDelay` has elapsed. This can be
/// called more than once until finalized to reset the values and
/// timer. An update can only be initialized before
/// `keepFactoriesUpgradeabilityPeriod` elapses after system initialization;
/// after that, no further updates can be initialized, though any pending
/// update can be finalized. All calls must set all three properties to
/// their desired value; leaving a value as 0, even if it was previously
/// set, will update that value to be 0. ETH-bond-only factory or the
/// strategy are allowed to be set as zero addresses.
/// @param _keepStakedFactory Address of the KEEP staked based factory.
/// @param _fullyBackedFactory Address of the ETH-bond-only-based factory.
/// @param _factorySelector Address of the keep factory selection strategy.
function beginKeepFactoriesUpdate(
address _keepStakedFactory,
address _fullyBackedFactory,
address _factorySelector
) external onlyOwner {
uint256 sinceInit = block.timestamp - initializedTimestamp;
require(
sinceInit < keepFactoriesUpgradeabilityPeriod,
"beginKeepFactoriesUpdate can only be called within 180 days of initialization"
);
// It is required that KEEP staked factory address is configured as this is
// a default choice factory. Fully backed factory and factory selector
// are optional for the system to work, hence they don't have to be provided.
require(
_keepStakedFactory != address(0),
"KEEP staked factory must be a nonzero address"
);
newKeepStakedFactory = _keepStakedFactory;
newFullyBackedFactory = _fullyBackedFactory;
newFactorySelector = _factorySelector;
keepFactoriesUpdateInitiated = block.timestamp;
emit KeepFactoriesUpdateStarted(
_keepStakedFactory,
_fullyBackedFactory,
_factorySelector,
block.timestamp
);
}
/// @notice Add a new ETH/BTC price feed contract to the priecFeed.
/// @dev This can be finalized by calling `finalizeEthBtcPriceFeedAddition`
/// anytime after `priceFeedGovernanceTimeDelay` has elapsed.
function beginEthBtcPriceFeedAddition(IMedianizer _ethBtcPriceFeed)
external
onlyOwner
{
bool ethBtcActive;
(, ethBtcActive) = _ethBtcPriceFeed.peek();
require(ethBtcActive, "Cannot add inactive feed");
nextEthBtcPriceFeed = _ethBtcPriceFeed;
ethBtcPriceFeedAdditionInitiated = block.timestamp;
emit EthBtcPriceFeedAdditionStarted(
address(_ethBtcPriceFeed),
block.timestamp
);
}
modifier onlyAfterGovernanceDelay(
uint256 _changeInitializedTimestamp,
uint256 _delay
) {
require(_changeInitializedTimestamp > 0, "Change not initiated");
require(
block.timestamp.sub(_changeInitializedTimestamp) >= _delay,
"Governance delay has not elapsed"
);
_;
}
/// @notice Finish setting the system signer fee divisor.
/// @dev `beginSignerFeeDivisorUpdate` must be called first, once `governanceTimeDelay`
/// has passed, this function can be called to set the signer fee divisor to the
/// value set in `beginSignerFeeDivisorUpdate`
function finalizeSignerFeeDivisorUpdate()
external
onlyOwner
onlyAfterGovernanceDelay(
signerFeeDivisorChangeInitiated,
governanceTimeDelay
)
{
signerFeeDivisor = newSignerFeeDivisor;
emit SignerFeeDivisorUpdated(newSignerFeeDivisor);
newSignerFeeDivisor = 0;
signerFeeDivisorChangeInitiated = 0;
}
/// @notice Finish setting the accepted system lot sizes.
/// @dev `beginLotSizesUpdate` must be called first, once `governanceTimeDelay`
/// has passed, this function can be called to set the lot sizes to the
/// value set in `beginLotSizesUpdate`
function finalizeLotSizesUpdate()
external
onlyOwner
onlyAfterGovernanceDelay(lotSizesChangeInitiated, governanceTimeDelay)
{
lotSizesSatoshis = newLotSizesSatoshis;
emit LotSizesUpdated(newLotSizesSatoshis);
lotSizesChangeInitiated = 0;
newLotSizesSatoshis.length = 0;
refreshMinimumBondableValue();
}
/// @notice Finish setting the system collateralization levels
/// @dev `beginCollateralizationThresholdsUpdate` must be called first, once `governanceTimeDelay`
/// has passed, this function can be called to set the collateralization thresholds to the
/// value set in `beginCollateralizationThresholdsUpdate`
function finalizeCollateralizationThresholdsUpdate()
external
onlyOwner
onlyAfterGovernanceDelay(
collateralizationThresholdsChangeInitiated,
governanceTimeDelay
)
{
initialCollateralizedPercent = newInitialCollateralizedPercent;
undercollateralizedThresholdPercent = newUndercollateralizedThresholdPercent;
severelyUndercollateralizedThresholdPercent = newSeverelyUndercollateralizedThresholdPercent;
emit CollateralizationThresholdsUpdated(
newInitialCollateralizedPercent,
newUndercollateralizedThresholdPercent,
newSeverelyUndercollateralizedThresholdPercent
);
newInitialCollateralizedPercent = 0;
newUndercollateralizedThresholdPercent = 0;
newSeverelyUndercollateralizedThresholdPercent = 0;
collateralizationThresholdsChangeInitiated = 0;
}
/// @notice Finish setting addresses of the KEEP-staked ECDSA keep factory,
/// ETH-only-backed ECDSA keep factory, and the selection strategy
/// that will choose between the two factories for new deposits.
/// @dev `beginKeepFactoriesUpdate` must be called first; once
/// `governanceTimeDelay` has passed, this function can be called to
/// set factories addresses to the values set in `beginKeepFactoriesUpdate`.
function finalizeKeepFactoriesUpdate()
external
onlyOwner
onlyAfterGovernanceDelay(
keepFactoriesUpdateInitiated,
governanceTimeDelay
)
{
keepFactorySelection.setFactories(
newKeepStakedFactory,
newFullyBackedFactory,
newFactorySelector
);
emit KeepFactoriesUpdated(
newKeepStakedFactory,
newFullyBackedFactory,
newFactorySelector
);
keepFactoriesUpdateInitiated = 0;
newKeepStakedFactory = address(0);
newFullyBackedFactory = address(0);
newFactorySelector = address(0);
}
/// @notice Finish adding a new price feed contract to the priceFeed.
/// @dev `beginEthBtcPriceFeedAddition` must be called first; once
/// `ethBtcPriceFeedAdditionInitiated` has passed, this function can be
/// called to append a new price feed.
function finalizeEthBtcPriceFeedAddition()
external
onlyOwner
onlyAfterGovernanceDelay(
ethBtcPriceFeedAdditionInitiated,
priceFeedGovernanceTimeDelay
)
{
// This process interacts with external contracts, so
// Checks-Effects-Interactions it.
IMedianizer _nextEthBtcPriceFeed = nextEthBtcPriceFeed;
nextEthBtcPriceFeed = IMedianizer(0);
ethBtcPriceFeedAdditionInitiated = 0;
emit EthBtcPriceFeedAdded(address(_nextEthBtcPriceFeed));
priceFeed.addEthBtcFeed(_nextEthBtcPriceFeed);
}
/// @notice Gets the system signer fee divisor.
/// @return The signer fee divisor.
function getSignerFeeDivisor() external view returns (uint16) {
return signerFeeDivisor;
}
/// @notice Gets the allowed lot sizes
/// @return Uint64 array of allowed lot sizes
function getAllowedLotSizes() external view returns (uint64[] memory) {
return lotSizesSatoshis;
}
/// @notice Get the system undercollateralization level for new deposits
function getUndercollateralizedThresholdPercent()
external
view
returns (uint16)
{
return undercollateralizedThresholdPercent;
}
/// @notice Get the system severe undercollateralization level for new deposits
function getSeverelyUndercollateralizedThresholdPercent()
external
view
returns (uint16)
{
return severelyUndercollateralizedThresholdPercent;
}
/// @notice Get the system initial collateralized level for new deposits.
function getInitialCollateralizedPercent() external view returns (uint16) {
return initialCollateralizedPercent;
}
/// @notice Get the price of one satoshi in wei.
/// @dev Reverts if the price of one satoshi is 0 wei, or if the price of
/// one satoshi is 1 ether. Can only be called by a deposit with minted
/// TDT.
/// @return The price of one satoshi in wei.
function fetchBitcoinPrice() external view returns (uint256) {
require(
tbtcDepositToken.exists(uint256(msg.sender)),
"Caller must be a Deposit contract"
);
return _fetchBitcoinPrice();
}
// Difficulty Oracle
function fetchRelayCurrentDifficulty() external view returns (uint256) {
return relay.getCurrentEpochDifficulty();
}
function fetchRelayPreviousDifficulty() external view returns (uint256) {
return relay.getPrevEpochDifficulty();
}
/// @notice Get the time remaining until the signer fee divisor can be updated.
function getRemainingSignerFeeDivisorUpdateTime()
external
view
returns (uint256)
{
return
getRemainingChangeTime(
signerFeeDivisorChangeInitiated,
governanceTimeDelay
);
}
/// @notice Get the time remaining until the lot sizes can be updated.
function getRemainingLotSizesUpdateTime() external view returns (uint256) {
return
getRemainingChangeTime(
lotSizesChangeInitiated,
governanceTimeDelay
);
}
/// @notice Get the time remaining until the collateralization thresholds can be updated.
function getRemainingCollateralizationThresholdsUpdateTime()
external
view
returns (uint256)
{
return
getRemainingChangeTime(
collateralizationThresholdsChangeInitiated,
governanceTimeDelay
);
}
/// @notice Get the time remaining until the Keep ETH-only-backed ECDSA keep
/// factory and the selection strategy that will choose between it
/// and the KEEP-backed factory can be updated.
function getRemainingKeepFactoriesUpdateTime()
external
view
returns (uint256)
{
return
getRemainingChangeTime(
keepFactoriesUpdateInitiated,
governanceTimeDelay
);
}
/// @notice Get the time remaining until the signer fee divisor can be updated.
function getRemainingEthBtcPriceFeedAdditionTime()
external
view
returns (uint256)
{
return
getRemainingChangeTime(
ethBtcPriceFeedAdditionInitiated,
priceFeedGovernanceTimeDelay
);
}
/// @notice Get the time remaining until Keep factories can no longer be updated.
function getRemainingKeepFactoriesUpgradeabilityTime()
external
view
returns (uint256)
{
return
getRemainingChangeTime(
initializedTimestamp,
keepFactoriesUpgradeabilityPeriod
);
}
/// @notice Refreshes the minimum bondable value required from the operator
/// to join the sortition pool for tBTC. The minimum bondable value is
/// equal to the current minimum lot size collateralized 150% multiplied by
/// the current BTC price.
/// @dev It is recommended to call this function on tBTC initialization and
/// after minimum lot size update.
function refreshMinimumBondableValue() public {
keepFactorySelection.setMinimumBondableValue(
calculateBondRequirementWei(getMinimumLotSize()),
keepSize,
keepThreshold
);
}
/// @notice Returns the time delay used for governance actions except for
/// price feed additions.
function getGovernanceTimeDelay() external pure returns (uint256) {
return governanceTimeDelay;
}
/// @notice Returns the time period when keep factories upgrades are allowed.
function getKeepFactoriesUpgradeabilityPeriod()
public
pure
returns (uint256)
{
return keepFactoriesUpgradeabilityPeriod;
}
/// @notice Returns the time delay used for price feed addition governance
/// actions.
function getPriceFeedGovernanceTimeDelay() external pure returns (uint256) {
return priceFeedGovernanceTimeDelay;
}
/// @notice Gets a fee estimate for creating a new Deposit.
/// @return Uint256 estimate.
function getNewDepositFeeEstimate() external view returns (uint256) {
IBondedECDSAKeepFactory _keepFactory =
keepFactorySelection.selectFactory();
return _keepFactory.openKeepFeeEstimate();
}
/// @notice Request a new keep opening.
/// @param _requestedLotSizeSatoshis Lot size in satoshis.
/// @param _maxSecuredLifetime Duration of stake lock in seconds.
/// @return Address of a new keep.
function requestNewKeep(
uint64 _requestedLotSizeSatoshis,
uint256 _maxSecuredLifetime
) external payable returns (address) {
require(
tbtcDepositToken.exists(uint256(msg.sender)),
"Caller must be a Deposit contract"
);
require(
isAllowedLotSize(_requestedLotSizeSatoshis),
"provided lot size not supported"
);
IBondedECDSAKeepFactory _keepFactory =
keepFactorySelection.selectFactoryAndRefresh();
uint256 bond = calculateBondRequirementWei(_requestedLotSizeSatoshis);
return
_keepFactory.openKeep.value(msg.value)(
keepSize,
keepThreshold,
msg.sender,
bond,
_maxSecuredLifetime
);
}
/// @notice Check if a lot size is allowed.
/// @param _requestedLotSizeSatoshis Lot size to check.
/// @return True if lot size is allowed, false otherwise.
function isAllowedLotSize(uint64 _requestedLotSizeSatoshis)
public
view
returns (bool)
{
for (uint256 i = 0; i < lotSizesSatoshis.length; i++) {
if (lotSizesSatoshis[i] == _requestedLotSizeSatoshis) {
return true;
}
}
return false;
}
/// @notice Calculates bond requirement in wei for the given lot size in
/// satoshis based on the current ETHBTC price.
/// @param _requestedLotSizeSatoshis Lot size in satoshis.
/// @return Bond requirement in wei.
function calculateBondRequirementWei(uint256 _requestedLotSizeSatoshis)
internal
view
returns (uint256)
{
uint256 lotSizeInWei =
_fetchBitcoinPrice().mul(_requestedLotSizeSatoshis);
return lotSizeInWei.mul(initialCollateralizedPercent).div(100);
}
function _fetchBitcoinPrice() internal view returns (uint256) {
uint256 price = priceFeed.getPrice();
if (price == 0 || price > 10**18) {
// This is if a sat is worth 0 wei, or is worth >1 ether. Revert at
// once.
revert("System returned a bad price");
}
return price;
}
/// @notice Get the time remaining until the function parameter timer value can be updated.
function getRemainingChangeTime(
uint256 _changeTimestamp,
uint256 _delayAmount
) internal view returns (uint256) {
require(_changeTimestamp > 0, "Update not initiated");
uint256 elapsed = block.timestamp.sub(_changeTimestamp);
if (elapsed >= _delayAmount) {
return 0;
} else {
return _delayAmount.sub(elapsed);
}
}
}